In PHP, you can have named functions like this:
function foo()
{
return "bar";
}
And you can have Closures like this:
$foo = function() {
return "bar";
};
Closures are awesome and easy to create, but unfortunately a library I need to use really wants a named function. Is it possible to create a named function from closures dynamically? I.e. not defining all functions in code ahead of time, but more like a register_function($name, callable $closure)
.
I am aware of create_function
, but that one takes a PHP string as function body and just eval
s it, which is not what I'm looking for.
I don't think if it is possible to create named functions out of closures on the fly but using __call()
magic method of a class, you may find this workaround helpful:
<?php
class Foo
{
public function __call($name, $args)
{
if (isset($this->{$name}))
return call_user_func_array($this->{$name}, $args);
}
}
$foo = new Foo();
// Declaring a closure then assign it to $bar property of Foo
$foo->bar = function () {
echo "bar";
};
// Call Foo method of the same name
$foo->bar();
You can create global array with callbacks. Add to this global array by register_func($name, $callback)
and call function by call_func($name, $parameter1, $parameter2, ...)
.
Without using eval
I think this is not possible to create named function from callback.
My proposal is to use a static methods because they can be call like simple functions. We can achive this with magic function __callStatic
like in this code
<?php
class FunctionsProvider
{
protected static $closures = [];
public static function addClosure($name, $closure)
{
if (is_callable($closure)) {
static::$closures[$name] = $closure;
} else {
throw new \Exception('Closure is not callable');
}
}
public static function __callStatic($name, $arguments)
{
if (array_key_exists($name, static::$closures)) {
return call_user_func_array(static::$closures[$name], $arguments);
} else {
throw new \Exception('Unknown method');
}
}
}
//Lets prepare sample closure
$foo = function() {
return "bar";
};
FunctionsProvider::addClosure('foo', $foo);
$return = FunctionsProvider::foo();
var_dump($return);
On the output we will get
string(3) "bar"
I managed to create one using evil eval
, ReflectionClass and SuperClosure library.
Here's my code:
<?php
function register_function(string $name, Closure $closure)
{
$serializedBody = (new SuperClosure\Serializer)->serialize($closure);
$obj = unserialize($serializedBody);
$reflection = new ReflectionClass($obj);
$property = $reflection->getProperty('data');
$property->setAccessible(true);
$data = $property->getValue($obj);
$body = preg_replace('/^function \(/', "function {$name} (", $data['code']);
eval($body);
}
$closure = function($a = 1, $b = 2, $c = 3) {
var_dump(compact('a', 'b', 'c'));
};
register_function('test', $closure);
var_dump(function_exists('test')); // true
test(13, 2, 3);
// array(3) {
// ["a"]=>
// int(13)
// ["b"]=>
// int(2)
// ["c"]=>
// int(3)
// }
In short, you cannot name a \Closure object in PHP.
While I am sure there is an API to do this, it is not exposed at the PHP level. One could possibly write a C Plugin to expose a User-land php function to name the function.