在PHP中,如何在字符串中调用插值函数?

20

假设我的字符串是:

$str = "abcdefg foo() hijklmopqrst";

我该如何让 PHP 调用函数 foo() 并将返回的字符串插入到该字符串的其余部分中?

7个回答

31

如果你正在调用某个类的方法,你可以使用普通的变量扩展。例如:

<?php
class thingie {

  public function sayHello() {
    return "hello";
  }

}

$t = new thingie();
echo "thingie says: {$t->sayHello()}";

这将输出:

thingie 说:你好

请注意,调用周围的大括号是必需的。

4
看起来以这种方式调用全局函数是行不通的。 - Opux
通过将您想要支持的全局函数包装在方法对象中,您可以控制暴露给错误和安全漏洞的足迹。我认为您很难找到一种更简单/安全的方式来提供这样的功能。如果需要,您的包装器方法甚至可以执行其他验证/限制。 - HonoredMule

24

只需使用以下代码:

$str = "abcdefg".foo()."hijklmnopqrstuvwxyz";

在字符串创建过程中将调用该函数。


21
function foo()
{
    return 'Hi';
}
$my_foo = 'foo';
echo "{$my_foo()}";

3
这是一个很棒的“技巧”。你甚至可以向它传递参数或其他变量,例如 echo "{$my_foo('bar')}"echo "{$my_foo($bar)}"。当构建具有许多转义值的 SQL 查询时,特别有用。 - Nick D

9
$foo = foo();
$str = "abcdefg {$foo} hijklmopqrst";

9
$str="abcdefg foo() hijklmopqrst";
function foo() {return "bar";}

$replaced = preg_replace_callback("~([a-z]+)\(\)~", 
     function ($m){
          return $m[1]();
     }, $str);

输出:

$replaced == 'abcdefg bar hijklmopqrst';

这将允许任何小写字母作为函数名。如果您需要其他符号,请将它们添加到模式中,例如 [a-zA-Z_]
非常注意要允许调用哪些函数。至少应检查 $m [1] 是否包含白名单函数,以防止远程代码注入攻击。
$allowedFunctions = array("foo", "bar" /*, ...*/);

$replaced = preg_replace_callback("~([a-z]+)\(\)~", 
     function ($m) use ($allowedFunctions) {
          if (!in_array($m[1], $allowedFunctions))
              return $m[0]; // Don't replace and maybe add some errors.

          return $m[1]();
     }, $str);

"abcdefg foo() bat() hijklmopqrst"上运行测试,输出"abcdefg bar bat() hijklmopqrst"

白名单方法的优化(从允许的函数名称动态构建模式,即(foo|bar))。

$allowedFunctions = array("foo", "bar");

$replaced = preg_replace_callback("~(".implode("|",$allowedFunctions).")\(\)~", 
     function ($m) {
          return $m[1]();
     }, $str);

如果这个函数有参数呢? - Munna Khan

7

简短回答

不行。没有原生的构造来实现这个。

一个简单的连接可能更好。可能更丑,但对于PHP解析器更有效率。

详细回答

嗯。是的。

如果你真的需要从双引号字符串中获取任意表达式的计算结果,你可以实现这个解决方法,利用一个叫做变量函数的特性:

<?php
/**
 * The hack
 *
 * @param $v mixed Value
 * return mixed Value (untouched)
 */
$GLOBALS['_'] = function ( $v ) {
    return $v;
};

// Happy hacking
echo "Today is {$_( date( 'Y-m-d' ) )} and the max function returns {$_( max( 1, 2, 3 ) )}...{$_( str_repeat( ' arrh', 3 ) )}!";

结果:

Today is 2018-02-07 and the max function returns 3... arrh arrh arrh!

这个例子不仅限于 date(), max()str_repeat(): 只要你定义的函数像 foo() 一样返回有效的字符串,你就可以调用它们。
简单来说,这个例子创建了一个简单的变量。它的名字非常短:只是下划线。所以,这个变量真正被称为 $_。这个变量实际上是一个函数,它只返回你在第一个参数中表达的内容。这对你来说是语法糖,因为函数不能很容易地扩展到字符串中。相反,变量很容易扩展。就是这样。
我想知道 PHP 是否会引入原生功能来实现这一点(注意:我是在 2018 年写这个注释的)。
限制:
请注意,即使以这种方式,函数也会在定义字符串时展开。我真的无法猜测你的需求,但如果您需要稍后扩展函数而不是在字符串定义期间进行扩展,则可能需要采用更高级的东西,例如解析器。有许多解析器,特别适用于文本模板。但我认为这超出了这个答案的范围。
欢迎评论/建议其他方法来扩展字符串内的函数或改进您的问题以分享更多上下文信息。
参考资料:

长答案:绝对不行(但你可以使用一些混淆的技巧)。请注意,如果插值字符串位于函数内部,这个解决方案将无法工作。你将不得不使用 $GLOBALS['_'] 或 global $_,这非常麻烦。 - goteguru

5
仍不可能,虽然现有一些可行的方法,但不建议使用。相反,建议坚持使用旧学校点运算符,即$str="abcdefg ". foo() ." hijklmopqrst";
根据复杂(花括号)语法文档
注意:
自PHP 5以来,在{$}内部使用函数、方法调用、静态类变量和类常量是可以的。然而,所访问的值将被解释为在定义字符串的范围内的变量名称。使用单个花括号({})无法访问函数或方法的返回值,也无法访问类常量或静态类变量的值。

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