如何从存储在变量中的字符串调用函数?


288

我需要能够调用一个函数,但是函数名称存储在变量中,这可能吗?例如:

函数foo()
{
  //在这里编码
}

功能栏()
{
  //在这里编码
}

$ functionName =“ foo”;
//我需要基于$ functionName调用函数

Answers:


464

85
如果您需要调用名称在变量中的对象函数,请执行以下操作:call_user_func(array($ obj,$ func),$ params)
BlackDivine

1
如果您正在处理类和方法,也请参见下面的答案。我没想到会发现什么。
克里斯·K

如果未定义函数,如何使函数定义错误无效?if($ functionName)是否足够?
SaidbakR

14
@sємsєм:您应该使用该is_callable功能。它将检查变量是否包含可以称为函数的内容(例如,函数的名称,包含对象实例和函数名称的数组或匿名函数/闭包)
knittl

6
@Pacerier:提供类名作为数组的第一个元素:call_user_func(array('ClassName', 'method'), $args)
knittl

121

我最喜欢的版本是内联版本:

${"variableName"} = 12;

$className->{"propertyName"};
$className->{"methodName"}();

StaticClass::${"propertyName"};
StaticClass::{"methodName"}();

您也可以将变量或表达式放在方括号内!


1
@marcioAlmada:发表评论,而不用这种方式编辑他人的答案;它增加了混乱-目前尚不清楚谁在说什么[好奇:请参阅修订说明]
kryger 2014年

4
好的,@ kryger。第2行{"functionName"}();不合法,将引发语法错误。
marcio 2014年

4
谢谢大家的答复,即使在php 5.4中,它的确也会引发错误,因此语法仅适用于对象方法。编辑帖子。
Lajos Meszaros 2014年


17

如前所述,有几种方法可能是最安全的方法,call_user_func()或者,如果必须的话,您可以采用的方法$function_name()。可以使用这两种方法传递参数

$function_name = 'foobar';

$function_name(arg1, arg2);

call_user_func_array($function_name, array(arg1, arg2));

如果您要调用的函数属于某个对象,则您仍然可以使用其中任何一个

$object->$function_name(arg1, arg2);

call_user_func_array(array($object, $function_name), array(arg1, arg2));

但是,如果您要使用该$function_name()方法,则最好以某种方式对名称进行动态测试,以测试该功能是否存在

if(method_exists($object, $function_name))
{
    $object->$function_name(arg1, arg2);
}

12

如果有人因为试图将变量用于类中的方法而被Google带来其他人,则下面是实际可用的代码示例。以上都不适合我的情况。关键区别在于的&声明$c = & new...&$c传递call_user_func

我的具体案例是实现某人的代码时要使用的颜色和两个成员方法lighten()以及darken()csscolor.php类中的代码。无论出于何种原因,我都希望具有相同的代码能够调用变亮或变暗,而不是通过逻辑将其选中。这可能是我固执的结果,而不只是使用if-else或更改了调用此方法的代码。

$lightdark="lighten"; // or optionally can be darken
$color="fcc";   // a hex color
$percent=0.15;  
include_once("csscolor.php");
$c = & new CSS_Color($color);
$rtn=call_user_func( array(&$c,$lightdark),$color,$percent);

请注意,尝试进行任何操作$c->{...}均无效。阅读了php.net页面底部的读者提供的内容后call_user_func,我就可以将以上内容拼凑起来。另外,请注意,$params由于数组对我不起作用

// This doesn't work:
$params=Array($color,$percent);
$rtn=call_user_func( array(&$c,$lightdark),$params);

上面的尝试会警告该方法需要第二个参数(百分比)。


“ $ c =&new CSS_Color”是做什么的?我说的是放大器(&)。这会改变什么?

@danon&是“的地址”或“引用”运算符。我实例化了类CSS_Color的新对象,然后将其引用(也称为地址)分配给$ c。我讨厌重复代码,因此我经常使用变量变量名。
克里斯·K

1
我不明白 对象不是通过引用传递的吗?我的意思是,如果将$ c传递给函数并对其进行更改,则原始对象也会受到影响,对吧?不管您是否使用此&,对吗?

我尝试了符号的不同组合,并且只有在阅读php.org上的用户注释时,我才能够获得有效的代码。您需要与负责php行为的人员开始讨论。
克里斯·K

对于您误读的第二个示例,您正在寻找call_user_func_array
Tofandel

12

迟了几年,但这是恕我直言的最好方式:

$x = (new ReflectionFunction("foo"))->getClosure();
$x();

3
对我来说OOP的方法,但是我给了+1,因为还没有人提到反射类。
Lajos Meszaros

