Fun with ‘anonymous’ functions in PHP
Just some fun with anonymous functions in PHP (which are surprisingly rare due to silly syntax, and a buggy implementation)
<?php
class LambdaException extends Exception {}
class Lambda
{
private static $cache;
public static function Create($functionString)
{
if(!isset(self::$cache[$functionString]))
{
list($args, $body) = self::BreakupFunctionString($functionString);
self::$cache[$functionString] = new self($args, $body);
}
return self::$cache[$functionString];
}
private static function BreakupFunctionString($functionString)
{
$errorMessage = "Unable to understand the function: '$functionString'." .
"Format should follow function (\$arg1, \$arg2) { return rand(\$arg1, \$arg2); }";
$regex = "^function\s*\(([^\)]*)\)\s*\{(.*?)\}$";
if(preg_match("#$regex#si", trim($functionString), $matches) !== 1)
{
throw new LambdaException($errorMessage);
}
list(, $args, $body) = $matches;
$args = explode(',', $args);
$args = array_map('trim', $args);
$args = array_diff($args, array(''));
return array($args, $body);
}
//--------------------------------------------------------------------
private $args;
private $body;
private $function;
private function __construct($args, $body)
{
assert('is_array($args)');
assert('is_string($body)');
$this->args = $args;
$this->body = $body;
$this->function = create_function(implode(',', $this->args), $this->body);
}
public function map($array)
{
// We could actually test if the number of args is two
// and do a array_map with keys as well
assert('count($this->args) == 1');
return array_map($this->function, $array);
}
public function filter($array)
{
assert('count($this->args) == 1');
return array_filter($array, $this->function);
}
public function sort($array)
{
assert('count($this->args) == 2');
usort($array, $this->function);
return $array;
}
public function call()
{
$args = func_get_args();
return call_user_func_array($this->function, $args);
}
public function getFunction()
{
return $this->function;
}
public function __toString()
{
return $this->function;
}
}
$extractRowId = Lambda::Create('function ($row) { return $row["id"]; }');
print_r($extractRowId->map(array(array('id' => 'a'), array('id' => 'b'))));
$randomise = Lambda::Create('function ($v, $v) { return rand(0,1) == 0 ? -1 : 1; }');
print_r($randomise->sort(array(1, 2, 3, 4, 5, 6, 7, 8, 9)));
$greaterThanFive = Lambda::Create('function ($var) { return $var > 5; }');
print_r(array_filter(array(1,2,3,4,5,6,7,8,9), $greaterThanFive->getFunction()));
print_r(array_filter(array(1,2,3,4,5,6,7,8,9), (string)$greaterThanFive));
$printString = Lambda::Create('function($str) { echo $str . "\n"; }');
$printString->call("Hello World");
function formatString($s)
{
$args = func_get_args();
for($i = 1; $i < count($args); $i++)
{
$s = $args[$i]->call($s);
}
return $s;
}
$italic = Lambda::Create('function($s) { return "<i>" . $s . "</i>"; }');
$bold = Lambda::Create('function($s) { return "<bold>" . $s . "</bold>"; }');
$tag = Lambda::Create('function($t) { return Lambda::Create("function(\$s) { return \"<$t>\" . \$s . \"</$t>\"; }"); }');
$heading = $tag->call('h1');
$para = $tag->call('p');
echo formatString('Weird Formatting', $italic, $bold, $heading) . "\n";
$content = Lambda::Create('function($s) { return "Hello Lambda!" . $s; }');
$newLine = Lambda::Create('function($s) { return $s . "<br/>"; }');
echo formatString('', $content, $bold, $italic, $newLine, $para) . "\n";
?>
Output
Array
(
[0] => a
[1] => b
)
Array
(
[0] => 7
[1] => 1
[2] => 5
[3] => 4
[4] => 3
[5] => 6
[6] => 2
[7] => 8
[8] => 9
)
Array
(
[5] => 6
[6] => 7
[7] => 8
[8] => 9
)
Array
(
[5] => 6
[6] => 7
[7] => 8
[8] => 9
)
Hello World
<h1><bold><i>Weird Formatting</i></bold></h1>
<p><i><bold>Hello Lambda!</bold></i><br/></p>
Note:Please don’t actually use this code for anything important :-)