php

If, like me, you hose Apache on your Ubuntu LTS server when you finally get around to upgrading libssl, and PHP stops working...

You'll be needing this:

apt-get --reinstall install apache2 php5-mysql \
libapache2-mod-php5 mysql-server

(You're welcome)

Preparing a string for use in a PHP regular expression.

This blog post is just for me, so I can look it up later... I suppose you can borrow it if you find it useful :)

Using dynamic strings inside a regex can be annoying, especially if you can't guarantee that the strings are actually sane. Including a URL in a regular expression is downright obnoxious, since URLs and regular expressions share most of the same special characters, but they mean entirely different things. I threw this function together to make strings regexable. Enjoy.

Function

/**
 * Prepare a string for use in a regular expression.
 * 
 * @author Justin Hileman {@link http://justinhileman.com}
 * @access public
 * @param string $str
 * @return string
 */
function preg_real_escape_string($str) {
    $replace = array('\\' => '\\\\', '^' => '\^', '.' => '\.',
        '$' => '\$', '|' => '\|', '(' => '\(', ')' => '\)',
        '[' => '\[', ']' => '\]', '*' => '\*', '+' => '\+',
        '?' => '\?', '{' => '\{', '}' => '\}', ',' => '\,');
    return strtr($str, $replace);
}

Yeah, the name is a bit weird. This function is an analog to mysql_real_escape_string(), so it seemed fitting.

Usage

// basic usage (this is actually exactly the case I wrote this function for).
$regex = '/^' . preg_real_escape_string($name) . '/i';
$nameless = preg_replace($regex, '', $something_starting_with_name);
 
// trying to find a URL via regex can be especially obnoxious.
$regex = '/' . preg_real_escape_string($my_url) . '/';
$some_text = preg_replace($regex, '<a href="$1">$1</a>', $some_text);

Spyc vs. syck: Speedier YAML parsing in PHP

This post is mostly pretty pictures. I just ran some really quick benchmarks on a couple of of the YAML parsing options available for PHP. The blue line is the Spyc YAML library. The green line is the syck PECL extension. The yellow line is a standard PHP include(), and is on the chart as a reference point.

The first two charts were tested with a fixed file size and a variable number of iterations. The final chart tracks the change in parsing time as file size increases. Each test was run with 100 iterations, and the file size was doubled at each stage.

The pretty pictures

Average parse time

Total parse time

Total parse time for larger files

More info than you cared to know

  • This is a very informal benchmark. Feel free to supplement it with testing of your own.
  • All benchmarks were run on the same Ubuntu 9.04 virtual machine running PHP 5.2.x.
  • Tests were run in a Zoop Framework skeleton app (from the upcoming Lunar release), since I had one handy and it's really easy to deploy.
  • The YAML benchmarks on the first two charts consist of loading and parsing a ~160 line file (a YAML dump of the default Zoop skeleton configuration).
  • The PHP include benchmark exists only as a reference point. It consists of loading a var_dump()'d version of the same configuration array used in the YAML tests. This file was included via a standard PHP include().
  • The first two sets of tests were run with 1, 50, 100, 250, and 500 iterations, respectively. The final test was run with 100 iterations and variable file size.

A PHP Framework Showdown

I've enjoyed working on the Zoop Framework's Lunar branch, and it's gotten pretty rad in the last several months. I wanted to compare it with some of the other offerings.

For some background, watch Simple is Hard, Rasmus Lerdorf's really long talk at Drupalcon Szeged last year. Make sure to check out the presentation slides for all the gory details. If you just want to see some numbers and pretty charts, see below.

I wrote a quick Hello World in the latest versions of CakePHP, CodeIgniter and Symfony, and compared them with the a skeleton app from Zoop's Lunar branch. I kept each of the apps as vanilla and basic as I could, so they look a lot like the ones Rasmus wrote.

For comparison, I also included a trivial PHP and HTML page with the exact same output

<html>
<head>
<title>Test</title>
</head>
<body>
<h1>Hello world!</h1>
</body>
</html>

The output looks like just like that for each of the frameworks.

Each framework was responsible for duplicating the following PHP snippet

<html>
<head>
<title><?php echo 'Title'; ?></title>
</head>
<body>
<h1><?php echo 'Hello world!'; ?></h1>
</body>
</html>

