如何手动进行字符串插值?

7

我发现插值字符串(即展开其中的变量)的唯一方法是以下方式:

$str = 'This is a $a';
$a = 'test';
echo eval('return "' . $str . '";');

请记住,在实际场景中,字符串是在不同的位置创建的,因此我不能只用'替换"
有没有更好的方法来扩展单引号字符串而不使用eval()?我正在寻找PHP本身提供的东西。
请注意:使用strtr()就像使用sprintf()一样。我的问题与此问题的可能重复部分中链接的问题不同,因为我让字符串控制它想要通过哪些函数调用或属性访问器来获取内容。

1
你是不是想说评估(evaluate)而不是插值(interpolate)? - codea
参见:替换字符串中的变量 - Bad Wolf
@Bad Wolf:这些都是定制解决方案,或者滥用翻译函数来提供此功能。我的问题是,PHP本身是否允许在不使用eval的情况下完成这样的事情? - Parham Doustdar
2
@Codea:在字符串中用它们的值替换变量名被称为字符串插值。 - Parham Doustdar
1
你的问题的实际答案是“否”,没有其他方法可以扩展单引号字符串而不使用eval。eval还会打开您的脚本,可能被滥用为脚本注入。字符串插值与变量扩展不同,其中字符串被解析(请参阅:http://php.net/manual/en/language.types.string.php#language.types.string.parsing) - PeteAUK
显示剩余4条评论
3个回答

4

除了PHP字符串文字语法之外,还有更多的机制可以在字符串中替换占位符。一个相当常见的方法是sprintf

$str = 'This is a %s';
$a   = 'test';
echo sprintf($str, $a);

http://php.net/sprintf

有很多其他更或多或少专业的模板语言。选择你最喜欢的那个。


1
这个字符串是动态的。我会确保我有 $a。但是,根据上下文,$a 可以是一个对象、数组等等。所以我的字符串可能是像 'this is a {$a->content}.' 这样的东西。我不能使用 sprintf 的原因是我不知道文本需要使用的变量,所以我不能只在 sprintf 中使用它。如果我知道了它,我可能也会使用双引号字符串。 - Parham Doustdar
1
这听起来非常疯狂。如果您不知道“字符串”期望的是什么,如何准备好您的环境以包含正确的变量呢?而且,如果您能够以那种方式准备您的环境,为什么不能将其传递给sprintf?无论如何,这仍然听起来像您正在寻找一个模板语言。也许类似 Mustache 或 Twig 的东西可以满足您的需求。 - deceze
2
仍然不确定您将如何完成这项任务。如果您的日志消息需要"{$a->content}",那么您将如何动态地将其与正确的变量匹配?字符串文本插值之所以起作用是因为它直接写在源代码中,因此您可以确保变量可用并将它们与字符串插值匹配。但是,如果变量和字符串来自不同的来源,正确地将它们匹配起来几乎是不可能的(或者至少非常困难)。 - deceze
假设我有一些事件。引发该事件的代码知道需要哪些代码来生成日志消息。它使用数据(变量 $data)调用监听对象,并在使用 eval() 时,监听器从数组中获取该消息。由于 $data 是函数参数之一并且已经在作用域中,因此当它评估类似 'The content is {$data->getContent()}' 的输出时,输出类似于“内容是somecontent”。这样讲清楚吗? - Parham Doustdar
有没有更好的方法来避免拥有许多只包含像 $this->logger->log('User with id ' . $data->getId() . ' has signed up.'); 这样内容的函数? - Parham Doustdar
显示剩余6条评论

1

你听说过 strtr() 吗?

它非常适合这个目的,非常有用,可以创建包含来自数据库的信息的动态HTML内容。

给定以下字符串:

$str = 'here is some text to greet user {zUserName}';

然后您可以使用strtr()解析它:
$userName = 'Mike';
$parsed = strtr($str,array('{zUserName}'=>$userName));
echo $parsed; // outputs: 'here is some text to greet user Mike'
< p >虽然在某些方面中,sprintf更快,但strtr允许您以更友好的方式控制替换内容的位置(例如,sprintf无法很好地处理包含100个占位符的非常长的字符串)。


1
为什么?像'这里是一些文本,显示了{zWhatever}的值'这样有一个字符串会更清晰,然后用你想要的任何内容替换它。这也更加可用,并且可以做你想要的事情,好好考虑一下。你不需要在字符串中调用所有这些内容。 - Félix Adriyel Gagnon-Grenier
嗯,因为我想要获取的内容更加动态。也许看一下我在@deceze答案上的评论会有所帮助? - Parham Doustdar
1
我认为你不是第一个遇到这个问题的人。事实上,我们有很多人在做这件事情,创造非常动态的东西,@deceze和我都试图让你明白你应该更好地考虑这个问题。这种方法允许非常动态的模板和内容创建,我真的不认为你已经充分利用了这些可能性。 - Félix Adriyel Gagnon-Grenier
好的,就像我说的一样,我将使用这些字符串来创建一个函数来获取 $data,并将其传递到一个单引号字符串中,以构造有意义的日志消息。由于我不知道在该字符串中需要 $data 的哪些属性,因此无法使用占位符。这样讲清楚了吗? - Parham Doustdar
虽然这是有道理的,但我坚持认为占位符不需要知道需要哪些属性,它只需要知道它们将被放置在何处。然后,你可以用脚本逻辑找到相关的动态属性来替换这个占位符。 - Félix Adriyel Gagnon-Grenier
显示剩余3条评论

0

这里有一个可能的解决方案。我不确定在你特定的情况下是否适用,但它肯定可以减少对于如此多的单引号和双引号的需求。

<?php
    class a {
        function b() {
            return "World";
        }
    }
    $c = new a;
    echo eval('Hello {$c->b()}.');
?>

当然,在这里,您将使用您要评估的代码替换字符串内容。 - Eric Mayfield
很棒的解决方案,谢谢。然而,我更好奇的是是否可以不使用 eval 就实现这个功能。有没有一个函数可以传递我的字符串(E.G. 'The value of the property is {$object->getRelatedObject()->getProperty()}'),并同时将 $object 传递进去,得到类似 "The value of the property is somevalue." 的结果,或者我需要自己编写这个函数? - Parham Doustdar
我不是100%确定我理解了,因为插值字符串应该基于你包含的对象和相应的类/函数来显示该属性,即当你通过关系深入挖掘时,相关类的函数应该返回一个类似我上面展示的示例的返回值。如果我有所理解,我认为你在问->getProperty()这一部分?如果是这样,当然你需要自己的函数来处理你的软件所完成的具体规格。就像当你创建你的对象时,你可能会有一个传递的属性。 - Eric Mayfield
例如 $object = new functionName($Property); 在这个例子中,你的 functionName 将处理 $Property 变量,并根据你使用对象调用的任何函数进行返回。因此,如果你调用 $object->getProperty(); 它将返回相应的值,就像其他面向对象的层次结构一样。 - Eric Mayfield
不。你的回答完全正确,我的问题与字符串内容无关。我的问题是,既然我已经读过“使用Eval很危险!”,那么有没有一种不使用eval的方法?换句话说,是否有其他你推荐的方法来完成这个任务,而不是通常使用eval呢?这个问题将更清楚地表达我的意图:http://stackoverflow.com/questions/24914449/how-can-i-construct-log-messages-in-a-dry-fashion - Parham Doustdar
1
我认为这篇文章恰好有你需要的内容。如果你往下看一些,回答问题的人提供了大量关于为什么应该避免使用eval以及在实际应用中如何执行字符串代码的信息。[https://dev59.com/e3TYa4cB1Zd3GeqPvYHo] - Eric Mayfield

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