web development

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.

Pro Tip: Use an explicit LIMIT in Doctrine

Doctrine is usually pretty rad. And sometimes it's really really dumb.

For example, in an imaginary CMS the following DQL query will grab a random blog post:

$random_post = Doctrine_Query::create()
    ->select('*')
    ->from('BlogPosts')
    ->where('published')
    ->orderBy('RANDOM()')
    ->fetchOne();

And it will work great when you first start writing blog posts. But as the total number of posts increases, the performance will get worse and worse. That's because, apparently, when you ask Doctrine to "fetch one" that doesn't quite convert to LIMIT 1 on the back end. It will actually select--and possibly instantiate an object for--everything that matches your query. After it's done with all this work, Doctrine will hand you the first result.

If you want to save yourself headaches later, use something like this:

$random_post = Doctrine_Query::create()
    ->select('*')
    ->from('BlogPosts')
    ->where('published')
    ->orderBy('RANDOM()')
    ->limit(1)
    ->fetchOne();

Any time you call fetchOne(), be sure to explicitly use limit(1).

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 :)

PHP Docblock generator plug-in for Coda

Panic's Coda is my current favorite editor. It's not perfect yet, but they just keep making it better. Coda 1.6 and later support a great plug-in architecture that allows users to tweak the editor's functionality to match their own needs and coding style.

Plug-ins have access to the text of the current document and can perform modifications on it as desired. For example, you could write a plug-in to insert the current date and time into your document, change the case of selected text, run code through a custom validator, or even wrap code with a special tag.

These plug-ins are pretty rad, let me tell you.

One of the things I missed most when I started using Coda was the ability to magically generate docblocks. You see, I'm absolutely horrid at writing documentation. With semi-automatic docblocks, writing documentation has a ton less friction so I find myself doing it more.

The new plug-in architecture lends itself well to things like that, so I decided to write a docblock generator. You can download my PHP docblock plug-in here:

PHP Docblock generator plug-in for Coda

Give it a spin, let me know where you'd like to see improvements. And be sure to check out the rest of the awesome plug-ins available over at Panic.

Some sample docblocks after the jump.

a drupal path redirect bookmarklet

i threw together a fancy little javascript bookmarklet to create redirects in drupal, and figured i'd share.

for this to work you need the path redirect and prepopulate modules installed... then simply replace "example.com" in the snippet below with your domain name, and save the link as a browser bookmark.

<a href="javascript:d='example.com/';location.href='http://'+d+'admin/build/path_redirect/new?edit[redirect][redirect]='+encodeURIComponent(location.href.split(/[\?#]/)[0].replace(RegExp('https?://'+d,'ig'),''))+'&amp;edit[redirect][query]='+encodeURIComponent(location.href.indexOf('?')&amp;gt;-1?location.href.split(/[\?#]/)[1]:'')+'&amp;edit[redirect][fragment]='+encodeURIComponent(location.href.indexOf('#')&amp;gt;-1?location.href.split(/#/)[1]:'')+'&amp;edit[path]='+encodeURIComponent(String(''+(window.getSelection?window.getSelection():document.getSelection?document.getSelection():document.selection.createRange().text)).replace(/[^a-zA-Z0-9]+/g,'-').toLowerCase());">new path redirect</a>

now click on it, and it will add a redirect to the current page. if you select any text before clicking the bookmarklet, it will use that text for the redirect path. this bookmarklet supports both internal (drupal) and external paths. it also works with urls containing queries (?foo=bar) and fragments (#baz). tested in ie, firefox and safari.

have fun!

justin hileman dot info is featuriffic!

justin hileman dot info has been updated. please allow me to draw your attention to the links at the bottom of the main page. i'm pretty excited about a couple. specifically the two with little arrows next to them.

you should click on them.

my space is cooler than your space

if you've visited my myspace profile recently, you've noticed that it doesn't look much like a standard myspace profile. which i'm pretty stoked about, since the hideousness of most myspace profiles approaches obscenity.

one key element in the makeover of my space is the blog. you see, i have way too many blogs already, so i really don't want to post to all of them. and i usually just end up crossposting everything from my regular blog to the myspace blog.

so i replaced it.

drupal dynamically generated MySpace blog replacement

the first generation myspace blog replacement was pretty sweet. i wrote a flash based RSS reader that would check my personal blog for new posts, and display them on the 'space. but the powers that be decided that flash files shouldn't be able to link to offsite pages. so the "read more" links didn't actually work.

so we try it again. enter myspace blog replacement, generation two. this one's really rad.

Give it another go after the jump.

a more secure drupal [multisite] install

I love the Drupal CMS. One of my favorite features of Drupal is the ability to do a multisite install. This site and my other blog, i <3 stella, are hosted on the same box, using the same Drupal install. Several sites can share one codebase. Updates are easily rolled out to every site simultaneously. Overall, it's a wonderful idea. But I have some problems with the implementation...

drupal secure multisite tutorial after the jump.