不明白这个答案。到底要解决什么问题?
David Spector

9

为了完整起见,您还可以使用eval()

$functionName = "foo()";
eval($functionName);

但是,call_user_func()是正确的方法。


8
应该注意的是,eval()将允许相同的行为,但也将允许执行任何PHP代码。因此,该变量可用于劫持系统。call_user_func()不会带来此类安全问题。
约翰

4
eval不是这个问题的解决方案。
2014年

1
请不要这样做,因为攻击者可能会彻底清除您的服务器。
andreszs

9

解决方案:使用PHP7

注意:有关摘要版本,请参见答案末尾的TL; DR。

旧方法

更新:此处介绍的一种旧方法已被删除。请参阅其他答案以获取有关其他方法的解释,此处不介绍。顺便说一句,如果这个答案没有帮助您,您应该返回升级您的东西。对PHP 5.6的支持已在2019年1月结束(现在甚至不支持PHP 7.0和7.1)。有关更多信息,请参见支持的版本

正如其他人提到的那样,在PHP5(以及类似PHP7的较新版本)中,我们可以将变量用作函数名,use call_user_func()call_user_func_array()(个人而言,我讨厌这些函数)等。

新方法

从PHP7开始,引入了新的方法:

注意:<something>方括号内的所有内容表示一个或多个表达形式的表达式,例如,<function_name>表示构成函数名称的表达式。

动态函数调用:函数名称即时

我们可以一次性使用括号内的一个或多个表达式作为函数名称,形式为:

(<function_name>)(arguments);

例如:

function something(): string
{
    return "something";
}

$bar = "some_thing";

(str_replace("_", "", $bar))(); // something

// Possible, too; but generally, not recommended, because makes your code more complicated
(str_replace("_", "", $bar))()(); 

注意:尽管删除括号str_replace()不是错误,但是放置括号会使代码更具可读性。但是,有时无法做到这一点,例如在使用.运算符时。为保持一致,我建议您始终加括号。

动态方法调用:方法名称即时

就像动态函数调用一样,我们可以对方法调用执行相同的方法,方法调用用大括号而不是括号括起来(有关其他形式,请导航至TL; DR部分):

$object->{<method_name>}(arguments);
$object::{<method_name>}(arguments);

在示例中查看:

class Foo
{
    public function another(): string
    {
        return "something";
    }
}

$bar = "another thing";

(new Something())->{explode(" ", $bar)[0]}(); // something

动态方法调用:数组语法

在PHP7中添加的一种更优雅的方法如下:

[<object>, <method_name>](arguments);
[<class_name>, <method_name>](arguments); // Static calls only

举个例子:

class Foo
{
    public function nonStaticCall()
    {
        echo "Non-static call";
    }
    public static function staticCall()
    {
        echo "Static call";
    }
}

$x = new X();

[$x, "non" . "StaticCall"](); // Non-static call
[$x, "static" . "Call"](); // Static call

注意:与上一个方法相比,使用此方法的好处在于,您不必关心调用类型(即,它是否为静态)。

额外的示例:使用匿名类

使事情变得有些复杂,您可以结合使用匿名类和上述功能:

$bar = "SomeThing";

echo (new class {
    public function something()
    {
        return 512;
    }
})->{strtolower($bar)}(); // 512

TL; DR(结论)

通常,在PHP7中,可以使用以下形式:

// Everything inside `<something>` brackets means one or more expressions
// to form something

// Dynamic function call
(<function_name>)(arguments)

// Dynamic method call on an object
$object->{<method_name>}(arguments)
$object::{<method_name>}(arguments)

// Dynamic method call on a dynamically-generated object
(<object>)->{<method_name>}(arguments)
(<object>)::{<method_name>}(arguments)

// Dynamic method call, statically
ClassName::{<method_name>}(arguments)
(<class_name>)::{<method_name>}(arguments)

// Dynamic method call, array-like (no different between static and non-static calls
[<object>, <method_name>](arguments)

// Dynamic method call, array-like, statically
[<class_name>, <method_name>](arguments)

主要基于此PHP讨论


1
漂亮的表达式列表还包括:(expressions)->$bar()
Necro '18

1
@Necro谢谢,我加了!
MAChitgarha

7

动态函数名称和名称空间

只是为了在使用名称空间时添加有关动态函数名称的要点。

如果您使用的是名称空间,则以下内容将不起作用,除非您的函数位于全局名称空间中:

namespace greetings;

function hello()
{
    // do something
}

$myvar = "hello";
$myvar(); // interpreted as "\hello();"

该怎么办?

