ChatGPT解决这个技术问题 Extra ChatGPT

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

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

function foo ()
{
    //code here
}

function bar ()
{
    //code here
}

$functionName = "foo";
// I need to call the function based on what is $functionName

h
htoip

$functionName()call_user_func($functionName)


如果您需要调用名称在变量中的对象函数,请执行以下操作: call_user_func( array($obj,$func), $params )
如果您正在处理类和方法,请参阅下面的答案。我没想到我发现了什么。
如果未定义函数,我如何使函数定义错误无效? if($functionName) 够用吗?
@sємsєм:您应该使用 is_callable 函数。它将检查变量是否包含可以作为函数调用的内容(无论是函数名、包含对象实例和函数名的数组,还是匿名函数/闭包)
@Pacerier:提供类名作为数组的第一个元素:call_user_func(array('ClassName', 'method'), $args)
L
Lajos Mészáros

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

${"variableName"} = 12;

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

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

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


@marcioAlmada:发表评论而不是以这种方式编辑其他人的答案;它增加了混乱-不清楚谁说了什么[好奇的人:请参阅修订以获取解释]
好的@kryger。第 2 行 {"functionName"}(); 不合法,会引发语法错误。
谢谢你们,对于回复,它确实会引发错误,即使在 php 5.4 中也是如此,因此该语法仅适用于对象方法。编辑了帖子。
请注意,通常您可以将 Variable functionsVariable variables 组合起来,这样您实际上可以执行 StaticClass::${StaticClass::$method_name}()self::{self::$some_method}() 之类的操作 :) 看起来很奇怪,但是当您希望在 __call 中具有变量加载功能时,它很有用: )
M
MAChitgarha

解决方案:使用PHP7

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

旧方法

更新:此处说明的其中一种旧方法已被删除。有关其他方法的说明,请参阅其他答案,此处不再赘述。顺便说一句,如果这个答案对您没有帮助,您应该返回升级您的东西。 PHP 5.6 支持已于 2019 年 1 月结束(现在甚至不支持 PHP 7.1 和 7.2)。有关详细信息,请参阅 supported versions

正如其他人提到的,在 PHP5(以及 PHP7 等较新版本)中,我们可以使用变量作为函数名,使用 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

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

注意:如果您关心性能(和微优化),请不要使用此方法。正如我测试的那样,这种方法确实比其他方法慢(10倍以上)。

额外示例:使用匿名类

让事情变得有点复杂,您可以结合使用 anonymous classes 和上述功能:

$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)

特别感谢 this PHP talk


不错的表达式列表,还包括:(expressions)->$bar()
@Necro 谢谢,我添加了它!
我试过了,但它不起作用:$class_name = 'TestController'; $fn = 'sayHello'; ($class_name)::{$fn}()。错误说:找不到类“TestController”。我不知道我做错了什么...
@rhemmuuu,您必须使用完全限定的类名,即沿其名称空间。考虑使用:use NamespaceOf\TestController; $class_name = TestController::class
C
Community

对的,这是可能的:

function foo($msg) {
    echo $msg."<br />";
}
$var1 = "foo";
$var1("testing 1,2,3");

来源:http://www.onlamp.com/pub/a/php/2001/05/17/php_foundations.html?page=2


o
olliefinn

如前所述,有几种方法可以实现这一点,最安全的方法可能是 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);
}

I
Internal Server Error

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

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

对我来说是 OOP 的方式,但我给了它一个 +1,因为还没有人提到反射类。
不明白这个答案。它到底在解决什么问题?
K
Krista K

万一其他人被谷歌带到这里,因为他们试图在类中使用变量作为方法,下面是一个实际工作的代码示例。以上都不适用于我的情况。主要区别在于 $c = & new...&$c 的声明中的 &call_user_func 中传递。

我的具体情况是在实现与颜色有关的某人的代码以及 csscolor.php 类中的两个成员方法 lighten()darken() 时。无论出于何种原因,我希望相同的代码能够调用变亮或变暗,而不是用逻辑选择它。这可能是我固执地不只使用 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->{...} 尝试任何操作均无效。在仔细阅读 call_user_func 上 php.net 页面底部的读者提供的内容后,我能够将上述内容拼凑起来。另外,请注意 $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。我讨厌重复代码,所以我经常使用变量变量名。
我不明白。无论如何,对象不是通过引用传递的吗?我的意思是,如果您将 $c 传递给函数并对其进行更改,那么原始对象也会受到影响,对吗?不管你有没有用这个&,对吗?
我尝试了不同的符号组合,只有在阅读 php.org 上的用户评论时,我才能获得工作代码。您需要与负责 php 行为的人员开始讨论。
对于您误读的第二个示例,您正在寻找 call_user_func_array
M
Michel Ayres

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

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

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


