Phillip Pearson - web + electronics notes

tech notes and web hackery from a new zealander who was vaguely useful on the web back in 2002 (see: python community server, the blogging ecosystem, the new zealand coffee review, the internet topic exchange).

2006-9-8

Hey, PHP *does* have anonymous functions!

I haven't been doing much Real Data Processing work in PHP recently, and today I'm getting back into it, writing some comment spam administration screens for PeopleAggregator. I was under the impression that you couldn't create anonymous functions in PHP, and was really missing being able to do stuff like Python list comprehensions.

My original thought was to use array_map with a named function, but by the time you've declared a function (wrapped appropriately so PHP doesn't complain when you call the parent function again) the amount of code you have to write to do it that way is probably equivalent to just using a foreach loop...

It turns out, though, that PHP has a create_function function, which will make a function from a couple of strings you give it. This might work fairly well; it seems that there are plenty of limitations, but it might work well for simple things like what I want here. I'm not confident that PHP will clean up the function when you're finished with it, so it might be safer not to use this inside loops, but it's still useful nonetheless.

- - -

Here are some examples of equivalent code in various languages:

Python:

ids = [item['comment_id'] for item in details]

PHP with foreach:

$ids = array();
foreach ($details as $item) {
   $ids[] = $item['comment_id'];
}

PHP with local function and array_map:

if (!function_exists("__temp_ajksdf")) function __temp_ajksdf($item) {
   return $item['comment_id'];
}
$ids = array_map("__temp_ajksdf", $details);

PHP with anonymous function and array_map:

$ids = array_map(create_function('$item', 'return $item["comment_id"];'), $details);

I haven't done any Perl for a while, but off the top of my head I think the equivalent there would be:

$ids = map { $_->{comment_id} }, $details;

And I guess Ruby would do something like this:

ids = details.each { |item| item['comment_id'] }

And Javascript + Prototype gets you:

var ids = details.map(function(item) { return item.comment_id; })

- - -

Of course, if you do something like this (which I haven't tested - one assumption in particular that may be wrong is that you can use arrays of strings as array keys), you should be able to use create_function inside loops, as long as you don't change the function text each time:

$function_cache = array();

function mkfunc($args, $code) {
   global $function_cache;

   $k = array($args, $code);
   $f =& $function_cache[$k];

   if (isset($f)) return $f;
   $f = create_function($args, $code);
   return $f;
}