Почему вы можете создать экземпляр класса из строковой переменной, но не строку в PHP?

Как работает PHP:

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

Но это приводит к ошибке:

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

Во втором примере бросается unexpected T_CONSTANT_ENCAPSED_STRING.

Как оказалось, приведенный выше пример обусловлен приоритетом оператора, поскольку new имеет наивысший приоритет, но...

Аналогично, я могу попробовать создать экземпляр с помощью только строки, как в:

$object = new 'App\MyClass';

И та же ошибка. Почему это?

Ответ 1

Синтаксис PHPs определен в zend_language_parser.y, и он просто не определяет более сложные выражения для 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, когда он был введен.)

Ответ 2

Реализация парсера не является общей, поэтому в некоторых случаях синтаксический анализатор будет зависеть от того, что может на самом деле обрабатывать движок. Там (еще) не является даже официальной официальной грамматикой для PHP, что затрудняет предсказать такие вещи, просто не пробовав их. Это путь мира PHP:)

Ответ 3

PHP ожидает переменную или ссылку на имя для определения объектов, она просто не позволит строки. Вы можете использовать этот хак, который основан на "пустой строке с именем variable", которая мгновенно создается и используется на основе того факта, что ${false} оценивается как ${''} следующим образом, чтобы создать объект из строки:

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

Ответ 4

Это связано с приоритетом оператора: http://php.net/manual/en/language.operators.precedence.php

Поскольку "новый" имеет более высокий приоритет, чем "." он обрабатывается первым.

Что касается вашего кода, то что видит компилятор:

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

Для второй части вашего вопроса, почему: $object = new 'App\MyClass'; не работает. Я не уверен.

Если бы я должен был догадаться, я бы сказал, что это происходит потому, что, когда вы делаете new 'App\MyClass', синтаксический анализатор не знает, что это строка, когда она встречается с ней. Пока линия не была обработана, она не знает, имеет ли она дело со строкой, числом, объектом и т.д. Она может обнаружить это и правильно передать его в строку, но я бы догадался, что это возвращается к приоритету оператора, t выполняйте эти типы приведения до тех пор, пока не будете работать с "новым" оператором.

Любопытно, что я просто протестировал его в Quercus PHP, и он работает нормально. Как говорили другие люди, php не является полностью определенным языком, поэтому такие вещи проявляются как различия между реализациями.