Порядок расширений классов PHP в одном файле

В некоторых случаях определение расширений класса 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 {}

работает отлично. Отличительным фактором является то, что все три возможные пары классов не упорядочены в случае, который дает ошибку, тогда как во всех остальных случаях не упорядочены не более двух из трех пар.

Что происходит под капотом, чтобы вызвать такое поведение?

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

Dave 10.04.2019 19:10

@Dave Это было с PHP 7.0.33 в Ubuntu 16.04.

MTS 10.04.2019 19:12

@Dave PHP с 5.5.x по 7.3.x на x86 и x64 демонстрирует эту фатальную ошибку.

MonkeyZeus 10.04.2019 19:15

Предполагая, что об этом еще не сообщалось, я бы сообщил об этом как об ошибке в PHP bugs.php.net.

MonkeyZeus 10.04.2019 19:19
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
9
4
122
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Поведение не интуитивно понятное, но я не думаю, что это ошибка, это просто эффект того, как 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 {}

Спасибо за подробный ответ. Я просто хотел бы отметить, что «имеет объяснение» не то же самое, что «имеет смысл» :)

MTS 10.04.2019 22:01

Ха-ха, да, наверное, это была не лучшая формулировка. Это определенно не интуитивно. я отредактирую ответ

Don't Panic 10.04.2019 22:12

Это имеет смысл.

axiac 11.04.2019 00:47

Другие вопросы по теме