为什么可以从字符串变量实例化类,而不是PHP中的字符串


Why can you instantiate a class from a string variable but not a string in PHP?

这是如何在PHP中工作的:

$myClass = 'App'MyClass';
$object = new $myClass;

但是这会导致错误:

$myClass = 'MyClass';
$object = new 'App'''.$myClass;

在第二个示例中,抛出一个unexpected T_CONSTANT_ENCAPSED_STRING

事实证明,上面的例子是由于操作符优先级,因为new具有最高优先级,但是…

类似地,我可以尝试只用字符串实例化,如下所示:
$object = new 'App'MyClass';

会抛出同样的错误。为什么会这样?

解析器的实现不是通用的,因此在某些情况下,解析器会在引擎实际上可以处理的事情上出现问题。PHP甚至还没有一个官方的正式语法,这使得在不尝试的情况下很难预测这样的事情。这是PHP世界的方式:)

PHP需要一个变量或名称引用对象定义,它根本不允许字符串。您可以使用这个hack,它是基于"空字符串命名变量"立即创建的,并基于${false}计算为${''}的事实使用,如下所示从字符串创建对象:

$obj = new ${!${''} = 'App'MyClass'}();

php语法在zend_language_parser中定义。它没有为new操作符定义任何更复杂的表达式:

new_expr:
    T_NEW class_name_reference ctor_arguments
            { $$ = zend_ast_create(ZEND_AST_NEW, $2,  }
    |   T_NEW anonymous_class
            { $$ = $2; }

其中class_name_reference为:

class_name_reference:
        class_name      { $$ = $1; }
    |   new_variable    { $$ = $1; }

new_variable允许一组有限的变量表达式:

new_variable:
        simple_variable
            { $$ = zend_ast_create(ZEND_AST_VAR, $1); }
    |   new_variable '[' optional_expr ']'
            { $$ = zend_ast_create(ZEND_AST_DIM, $1, $3); }
    |   new_variable '{' expr '}'
            { $$ = zend_ast_create(ZEND_AST_DIM, $1, $3); }
    |   new_variable T_OBJECT_OPERATOR property_name
            { $$ = zend_ast_create(ZEND_AST_PROP, $1, $3); }
    |   class_name T_PAAMAYIM_NEKUDOTAYIM simple_variable
            { $$ = zend_ast_create(ZEND_AST_STATIC_PROP, $1, $3); }
    |   new_variable T_PAAMAYIM_NEKUDOTAYIM simple_variable
            { $$ = zend_ast_create(ZEND_AST_STATIC_PROP, $1, $3); }
// Copyright (c) 1998-2015 Zend Technologies Ltd., the Zend license 2.00

这就是为什么你不能有字符串表达式。(它从未被扩展过,因为简单的实例化和$classvarnames通常就足够了。因此,允许的语法与PHP3引入时基本相同。)

这是因为操作符的优先级:http://php.net/manual/en/language.operators.precedence.php

因为'new'的优先级高于'。

对于你的代码,编译器看到的是:
$myClass = 'MyClass';
$object = new 'App''';
$object = $object . $myClass;

对于你问题的第二部分,为什么:$object = new 'App'MyClass';是行不通的。我不确定。

如果我不得不猜测,我会说这是因为当您执行new 'App'MyClass'时,解析器在遇到它时不知道它是字符串。在行被处理之前,它不知道它正在处理的是字符串、数字、对象等等。它可以发现并正确地将其转换为字符串,但我猜这又回到了操作符优先级,在处理'new'操作符之前,它不会进行这些类型的转换。

奇怪的是,我刚刚在Quercus PHP中测试了它,它运行得很好。正如其他人所提到的,php不是一种完全定义的语言,所以像这样的事情会在实现之间表现出差异。