Ignored By Dinosaurs 🦕

php

Easy Markdown with Syntax Highlighting, PHP Edition

Hi there, and welcome back to this 14th installment of “I rewrote my blog in another framework that I'm interested in learning, this time in Laravel”. The trick that we'll be exploring today is that, in contrast to Python (the last version was in Django), PHP's lib story is a bit more sparse for this exact use case. However, I'm completely pleased with the outcome, so let's get busy!

PHP Markdown

Google that term and you'll find this – https://michelf.ca/projects/php-markdown/. This appears to be basically the most robust and well maintained Mardown parser for PHP, so that's where I started. It's quite simple to add to a Laravel project – composer require michelf/php-markdown and then (for the purposes of syntax highlighting) you'll want to use the MarkdownExtra class. Here's the Laravel code for rendering this article body that you're reading right now -

public function rendered_body() {
	$parser = new MarkdownExtra();
	$parser->code_class_prefix = "language-";
	return $parser->transform($this->body);
}

Pretty darn simple. The only option code_class_prefix will be explained shortly.

Syntax Highlighting

In contrast to my previous post on the matter, the PHP landscape does *not* have the most robust syntax highlighting parser in the universe at its disposal. After casting about in vain for a pure PHP solution I had one of those “I wonder if there's a javascript lib for this” moments. Turns out there are a couple..

Prismjs is the lib I chose, primarily because of the well known pubs namedropped on the front page and the fact that it has my beloved Twilight theme right out of the box.

Installation was straightforward and took about 3 minutes following the instructions on the website.

The only trick is that by default, the Markdown parser wraps its code blocks with a class of lang-$language, and we need it to be language-$language for prism to correctly work its magic. Luckily this is exactly what that configuration item is for above.

Conclusion

So that's it! Literally 10 minutes worth of work between “I wonder if there's a JS highlighter that I can use instead of trying to do this in pure PHP” and having this up and running.

#laravel #php

Was asked this question recently, and haven't done any low level string manipulation w PHP in a little while. Couldn't remember the signature of substr(), but that wasn't my method anyway. Mine was more like iterating over an index, working from the back forward and concat-ing that on to a new string. Also, this is like 5 minutes worth of code, so cut me some slack.


echo "\nString reverse test\n";

function bench_it($func) {
  if (function_exists($func)) {
    $str = file_get_contents(__DIR__ . '/mobydick.txt');
    $results = [];
    $iter = 10;
    for ($i = 0; $i < $iter; $i++) {
      $then = microtime(true);
      $func($str);
      $now = microtime(true);
      $results[] = $now - $then;
    }
    $timeout = array_sum($results) / count($results);

    echo "$func avg: $timeout\n";
  }
}

function substr_test($str) {
  $len = strlen($str);
  $new_str = "";
  while ($len  0) {
    $new_str += substr($str, $len, 1);
    $len--;
  }
}

function str_concat($str) {
  $len = strlen($str);
  $new_str = "";
  while ($len > 0) {
    $new_str += $str[$len - 1];
    $len--;
  }
}

function str_push_and_join($str) {
  $len = strlen($str);
  $arr = [];
  while ($len > 0) {
    $arr[] = $str[$len - 1];
    $len--;
  }
  implode('', $arr);
}

function strrev_test($str) {
  strrev($str);
}
bench_it('substr_test');
bench_it('str_concat');
bench_it('str_push_and_join');
bench_it('strrev_test');
grubb:php/ $ php test.php [18:18:13]

String reverse test

substr_test avg: 1.2850220918655
str_concat avg: 0.24263381958008
str_push_and_join avg: 0.63150105476379
strrev_test avg: 0.00075311660766602