您必须改为使用call_user_func()

// if hello() is in the current namespace
call_user_func(__NAMESPACE__.'\\'.$myvar);

// if hello() is in another namespace
call_user_func('mynamespace\\'.$myvar);

3

如果要调用对象的方法,可以补充@Chris K的答案,可以在闭包的帮助下使用单个变量来调用它:

function get_method($object, $method){
    return function() use($object, $method){
        $args = func_get_args();
        return call_user_func_array(array($object, $method), $args);           
    };
}

class test{        

    function echo_this($text){
        echo $text;
    }
}

$test = new test();
$echo = get_method($test, 'echo_this');
$echo('Hello');  //Output is "Hello"

我在这里张贴了另一个例子


3

我从这个问题和答案中学到了什么。谢谢大家!

假设我有以下变量和函数:

$functionName1 = "sayHello";
$functionName2 = "sayHelloTo";
$functionName3 = "saySomethingTo";

$friend = "John";
$datas = array(
    "something"=>"how are you?",
    "to"=>"Sarah"
);

function sayHello()
{
echo "Hello!";
}

function sayHelloTo($to)
{
echo "Dear $to, hello!";
}

function saySomethingTo($something, $to)
{
echo "Dear $to, $something";
}
  1. 调用不带参数的函数

    // Calling sayHello()
    call_user_func($functionName1); 

    你好!

  2. 用1个参数调用函数

    // Calling sayHelloTo("John")
    call_user_func($functionName2, $friend);

    尊敬的约翰,您好!

  3. 调用带有1个或多个参数的函数 如果动态调用函数且每个函数具有不同数量的参数,这将很有用。这是我一直在寻找(并解决)的情况。call_user_func_array是关键

    // You can add your arguments
    // 1. statically by hard-code, 
    $arguments[0] = "how are you?"; // my $something
    $arguments[1] = "Sarah"; // my $to
    
    // 2. OR dynamically using foreach
    $arguments = NULL;
    foreach($datas as $data) 
    {
        $arguments[] = $data;
    }
    
    // Calling saySomethingTo("how are you?", "Sarah")
    call_user_func_array($functionName3, $arguments);

    亲爱的莎拉,你好吗?

再见!



1

以下代码可以帮助编写PHP动态函数。现在可以通过变量“ $ current_page”动态更改函数名称。

$current_page = 'home_page';
$function = @${$current_page . '_page_versions'};
$function = function() {
    echo 'current page';
};
$function();

匿名函数(即动态函数)不同于变量函数(即动态调用函数)。另外,您正在$function无原因地替换变量。
MAChitgarha

0

使用存储在变量中的名称安全地调用函数的最简单方法是:

//I want to call method deploy that is stored in functionname 
$functionname = 'deploy';

$retVal = {$functionname}('parameters');

我使用了如下所示的动态创建Laravel迁移表的方法,

foreach(App\Test::$columns as $name => $column){
        $table->{$column[0]}($name);
}

-5

我想到的一种非常规方法是,除非您通过某种自我编写的超级超自治AI生成整个代码否则很有可能您要“动态”调用的功能已经在代码中定义了基础。那么,为什么不只是检查一下琴弦,然后做臭名昭著的ifelse舞蹈来召唤……你就明白了。

例如。

if($functionName == 'foo'){
  foo();
} else if($functionName == 'bar'){
  bar();
}

switch-case如果您不喜欢ifelse梯子平淡的味道,甚至可以使用。

我了解在某些情况下“ 动态调用函数 ”是绝对必要的(就像一些修改自身的递归逻辑)。但是,大多数日常琐碎的用例都可以躲开。

它消除了应用程序的许多不确定性,同时如果字符串与任何可用函数的定义都不匹配,则使您有机会执行后备函数。恕我直言。


如果变量的值已经是函数的名称,为什么还要做额外的工作?在运行它之前,最好获得可靠的反馈以查看该函数是否已存在: if (method_exists($functionName)) $functionName(); else //exception or handle
Necro

2
是的,您的观点是正确的,但是由于函数名称是动态的,因此在您的逻辑中,如果存在具有该名称的函数,则无论该函数在当前情况下是否相关,都将执行任何字符串。我的方法确保只能执行某些功能,否则会引发错误。可以通过数组等简化它。
Mohd Abdul Mujib

-11

我不知道为什么你必须使用它,对我来说听起来并不好,但是如果只有少量函数,则可以使用if / elseif构造。我不知道是否有可能直接解决。

像$ foo =“ bar”; $ test =“ foo”; 回显$$ test;

应该返回酒吧,你可以尝试一下,但我不认为这将适用于功能

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.