Можно ли создать именованную функцию из замыкания?
В PHP вы можете иметь именованные функции следующим образом:
function foo()
{
return "bar";
}
И вы можете иметь Closures следующим образом:
$foo = function() {
return "bar";
};
Закрытие является удивительным и легким в создании, но, к сожалению, библиотеке, которую мне нужно использовать, действительно нужна именованная функция. Можно ли динамически создавать именованную функцию из замыканий? То есть не определяя все функции в коде заблаговременно, но больше похоже на register_function($name, callable $closure)
.
Я знаю create_function
, но тот берет строку PHP как тело функции и просто eval
, это не то, что я ищу.
Ответы
Ответ 1
Я не думаю, что если можно создать именованные функции из-за закрытия на лету, но используя __call()
магический метод класс, вы можете найти это обходное решение полезным:
<?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();
Ответ 2
Вы можете создать глобальный массив с обратными вызовами. Добавьте к этому глобальному массиву register_func($name, $callback)
и вызовите функцию call_func($name, $parameter1, $parameter2, ...)
.
Без использования eval
Я думаю, что невозможно создать именованную функцию из обратного вызова.
Ответ 3
Мне удалось создать одно с помощью зла eval
, ReflectionClass и Библиотека SuperClosure.
Здесь мой код:
<?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)
// }
Ответ 4
Мое предложение - использовать статические методы, потому что их можно вызвать как простые функции. Мы можем достичь этого с помощью магической функции __callStatic
, как в этом коде
<?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);
На выходе мы получим
string (3) "bar"
Ответ 5
Короче говоря, вы не можете назвать объект \Closure в PHP.
Хотя я уверен, что для этого есть API, он не отображается на уровне PHP. Можно было бы написать C-плагин, чтобы открыть функцию php-функции User-land, чтобы назвать функцию.