Here are the good parts of the Zoop app

class zone_default extends Zone {
    function pageIndex() {
        global $gui;
        $gui->assign('title', 'Test');
        $gui->assign('message', 'Hello world!');
        $gui->display('base/hello_world.tpl');
    }
}
<html>
<head>
<title>{$title}</title>
</head>
<body>
<h1>{$message}</h1>
</body>
</html>

This kept the differences to a minimum. All of my tests were done on a virtual machine with PHP 5.2.4, and tested with 15 concurrent connections, using Siege in benchmark mode. These benchmarks essentially measure the minimum overhead of each framework while completing a trivial task. Note that it doesn't address feature sets, functionality, ease of use, available libraries and support, or any of the other reasons you might want to use a framework... That's another blog post for another day.

Here's the big picture

PHP Framework showdown

The HTML and trivial PHP pages are obviously fast. And APC (the blue sections) is worth every cent. It's a pretty chart, too. Raw PHP is twice as fast as CodeIgniter, which is twice as fast as Lunar. Lunar is twice as fast as Symfony, and CakePHP just crawls, even with APC enabled. Scroll to the bottom of this post to see all the numbers, if you're into that sort of thing.

Tweaking Zoop for even better performance after the jump.

'Unsupported operand types' in Drupal 6.x

I recently (finally) updated my blog to Drupal 6, but I was having all sorts of trouble running cron. Instead of doing all the chrony magical stuff, it kept saying things like this:

Fatal error: Unsupported operand types in [...]/drupal/includes/common.inc on line 1376
Fatal error: Unsupported operand types in [...]/drupal/includes/common.inc on line 1546

If you run into either of these errors, you've found the one of the joys of a rapidly changing api! As of Drupal 6.x, Drupal has changed the arguments for the l() and url() functions. These functions used to have a bunch of arguments for things like 'absolute', 'query', 'fragment', and so forth. The problem was when you had a fragment but no query string, or you just wanted to declare a url as absolute. You had to add a whole bunch of null arguments to get to the one you actually wanted to set. So Drupal made a wise move and combined all the options into a single argument, fittingly named  $options .

How to fix your unsupported operand type error after the jump.

Smarty {html_options} plugin with 'disabled' support

I needed the ability to disable certain HTML <select> options in the Smarty {html_options} plugin so I whipped up this version which allows just that.

This is a drop-in replacement for the existing {html_options} plugin. Disabled options are defined by passing in an array of keys, or a single key, just like 'selected' options are defined.

The PHP end

$options = array(
    '1' => 'foo',
    '2' => 'bar',
    '3' => 'baz',
)
$selected = 1;
$disabled = array(2, 3);
 
$smarty->assign('options', $options);
$smarty->assign('selected', $selected);
$smarty->assign('disabled', $disabled);

In the Smarty template

<select>
    {html_options options=$options selected=$selected disabled=$disabled}
</select>

Upgrade today!

Download function.html_options.php here

You will need to change the extension back to .php or this won't work for ya :)

And if you're Monte, here's the (almost trivial) diff. I'd love to see it in the next Smarty version :)

wrand(); // a php weighted randomization function

dan from todaywasawesome just asked me to help him with a weighted randomization function in php. i thought my solution was cool, if a bit simplistic. like everything else i want to keep around, i'm posting it in my blag. enjoy!

/* this function expects either an array
 * ordered by weight, or with exact weights
 * as it's array indices.
 * it will return a randomly selected value.
 */
function wrand($data) {
    $totalw = $curw = 0;
 
    foreach ($data as $i => $val) {
        $totalw += $i;
    }
 
    $rand = rand(0,$totalw);
 
    foreach ($data as $i => $val) {
        if ($curw >= $rand) return $val;
        $curw += $i;
    }
 
    return array_pop($data);
}

Snippet of the day: Trim a string (the smart way) with PHP

I reworked the feed reading signature images I made a while back and added something to trim the strings to a decent length without breaking words. So here you go. A one-liner that will trim a string to a decent length, and break it on whitespace:

if (strlen($blog_title) > 35) $blog_title =
array_shift(explode("|||", wordwrap($blog_title,
35, "|||"))) . "...";