如何动态创建一个函数?

18

我有一个变量,例如$string = "blah";

如何创建一个以变量值为名称的函数?在PHP中是否可能实现?

例如:function $string($args){ ... }或类似的方法,并能够像这样调用:

blah($args);

你是否在寻找匿名函数 - Indranil
6
糟糕的想法。你到底想干什么? - Ignacio Vazquez-Abrams
不行,我需要通过同一段代码访问该名称 :) - Alex
但是人们随时可以创建类属性。我不明白为什么在这种情况下函数应该特殊。 - Alex
2
@IgnacioVazquez-Abrams,但Doctrine一直在这样做。您可以在实体类中声明属性,然后可以调用Doctrine方法,例如$this->getDoctrine()->findByFirstName(); 这有什么不好的设计吗?我觉得非常直观。 - ILikeTacos
显示剩余3条评论
5个回答

17

这可能不是一个好主意,但你可以这样做:

$string = "blah";
$args = "args"
$string = 'function ' . $string . "({$args}) { ... }";
eval($string);

嗯,有趣,这应该可以工作,我没有想到那个 :D - Alex
6
这应该是被接受的答案。这是唯一一个定义了函数而不是闭包的解决方案。尝试使用 get_defined_functions()['user']get_defined_vars() 看看它们之间的区别。 - jameshfisher
@downvoter,我希望你已经阅读了我回答开头的免责声明。 - fardjad
是的,在某些情况下这是正确的答案。例如用法:如果你正在使用表达式引擎,并且想要编写单元测试而不必引导EE,你可能仍然需要全局的ee()函数。这将无法作为闭包工作。 - John Hunt
3
哎呀,这是对问题最好的答案。如果明智使用,使用“eval”并不算坏。如果那么糟糕,那为什么大多数受人尊敬的脚本语言都支持它呢?假设语言解释器会为“eval”分叉另一个解释器进程是荒谬的;因为这样做的话,这种语言就完全有缺陷了。通常,“eval”是通过将文本传递给进程的“stdin”来完成的——就像代码的其余部分一样。 - user4244405
我会给它点踩,因为很多没有经验的人可能只是看到它,说“哇”,然后简单地使用它。 - Adam Pietrasiak

12

那听起来不像是一个很好的设计选择,它可能值得重新考虑,但是...

如果您正在使用 PHP 5.3,您可以使用匿名函数。

<?php
$functionName = "doStuff";
$$functionName = function($args) {
    // Do stuff
};

$args = array();
$doStuff($args);
?>

2
从技术上讲,这并不是定义函数,而是闭包。尝试使用 get_defined_functions()['user']get_defined_vars() 来查看区别。 - jameshfisher
1
@jameshfisher PHP的“闭包”也被称为匿名函数,并且在语法中使用关键字function。当然,这并没有定义一个命名函数,但我认为它超越了吹毛求疵的程度,到达了不合理的地步,说它根本没有定义一个“函数”。 - Mark Amery
@MarkAmery PHP的“函数”和“变量”位于不同的命名空间中。它们在本质上是不同的。这是一个语义问题,而不是语法问题。 - jameshfisher
为什么要让代码示例如此复杂:$foo = function () { ... }; $foo();。就这样。 - mms27
1
@mms27 因为那并没有回答原始问题,它只是定义并执行了一个匿名函数。 - Richard John

11

好的,接受挑战!

无论这个问题有多奇怪(实际上不是),让我们认真对待它一会儿!拥有一个能够声明函数并使其成为现实的类可能会很有用:

<?php

customFunctions::add("hello",                 // prepare function "hello"

    function($what) {
        print "Hello $what, as Ritchie said";
        print "<br>";
    }

);

customFunctions::add("goodbye",               // prepare function "goodbye"

    function($what,$when) {
        print "Goodbye cruel $what, ";
        print "I'm leaving you $when";
        print "<br>";
    }

);

eval(customFunctions::make());                // inevitable - but it's safe!

完成了!现在它们是真正的函数。没有$前缀,也不会在每次调用时运行时评估 - eval()仅在声明时需要一次。之后,它们像任何函数一样工作。

让我们试试它们:

    hello('World');                 // "Hello World"
    goodbye('world','today');       // "Goodbye cruel world, I'm leaving you today" 

魔法背后的原理

这是能够完成此操作的类。真的不是一个复杂的类:

class customFunctions {

    private static $store = [];
    private static $maker = "";
    private static $declaration = '
        function %s() {
            return call_user_func_array(
                %s::get(__FUNCTION__),
                func_get_args()
            );
        }
    ';

    private static function safeName($name) {
        // extra safety against bad function names
        $name = preg_replace('/[^a-zA-Z0-9_]/',"",$name);
        $name = substr($name,0,64);
        return $name;
    }

    public static function add($name,$func) {
        // prepares a new function for make()
        $name = self::safeName($name);
        self::$store[$name] = $func;
        self::$maker.=sprintf(self::$declaration,$name,__CLASS__);
    }

    public static function get($name) {  
        // returns a stored callable
        return self::$store[$name];
    }

    public static function make() {  
        // returns a string with all declarations
        return self::$maker;
    }

}

它为您的函数提供了内部存储,并声明“真正”的函数来调用它们。这类似于fardjad的解决方案,但使用真正的代码(而不是字符串),因此更加便捷和易读。


2

1
你可以通过存储在变量中的函数名来调用该函数,还可以将函数分配给变量,并使用该变量调用它。如果这不是您想要的,请说明更多细节。

4
这个回答如果作为评论的话会更好 :) - PeeHaa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接