通过额外的步骤从字符串启动 class?

Initiating a class from string with extra step?

我一直在研究从 PHP 中的字符串中证实一个新的 class 实例。这似乎是一个可以接受的过程,但我很好奇为什么它不能用函数调用的 returns 完成。我在下面发布了一个简短的测试,我的结果表明如果有一个变量中介它就可以工作(即 $bar = new $foo->callBar(); 不起作用,而 $x = $foo->callBar(); $bar = new $x; 可以)。

class Foo {
    function callBar() {
        return 'Bar';
    }
}
class Bar {
    function sayHi() {
        echo 'Hi';
    }
}

$foo = new Foo();
$bar = new $foo->callBar();

Warning: Uncaught Error: Class name must be a valid object or a string
in php shell code:1 Stack trace:
#0 {main} thrown in php shell code on line 1

$x = $foo->callBar();
$bar = new $x;
$bar->sayHi();
//Output: Hi

我很想知道这是为什么,或者如果我错了,这样做的正确原因是什么?

我知道使用 new 创建 class 实例有四个选项:

  • 直接命名class:$bar = new Bar;
  • 直接使用变量:$barName = 'Bar'; $bar = new $barName;
  • 使用对象属性:$obj = (object) ['barName' => 'Bar']; $bar = new $obj->barName
  • 使用现有实例:$bar1 = new Bar; $bar2 = new $bar1;

您问题的前半部分答案是 PHP 解析器:它禁止许多可用于构建 hack 的内容,例如 new (Foo)new "Foo"

后半部分可能隐藏在 PHP 来源中:here's the C function 抛出异常。

如 PHP 手册所述,new 关键字接受字符串或对象作为 class 名称。您可以选择在末尾的括号中为构造函数提供参数。

这意味着语法大致预期:

    new         [string|object]     ()
//  ^^ keyword  ^^ name             ^^ optional parentheses

另一个重要的事实是关键字 new 在所有运算符中具有最高的优先级。

将这两个事实结合在一起意味着我们不能使用括号来增加带有new关键字的操作的优先级。如果我们使用括号 PHP 会将它们视为 new 语法的可选部分。这样的东西是行不通的。

$bar = new   ($foo->callBar());
//         ^ missing class name

没有明确的方式告诉 PHP 解析器以其他方式处理。

还有一个值得记住的警告,Edward Surov 在他的回答中部分提到了这一点。 class 名称可以来自任何变量,它是字符串或实例。
下面的代码将创建一个 class Bar 的对象:

$obj = ['a'=>'Bar'];
$bar = new $obj['a'];
// or
$obj = (object) ['a'=>'Bar'];
$bar = new $obj->a;

那么,让我们解释一下您的代码的作用。

    new         $foo->callBar     ()
//  ^^ keyword  ^^ name           ^^ optional parentheses

因为你的 Foo class 没有 属性 callBar 它会触发通知消息,但 PHP 会检查它是否可以无论如何用作 class 名称。因为它不存在,所以它不能是字符串或实例,这就是您看到错误的原因。

这已在 PHP 8 中修复。
Variable Syntax Tweaks RFC 解决了这个问题。现在您可以在创建 class 的实例时使用表达式。唯一需要注意的是运算符优先级。调用 function/method 时应使用 () 表示优先级。例如:

$bar = new ( $foo->callBar() )  ();
//           ^^ expression      ^^ optional parentheses