В некоторых случаях определение расширений класса PHP не по порядку приводит к фатальной ошибке, а в некоторых — нет. Я пытаюсь понять основное поведение.
Например, оба
<?php
class BaseClass {}
class FirstExt extends BaseClass {}
а также
<?php
class FirstExt extends BaseClass {}
class BaseClass {}
в порядке, так что это не тот случай, когда простое определение подклассов не по порядку вызывает проблему.
Однако ошибки возникают, когда участвуют три класса, но только в одном конкретном случае, а именно когда цепочка классов определяется в обратном порядке. То есть следующий код приводит к фатальной ошибке:
<?php
class SecondExt extends FirstExt {}
class FirstExt extends BaseClass {}
class BaseClass {}
Если вы попытаетесь запустить это из командной строки (скажем, как main.php
), вы получите
PHP Fatal error: Class 'FirstExt' not found in /path/to/main.php on line 2
Однако любое из пяти других упорядочений трех классов выполняется без ошибок. Я был очень удивлен, что даже
<?php
class SecondExt extends FirstExt {}
class BaseClass {}
class FirstExt extends BaseClass {}
работает отлично. Отличительным фактором является то, что все три возможные пары классов не упорядочены в случае, который дает ошибку, тогда как во всех остальных случаях не упорядочены не более двух из трех пар.
Что происходит под капотом, чтобы вызвать такое поведение?
@Dave Это было с PHP 7.0.33 в Ubuntu 16.04.
@Dave PHP с 5.5.x по 7.3.x на x86 и x64 демонстрирует эту фатальную ошибку.
Предполагая, что об этом еще не сообщалось, я бы сообщил об этом как об ошибке в PHP bugs.php.net.
Поведение не интуитивно понятное, но я не думаю, что это ошибка, это просто эффект того, как PHP загружает классы.
Чтобы класс мог расширить родительский класс, родительский класс уже должен быть определен при определении дочернего класса.
По моим наблюдениям получается, что после разбора файла и начала выполнения определяются следующие классы:
В основном любой класс, который может быть определен во время компиляции, будет, и любые другие классы, не определенные в этот момент, будут (попытаться быть) определенными во время выполнения.
Итак, в этом примере:
<?php
echo class_exists('A') ? "Yes" : "No"; // No
echo class_exists('B') ? "Yes" : "No"; // Yes
echo class_exists('C') ? "Yes" : "No"; // Yes
class A extends B {}
class C {}
class B extends C {}
класс B определяется, когда класс A пытается его расширить, потому что он был определен при разборе файла, потому что класс C был определен в файле до него.
Но в этом примере:
<?php
echo class_exists('A') ? "Yes" : "No"; // No
echo class_exists('B') ? "Yes" : "No"; // No
echo class_exists('C') ? "Yes" : "No"; // Yes
class A extends B {}
class B extends C {}
class C {}
класс B не определен, когда класс A пытается его расширить, потому что он не был определен при анализе файла, потому что класс C не был определен до него в файле.
PHP пытается найти его, но не будет снова проверять тот же файл, а попытается загрузить его автоматически. Вот когда вы получаете "не найдено".
Добавьте четвертый класс, и вы увидите, что это происходит не только тогда, когда классы определены в обратном порядке:
echo class_exists('A') ? "Yes" : "No"; // No
echo class_exists('B') ? "Yes" : "No"; // No
echo class_exists('C') ? "Yes" : "No"; // Yes
echo class_exists('D') ? "Yes" : "No"; // Yes
class A extends B {}
class D {}
class B extends C {}
class C extends D {}
Спасибо за подробный ответ. Я просто хотел бы отметить, что «имеет объяснение» не то же самое, что «имеет смысл» :)
Ха-ха, да, наверное, это была не лучшая формулировка. Это определенно не интуитивно. я отредактирую ответ
Это имеет смысл.
В случае, если это может иметь значение, с какой версией PHP вы тестировали?