Инициирование класса из строки с дополнительным шагом? - PullRequest
1 голос
/ 21 сентября 2019

Я пытался обосновать новый экземпляр класса из строки в PHP.Кажется, это приемлемая процедура, однако мне любопытно, почему этого нельзя сделать с помощью возвращений вызова функции.Ниже я опубликовал короткий тест, и мои результаты показывают, что он работает, если есть переменный посредник (то есть $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();

Предупреждение: UncaughtОшибка: имя класса должно быть допустимым объектом или строкой
в коде оболочки php: 1 Трассировка стека:
# 0 {main}, добавляемый в код оболочки php в строке 1

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

Мне бы очень хотелось узнать, почему это так, или если я ошибаюсь, как правильно поступить так?

Ответы [ 2 ]

2 голосов
/ 22 сентября 2019

Как указано в руководстве по PHP, ключевое слово new принимает строку или объект в качестве имени класса.При желании вы можете указать аргумент для конструктора в скобках в конце.

Это означает, что синтаксис приблизительно ожидает:

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

Другим важным фактом является то, что ключевое слово new имеетвысший приоритет всех операторов.

Объединение двух фактов вместе означает, что мы не можем использовать скобки, чтобы увеличить приоритет операции с ключевым словом new.Если мы используем круглые скобки, PHP будет рассматривать их как необязательную часть синтаксиса new.Что-то вроде этого не сработает.

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

Нет просто однозначного способа сказать PHP-парсеру, чтобы он воспринимал это иначе.

Есть еще одна оговорка, о которой стоит помнить, о которой Эдуард Суров частично упомянул в своем ответе.Имя класса может происходить из любой переменной , которая является строкой или экземпляром.
Следующий код создаст объект класса 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 не имеет свойства callBar, он вызовет сообщение Notice, но PHPпроверит, может ли оно быть использовано в качестве имени класса в любом случае.А поскольку он не существует, он не может быть строкой или экземпляром, поэтому вы видите ошибку.

2 голосов
/ 22 сентября 2019

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

  • Имя класса напрямую: $bar = new Bar;
  • Использование переменной напрямую: $barName = 'Bar'; $bar = new $barName;
  • Использовать свойство объекта: $obj = (object) ['barName' => 'Bar']; $bar = new $obj->barName;
  • Использовать существующий экземпляр: $bar1 = new Bar; $bar2 = new $bar1;

Первая половина ответа на ваш вопрос - синтаксический анализатор PHP: он запрещаетмногие вещи, такие как new (Foo) и new "Foo", которые можно использовать для создания хака.

Вторая половина может скрываться в источниках PHP: вот функция C , которая выдает это исключение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...