So the method that I was working on (#2 str_concat) is the fastest besides the built in strrev(), but most interesting is when you run these same tests on PHP 7.0.4 —

grubb:php/ $ brew unlink php56 && brew link php70 [18:18:56]
  Unlinking /usr/local/Cellar/php56/5.6.19... 19 symlinks removed
  Linking /usr/local/Cellar/php70/7.0.4... 17 symlinks created
grubb:php/ $ php test.php [18:19:20]

String reverse test

substr_test avg: 0.12836751937866
str_concat avg: 0.084317827224731
str_push_and_join avg: 0.13263397216797
strrev_test avg: 0.00083370208740234

#php

When I first started getting into this, I read a lot on PHP and remember clearly having my eyes go crossed when I came across code like this —

<?php
// Example code: Creating Drupal 7 nodes by POSTing from cURL in PHP:

$site = "127.0.0.1/d7";
$user = "someusername";
$pass = "theusersassword";
$crl = curl_init();
curl_setopt($crl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($crl, CURLOPT_USERAGENT, 'PHP script');
curl_setopt($crl, CURLOPT_COOKIEJAR, "/tmp/cookie.txt");
curl_setopt($crl, CURLOPT_COOKIEFILE, '/tmp/cookie.txt');

/// etc, etc, etc
// taken from http://blog.ampli.fi/creating-drupal-7-nodes-with-php-via-the-restws-api/

So, in a nutshell, Curl is a very popular Linux/Unix command line program for doing things across the internet – downloading things, uploading things, pinging remote servers with requests, etc. Curl has a wealth of options that you can set with command line flags (-I to only get response headers, -D to post form data, -X to specify a HTTP method, etc).

PHP has a built in function for working with Curl, thereby making it easy to programmatically make HTTP calls to other servers. Of course, to really be able to use Curl in a way that's analogous to it's usage on the command line, you need a way to set those flags. curl_init() sets all that up for you, and all you really need to do after that is set whatever flags you need in calls to curl_setopt().

See the docs here – https://php.net/manual/en/function.curl-init.php.

#php #generaldevelopment

I'm reading this morning and I come across a post about a thing that somebody wrote called (or subtitled, I'm not sure) Node.php. Anyone who doesn't understand what it is by looking at the name wouldn't understand my explanation for what it does, so whatever.

For the non-programmers —

It's written in a language called PHP. PHP, despite being the backbone for the server side component of a huge number of the largest websites in existence (as well as this tiny little one), is almost universally derided in hacker circles. It's sort of like the Alvarez guitar of programming languages. No “real professional” guitar player would be caught dead playing anything but a Les Paul or a Strat, so the Alvarez is widely acknowledged to be a “starter” guitar, much like PHP is “starter” language to some people.

The problem is that this is all a bunch of pseudo-religious hokey, and (many) hackers are more vain, egotistical, and prone to engaging in flame wars over their chosen tools than even musicians. Any actual real professional musician knows good and damn well that he can get a beautiful sound out of an Alvarez and that only rookies have the time to make fun of each other for their choice because the pros are too busy creating.

I typically play a plywood Chinese bass that's been cut up into little pieces and I get a damn good sound out of it. Is the sound better than my carved bass? Is it easier to play? No. Does that stop me from rocking the joint with it? No.

So why in the face of so much evidence that PHP is a perfectly useful language to a vast number of programmers of all skill levels around the world does it garner such derision? Someone please explain it to me as if you were trying to convince a non-programmer that they should care because the whole thing seems a little immature.

#technology #php

So I've decided to start playing around with CodeIgniter. It's a supremely simple PHP framework that has a lot of good documentation, a pretty big user/developer base, and has a lot in common stylistically with Rails. I've just really started playing around with it, and wanted to use the migrations feature for building up my database. I won't explain what migrations are or how they work or why to use them because that's been covered.

Our project will be something like a database of shows. So we need a “shows” table in our database. CI needs you to edit application/config/migrations.php to change $config['migration_enabled'] = FALSE; to $config['migration_enabled'] = TRUE;. This will enable migrations in the app. The next meaningful line down the page – $config['migration_version'] = X; will tell your app which migration it's supposed to be on. If you're working with someone else and they update their codebase from a repo, then their app will check their local development database, see that X number of migrations have not been applied (by checking the version number in the migrations table in the DB) and bring the schema up to date. It's neat.

Step 2 will be to write our first migration. Under application/ you'll need a /migrations directory (application/migrations). This is where you'll write out the migration file. About here is where you start realizing some of the amazing things that Rails does for you, such as building all of this with a line on the terminal instead of making you trot all over you app to set this up. I digress.

Our first migration will look like this -

// application/migrations/001_add_shows.php
 
php defined("BASEPATH") or exit("No direct script access allowed");

 class Migration_Add_shows extends CI_Migration {
 
 public function up() {
 $this-dbforge->add_field('id');
 $this->dbforge->add_field(array(
 'date' => array(
 'type' => 'DATE',
 'null' => FALSE,
 ),
 'location' => array(
 'type' => 'VARCHAR',
 'constraint' => '255',
 'null' => FALSE,
 ),
 'description' => array(
 'type' => 'TEXT',
 'null' => TRUE,
 ),
 ));
 $this->dbforge->create_table('shows');
 }

 public function down() {
 $this->dbforge->drop_table('shows');
 }

 }
?>

So here's what this says -

We're creating a migration called “Add shows”. This will create our “shows” table in the DB. It will have an 'id' column. By using $this->dbforge->add_field('id');, CI knows to make this your primary key, to make it an auto-incrementing integer, and for it not to be NULL (http://codeigniter.com/userguide/database/forge.html#addfield). We're also adding a date field, a location field, and a text description.

The name of this file is important. If you call the class Migration_Add_shows and name the file 001addshow.php (singular), CI won't be able to find it. This is it for step 2.

Note: the down() method is for reversing this change. I haven't figured out how that works just yet from the standpoint of running the down() migration, but you'll want to write the reverse of any up() method for every migration. This is the “undo” button on this process. Ignore it at your peril.

Step 3 is to create a migrations controller, since you'll need this migration to be called from somewhere. I will learn how to run migrations from the command line next, since the CLI is obviously where it's at, but for now we'll write a controller and do it the hard way.

// application/controllers/migrate.php
 
php defined("BASEPATH") or exit("No direct script access allowed");

 class Migrate extends CI_Controller {
 public function index() {
 if (ENVIRONMENT == 'development') {
 $this-load->library('migration');
 if ( ! $this->migration->current()) {
 show_error($this->migration->error_string());
 } else {
 echo "success";
 }
 } else {
 echo "go away";
 }
 }
 }
?>

Now, if you visit yourapp.com/index.php/migrate, you'll have run the migration. It took me a few minutes to get this wired up correctly, and CI gives you some useful error messages, so hopefully you'll be on your way. Basically this says -

This can only be run while in development mode, else “go away” and stop monkeying with my app. Load the migration library, which runs the migration up to the version specified in config/migrations.php. When you add a new migration, update the version number in that file, and it'll be run next time you visit this url.

Did you get all that?

#php