是否可以从闭包创建一个命名函数?

时间:2016-08-19 15:11:46

标签: php

在PHP中,您可以使用以下命名函数:

function foo()
{
   return "bar";
}

你可以拥有这样的闭包:

$foo = function() {
    return "bar";
};

闭包非常棒且易于创建,但遗憾的是我需要使用的库真的需要一个命名函数。是否可以动态地从闭包创建命名函数?即没有提前在代码中定义所有函数,但更像是register_function($name, callable $closure)

我知道create_function,但是那个人将PHP字符串作为函数体而只是eval它,这不是我想要的。

5 个答案:

答案 0 :(得分:8)

您可以使用回调创建全局数组。按register_func($name, $callback)添加到此全局数组,并按call_func($name, $parameter1, $parameter2, ...)调用函数。

不使用eval我认为无法从回调创建命名函数。

答案 1 :(得分:4)

我设法使用邪恶的evalReflectionClassSuperClosure库创建了一个。

这是我的代码:

<?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)
// }

答案 2 :(得分:2)

我不认为是否可以动态创建闭包中的命名函数但是使用类的__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();

答案 3 :(得分:2)

我的建议是使用静态方法,因为它们可以像简单函数一样调用。我们可以使用魔术函数__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)&#34; bar&#34;

答案 4 :(得分:1)

简而言之,你不能在PHP中命名一个\ Closure对象。

虽然我确信有一个API可以执行此操作,但它不会在PHP级别公开。有人可能会编写一个C插件来公开User-land php函数来命名函数。