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

Php 是否可以从闭包创建命名函数?,php,Php,在PHP中,可以使用如下命名函数: function foo() { return "bar"; } $foo = function() { return "bar"; }; 您可以使用如下的闭包: function foo() { return "bar"; } $foo = function() { return "bar"; }; 闭包非常棒,而且很容易创建,但不幸的是,我需要使用的库确实需要一个命名函数。是否可以从闭包动态创建命名函数?也就是说,不是提

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

function foo()
{
   return "bar";
}
$foo = function() {
    return "bar";
};
您可以使用如下的闭包:

function foo()
{
   return "bar";
}
$foo = function() {
    return "bar";
};
闭包非常棒,而且很容易创建,但不幸的是,我需要使用的库确实需要一个命名函数。是否可以从闭包动态创建命名函数?也就是说,不是提前在代码中定义所有函数,而是更像一个
register\u函数($name,callable$closure)


我知道
create\u function
,但它将PHP字符串作为函数体,并只是
eval
s它,这不是我想要的。

您可以使用回调创建全局数组。通过
register\u func($name,$callback)
添加到此全局数组,并通过
call\u func($name,$parameter1,$parameter2,…)
调用函数


如果不使用
eval
我认为这不可能从回调创建命名函数。

我不认为如果可以通过闭包动态创建命名函数,但使用类的神奇方法,您可能会发现此解决方法很有帮助:

<?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();

我的建议是使用静态方法,因为它们可以像简单函数一样被调用。我们可以用神奇的函数
\uu 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);

我设法用邪恶的
eval
和库创建了一个

这是我的密码:

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

简而言之,您不能在PHP中命名\Closure对象


虽然我确信有一个API可以做到这一点,但它并没有在PHP级别公开。你可以编写一个C插件来公开一个用户用php函数来命名这个函数。

你说的“想要一个命名函数”是什么意思?你必须做
library\u function('my\u func')
,然后它做
function library\u function($func){$func();}
。不,库是Smarty,用例是注册插件。它会根据您提供的可调用类型对模板进行特定编译,而当前不支持闭包。它可以是实例方法、静态方法或命名函数。因此smarty需要命名函数,但您不想命名它?我不能命名它们,因为它们实际上是运行时定义的闭包。可以把它看作是foreach($things as$thing){$closureforthing($thing);registerClosure($closure);}
。无论如何,对于问题的范围来说,确切的用法有点太多了。我只是想知道这是否可能。如果没有,我将不得不等到相关问题得到解决。作为一般的良好实践,应该避免使用全局变量和名称空间。如果必须创建一个“上帝”数据对象,至少要创建它。@Pheagey这是一个简单的想法。我知道更好的解决方案是(例如)单例回调容器类。但我只想展示一个草案——不是一个完整的解决方案。啊,是的,很好。只是不想让OP养成坏习惯。继续:)@Pheagey我不明白-如果你有更好的解决方案,那就去吧!写下来!;)这似乎确实是不可能的,我最终实现了一些简单地调用适当闭包的对象。不过,您的解决方案更好,因为它不需要每次闭包都实例化一个额外的对象。@Skysplit我希望您这样做只是为了锻炼您的PHP知识,我们永远不会在任何实际项目中看到它。@d3jn我绝对不打算在任何项目中使用它:)检查(当时)很有趣!