通过额外的步骤从字符串启动 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
我一直在研究从 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