应该注意 eval() 将允许相同的行为,但它也允许执行任何 PHP 代码。所以这个变量可以用来劫持系统。 call_user_func() 不会带来这样的安全问题。
eval 不是这个问题的解决方案。
请不要这样做,因为攻击者可能会彻底摧毁您的服务器。
J
Jivan

动态函数名称和命名空间

只是在使用命名空间时添加关于动态函数名称的一点。

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

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

C
Community

如果要调用对象的方法,可以补充@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"

我发布了另一个示例 here


P
PaulJWilliams

使用 call_user_func 函数。


a
asmasyakirah

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

假设我有这些变量和函数:

$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";
}

调用不带参数的函数 // 调用 sayHello() call_user_func($functionName1);你好!使用 1 个参数调用函数 // 调用 sayHelloTo("John") call_user_func($functionName2, $friend);亲爱的约翰,你好!使用 1 个或多个参数调用函数 如果您动态调用函数并且每个函数具有不同数量的参数,这将很有用。这是我一直在寻找(并解决)的案例。 call_user_func_array 是关键 // 你可以添加你的参数 // 1. 通过硬编码静态地 $arguments[0] = "how are you?"; // 我的 $something $arguments[1] = "Sarah"; // my $to // 2. 或动态使用 foreach $arguments = NULL; foreach($datas as $data) { $arguments[] = $data; } // 调用 saySomethingTo("你好吗?", "Sarah") call_user_func_array($functionName3, $arguments);亲爱的莎拉,你好吗?

再见!


R
Rafael Xavier

如果您在对象上下文中尝试动态调用函数,请尝试以下代码:

$this->{$variable}();

r
rakesh.falke

以下代码有助于在 PHP 中编写动态函数。现在函数名可以通过变量'$current_page'动态改变。

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

Anonymous functions(即动态函数)与 variable functions(即动态调用函数)不同。此外,您正在无任何理由地替换 $function 变量。
D
Debashis Prusty

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

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

S
Sirjiskit

考虑到这里给出的一些优秀答案,有时您需要精确。例如。

如果一个函数有一个返回值,例如(布尔值、数组、字符串、整数、浮点数等)。如果函数没有返回值 检查函数是否存在

让我们看看它对给出的一些答案的功劳。

Class Cars{
        function carMake(){
        return 'Toyota';
        }
        function carMakeYear(){
        return 2020;
        }
        function estimatedPriceInDollar{
        return 1500.89;
        }
        function colorList(){
        return array("Black","Gold","Silver","Blue");
        }
        function carUsage(){
        return array("Private","Commercial","Government");
        }
    function getCar(){
    echo "Toyota Venza 2020 model private estimated price is 1500 USD";
    }
 }

我们要检查方法是否存在并动态调用它。

$method = "color List";
        $class = new Cars();
        //If the function have return value;
        $arrayColor = method_exists($class, str_replace(' ', "", $method)) ? call_user_func(array($this, $obj)) : [];
        //If the function have no return value e.g echo,die,print e.t.c
     $method = "get Car";
        if(method_exists($class, str_replace(' ', "", $method))){
        call_user_func(array($class, $method))
        }

谢谢


我看不出这个答案对这个线程有什么价值。请解释示例中 $this 的上下文以及方法名称的变量 $obj。我想我们知道你的意图($class 和 $method 的重新分配),但这不是你所说的。另外,请修复您的格式。我会这样做,但如果您要修改代码,我不会这样做。谢谢。
M
Mohd Abdul Mujib

我想到的一种非常规方法是,除非你通过一些超级自主的 AI 自己编写来生成整个代码,否则你想要“动态”调用的函数很有可能, 已在您的代码库中定义。那么为什么不检查字符串并跳出臭名昭著的 ifelse 舞蹈来召唤......你明白我的意思。

例如。

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

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

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

它消除了您的应用程序中的许多不确定性,同时让您有机会在字符串与任何可用函数的定义不匹配时执行回退函数。恕我直言。


如果变量的值已经是函数的名称,为什么还要额外工作?此外,最好在运行之前获得可靠的反馈以查看 php 是否存在该函数:if (method_exists($functionName)) $functionName(); else //exception or handle
是的,您的观点是有效的,但是由于函数名称是动态的,因此在您的逻辑中,如果存在具有该名称的函数,则应执行任何出现的字符串,而不管该函数在当前情况下是否相关。我的方法确保只能执行某些函数,否则会引发错误。它可以通过数组等来简化。
F
Flo

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

类似 $foo = "bar"; $test = "foo";回声$$测试;

应该返回吧,您可以尝试一下,但我认为这不适用于函数