Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

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

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11149 глав4458 карточек справочника
Дерево руководства

Глава 68 · От первого объекта к диагностике

Информация о классах и объектах: полный справочник функций

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

Узнать, какой объект пришёл в обработчик

Наглядная модель

Объект и имя его класса — разные значения

Создадим Delivery и получим строковое имя для понятной диагностической записи.

  1. class Delivery {}

    Объявлено описание класса; экземпляра пока нет.

  2. $delivery = new Delivery

    Создан один объект, доступный через переменную.

  3. get_class($delivery)

    Функция получает объект и возвращает строку Delivery.

  4. $name = …

    Имя сохранено в новой переменной. Объект не изменён.

  5. echo $name

    На экране Delivery; имя помогает диагностике, а не проверке прав.

Имя класса не равно ID экземпляра и не доказывает доступность каждого его метода. Эти вопросы разбираются отдельными функциями.

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

От переданного объекта до сообщения
  1. new CatalogOptions() создаёт первый объект
  2. describeInput получает его в параметр $options типа object
  3. get_class($options) возвращает CatalogOptions
  4. Функция присоединяет имя к сообщению; echo выводит строку
  5. Второй вызов с DeliveryOptions даёт другое имя

class CatalogOptions {} объявляет вид объекта; пустые фигурные скобки означают отсутствие собственных полей и методов. new создаёт экземпляр. Параметр object $options допускает любой объект, а get_class($options) получает его имя строкой. return возвращает сообщение; точка соединяет строки. Для обычной операции предпочтительнее конкретный интерфейс, а здесь широкий object нужен именно диагностике.

Сначала можно пройти карточку товара и аргументы и результат функции. Имя класса не доказывает правильность настройки, наличие соединения или права пользователя.

Диагностировать неверный объект в обработчике доставкиСамостоятельный пример
<?php
declare(strict_types=1);

class DeliveryOptions {}
class CatalogOptions {}
function describeInput(object $options): string
{
    return 'Обработчик получил: ' . get_class($options);
}
echo describeInput(new CatalogOptions()), "\n";
echo describeInput(new DeliveryOptions()), "\n";
Ожидаемый результат
Обработчик получил: CatalogOptions
Обработчик получил: DeliveryOptions

get_class возвращает имя реального класса экземпляра. Так видно, какой объект передан в ошибочном месте. Для ограничения рабочего входа лучше конкретный тип или интерфейс в параметре: диагностическое имя не заменяет договор и авторизацию.

Если слово «объект» пока не связано с задачей, пройдите два отдельных экземпляра и их данные. В этой главе функции исследуют уже описанные классы; они не заменяют урок создания класса, методов и свойств. Для вызова, параметра и результата есть начальная ступень.

Сначала сформулировать вопрос, затем выбрать функцию

ВопросФункцииЧего результат не обещает
Что это за экземпляр?get_class, get_parent_classИмя не равно праву на действие и не является постоянным ID объекта
Описан ли тип?class_exists, interface_exists, trait_exists, enum_existsНаличие не означает возможность new, совместимый конструктор или рабочую сеть
Подходит ли тип?is_a, is_subclass_ofИнтерфейс задаёт программный контракт, не авторизацию пользователя
Объявлен ли член?method_exists, property_existsОбъявленное может быть private, неинициализированным или нечитабельным
Какие данные доступны?get_object_vars, get_class_varsТекущие значения экземпляра и исходные значения класса различаются
Что загружено в текущем процессе?get_declared_classes, get_declared_interfaces, get_declared_traitsЭто не полный каталог файлов и установленных Composer-пакетов
От какого класса пришёл статический вызов?get_called_classЭто другой вопрос, чем место объявления метода
Как временно поддержать прежнее имя?class_aliasПсевдоним не создаёт независимый тип или новое поведение

Существование, доступность, значение и право пользователя — четыре разных проверки. Для известного контракта параметр интерфейсного типа и обычный вызов часто яснее, чем поиск имён методов. Для диагностики дополнительных признаков подходят Reflection и объявивший метод класс.

Строка имени, импорт use и автозагрузка

Имя класса в коде и произвольная строка — разные вещи. Запись PaymentClient::class даёт корректное имя с учётом namespace/use, но сама не загружает класс и не вызывает конструктор. class_exists обычно может вызвать автозагрузчик, если имя ещё неизвестно; false во втором аргументе запрещает именно эту попытку. Такой false не доказывает, что пакета нет на диске.

Проверка class_exists($name, true)
  1. Имя уже объявлено → ответ true без создания экземпляра
  2. Имя неизвестно → зарегистрированному autoloader передаётся это имя
  3. Загрузчик может подключить PHP-файл; его верхний уровень исполняется
  4. Если тип объявлен, проверка возвращает true; иначе false, если загрузчик не выбросил ошибку

Этот процесс объясняет, почему проверка иногда не является «чистым чтением». Исключение или ошибка внутри autoloader не превращается автоматически в false. Имена из HTTP не следует напрямую использовать для new или require: сначала выбирают разрешённую операцию из собственной карты. Путь файлов и PSR-4 разобраны в Composer и автозагрузке.

Импортированное имя и строка при поиске классаСамостоятельный пример
<?php
declare(strict_types=1);

namespace Demo\Shipping {
    class Client {}
}
namespace Demo\Report {
    use Demo\Shipping\Client as DeliveryClient;
    echo 'Полное имя: ', DeliveryClient::class, "\n";
    echo class_exists(DeliveryClient::class, false) ? "Клиент уже загружен\n" : "Определения пока нет\n";
    echo class_exists('DeliveryClient', false) ? "Короткая строка найдена\n" : "Импорт не применяется к произвольной строке\n";
}
Ожидаемый результат
Полное имя: Demo\Shipping\Client
Клиент уже загружен
Импорт не применяется к произвольной строке

use задаёт соответствие имени при разборе PHP-кода. Строка "DeliveryClient" не проходит такую подстановку. ::class получает корректное полное имя, но само по себе не запускает конструктор и не требует автозагрузки. Второй аргумент false запрещает попытку автозагрузки этой проверки.

Читать описание класса и состояние объекта отдельно

В книге PHP.net общий пример соединяет базовый класс, потомка, свойства и методы. Ниже тот же учебный приём на собственном примере доставки: класс задаёт начальную стоимость, экземпляр меняет её, а диагностика показывает разницу. Это локальные условные числа, не реальное списание денег.

Описание и экземпляр — два разных источника сведений
  1. Delivery описывает default feeKopecks = 30000 и метод label
  2. ExpressDelivery расширяет Delivery; создан отдельный объект
  3. Его feeKopecks меняют на 50000: описание default остаётся 30000
  4. get_class_vars читает defaults; get_object_vars читает доступное текущее состояние
Различить настройки по умолчанию и состояние доставкиСамостоятельный пример
<?php
declare(strict_types=1);

class Delivery
{
    public int $feeKopecks = 30000;
    public function label(): string { return 'Доставка'; }
}
class ExpressDelivery extends Delivery {}
$delivery = new ExpressDelivery();
$delivery->feeKopecks = 50000;
$defaults = get_class_vars(Delivery::class);
$current = get_object_vars($delivery);
echo 'Исполнитель: ', get_class($delivery), "\n";
echo 'Родитель: ', get_parent_class($delivery), "\n";
echo 'По умолчанию: ', $defaults['feeKopecks'], " коп.\n";
echo 'Для этого заказа: ', $current['feeKopecks'], " коп.\n";
Ожидаемый результат
Исполнитель: ExpressDelivery
Родитель: Delivery
По умолчанию: 30000 коп.
Для этого заказа: 50000 коп.

get_class_vars читает доступные начальные значения класса, get_object_vars — доступные текущие свойства экземпляра. Состав зависит от области видимости. Это не универсальный дамп всего внутреннего состояния и не вызов виртуальных свойств через __get.

Упражнение: добавьте второй экземпляр ExpressDelivery, не меняйте его feeKopecks и сравните результаты. Ожидается 50000 копеек у первого, 30000 у второго и default 30000. Затем добавьте private-поле: внешний get_object_vars его не покажет. Это граница текущей области доступа, а не пропавшее значение. Разбор полей и hooks находится в карточках get_object_vars и get_mangled_object_vars.

Доступность, версии и цена исследования

Сигнатура — краткий контракт вызова. object требует экземпляр, string — строку, object|string разрешает оба варианта; bool обозначает true/false, array — массив. $autoload = true задаёт значение при пропуске аргумента. Запись = ? в двух исторических сигнатурах означает специальное отсутствие аргумента, а не значение, которое нужно печатать в своём коде. Не передавайте null вместо пропущенного аргумента. Общая механика разобрана в параметрах и возвращаемом результате.

В примерах с declare(strict_types=1) неподходящие типы скалярных параметров встроенных функций приводят к TypeError; в слабом режиме некоторые скаляры могут преобразоваться, а null у ненулевых встроенных параметров deprecated с PHP 8.1. Специальные старые разборщики имени класса имеют отдельные границы, указанные в карточках. Отсутствующий обязательный аргумент или лишний аргумент встроенной функции обычно даёт ArgumentCountError. Это ошибка вызова, а не отрицательный результат проверки. Не ловите все Throwable ради false: так можно скрыть сбой подключаемого файла.

Эти функции относятся к PHP Core: отдельная установка PECL, Composer или настройка подключения расширения не нужна. У книги нет отдельных страниц setup, configuration или constants; свои INI-режимы для неё не придуманы. Ограничения управляемой среды и общая настройка disable_functions рассматриваются отдельно в настройке PHP. Наличие функции не отменяет требований версии: enum_exists требует PHP 8.1, get_mangled_object_vars — 7.4; поддержка alias встроенного класса появилась в 8.3.

Вызовы get_class() и get_parent_class() без аргумента устарели с PHP 8.3. Старый __autoload удалён в PHP 8.0, поэтому его карточка показывает миграцию, а не действующий шаблон. Неопределённое имя у проверок *_exists обычно даёт false, но у get_class_methods/get_class_vars/get_parent_class в современном PHP может быть TypeError. Ошибки автозагрузки проходят наружу.

Перечисление всех классов/методов/полей строит массив, который занимает память. Повторять его для каждого товара в большом цикле обычно незачем: описание схемы можно вычислить в нужной области и переиспользовать. Не кешируйте доступность без учёта scope и не храните «все объявления» как вечный список при динамической загрузке. Для PHP 8.4+ hooks и lazy-инициализаторы могут добавить выполнение кода при чтении свойств; это важнее неподтверждённых обещаний, какая проверка «быстрее».

Вся категория: точные карточки и соседние инструменты

ФункцияДля чего открыть
__autoloadУзнать удалённый механизм автозагрузки в старом проекте и заменить его современной регистрацией загрузчика.
class_aliasВременно поддержать старое имя класса при переименовании, сохранив тот же тип.
class_existsПроверить, объявлен ли класс, при необходимости дав автозагрузчику найти его файл.
enum_existsПроверить, что имя относится именно к перечислению, а не к обычному классу.
get_called_classПолучить класс, от имени которого продолжается поздно связанный статический вызов.
get_classПолучить имя класса уже созданного объекта для диагностики.
get_class_methodsПолучить имена методов, доступных из текущей области, чтобы исследовать установленный клиент.
get_class_varsПосмотреть доступные исходные значения свойств класса, не создавая объект и не запуская конструктор.
get_declared_classesПолучить снимок классов, уже известных текущему PHP-процессу.
get_declared_interfacesИсследовать интерфейсы, объявленные к текущему моменту выполнения.
get_declared_traitsПолучить имена трейтов, уже объявленных в этом процессе.
get_mangled_object_varsИсследовать сырое хранилище объекта, включая закрытые поля и их владельцев.
get_object_varsПолучить доступные текущие не-static свойства конкретного объекта как массив.
get_parent_classУзнать непосредственного родителя объекта или класса, например при исследовании расширения.
interface_existsПроверить именно объявление интерфейса, например при поддержке необязательного пакета.
is_aПроверить, является ли объект или разрешённое имя данным типом, включая сам тип и его потомков.
is_subclass_ofПроверить наследование или реализацию интерфейса, исключив совпадение с самим исходным типом.
method_existsУзнать, объявлен ли метод, не смешивая это с возможностью и разрешением его вызвать.
property_existsПроверить наличие свойства, даже если оно содержит null или ещё не инициализировано.
trait_existsУзнать, объявлен ли трейт, не путая наличие трейта с его использованием конкретным классом.

Часть карточек уже использовалась в прикладных главах; их прежние адреса сохранены. Ссылки выше ведут к тем же дополненным карточкам. class_parents, class_implements, class_uses и spl_object_id принадлежат SPL; ReflectionClass и ReflectionProperty — отдельной книге Reflection. Они связаны с задачей, но не подменяют официальный список этой категории.

Исследование не должно подменять нормальный вызов

Для диагностики полезны имя класса, перечень доступных членов и проверка загруженности. Для обычной доставки надёжнее передать DeliveryCalculator в типизированный параметр и вызвать calculate. method_exists не доказывает допустимость внешнего вызова; is_callable учитывает свой договор, включая магические вызовы. Полные карточки ниже сохраняют параметры, возвраты и ограничения каждой функции.

Попробуйте. У delivery переопределите текущее feeKopecks, затем сравните get_class_vars и get_object_vars.

Разобрать решение

Начальное значение останется 30000, текущее изменится. Добавьте private-поле: из внешней области оно не появится среди доступных значений. Не выводите эти массивы пользователю без отбора данных.

Функции и методы этой темы

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

get_parent_classУзнать непосредственного родителя объекта или класса, например при исследовании расширения.

PHP 4+ · Core

get_parent_class(object|string $object_or_class = ?): string|false

Узнать непосредственного родителя объекта или класса, например при исследовании расширения.

Что происходит в этом вызове
  1. ExpressDelivery наследует Courier
  2. Courier наследует Delivery
  3. get_parent_class(ExpressDelivery::class) возвращает Courier, один шаг вверх
Вход / результатТочный смысл
$object_or_classЭкземпляр или полное имя существующего класса. Строка может вызвать автозагрузку.
РезультатИмя только непосредственного родителя; false, если родительского класса нет. Реализованные интерфейсы сюда не входят.
Без аргументаБерёт контекст класса вызова; deprecated с PHP 8.3. Вне класса без аргумента возвращает false с диагностикой deprecated.
ОшибкиВ PHP 8 неизвестное имя не является допустимым классом: TypeError. Ошибки autoloader могут пройти наружу. Старое описание false для отсутствующего класса нельзя переносить на PHP 8.

extends объявляет наследование; ::class получает строку имени. Это инструмент объяснения происхождения, а не доказательство совместимости бизнес-поведения.

Сделать один шаг по наследованиюСамостоятельный пример
<?php
declare(strict_types=1);

class Delivery {}
class Courier extends Delivery {}
class ExpressDelivery extends Courier {}
echo get_parent_class(ExpressDelivery::class), "\n";
var_dump(get_parent_class(Delivery::class));
Ожидаемый результат
Courier
bool(false)

У самого Delivery родителя нет. var_dump показывает логическое false, которое echo было бы трудно заметить.

Показать цепочку установленного обработчикаСамостоятельный пример
<?php
declare(strict_types=1);

class Importer {}
class FileImporter extends Importer {}
class CsvImporter extends FileImporter {}
$name = CsvImporter::class;
$chain = [$name];
while (($parent = get_parent_class($name)) !== false) {
    $chain[] = $parent;
    $name = $parent;
}
echo implode(' -> ', $chain), "\n";
Ожидаемый результат
CsvImporter -> FileImporter -> Importer

while повторяет шаг, пока найден родитель. !== false отличает окончание цепочки от имени; [] дописывает элемент. Начальная строка взята из известного класса, а не из HTTP. Для готового списка предков есть SPL class_parents; интерфейсы перечисляет class_implements.

Отдельно обработать ошибочное имя конфигурацииСамостоятельный пример
<?php
declare(strict_types=1);

try {
    get_parent_class('MissingImporter');
} catch (TypeError $error) {
    echo "В конфигурации нет такого класса\n";
}
Ожидаемый результат
В конфигурации нет такого класса

try/catch здесь специально показывает современный сбой контракта. В обычной конфигурации проверяйте разрешённую карту обработчиков при запуске; не скрывайте так произвольные ошибки их конструктора.

Рядом: get_class, is_subclass_of, class_exists. Как выбрать проверку.

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

PHP 5.3+ · Core

get_called_class(): string

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

Что происходит в этом вызове
  1. CsvReport::label() вызывает унаследованный метод
  2. Метод объявлен в Report, но called class — CsvReport
  3. Строка CsvReport попадает в диагностическую подпись
Вход / результатТочный смысл
ПараметрыНет. Это не функция с объектом или строкой имени на входе.
РезультатИмя called class; в статическом методе обычно то же имя, что static::class. Место объявления может быть другим.
Ошибки / версииДоступна с PHP 5.3. Вне классового контекста с PHP 8 выбрасывает Error; раньше warning и false.

Двойное двоеточие выбирает статический метод класса. Такой метод работает без new; внутри него нет $this. Унаследованный код может сохранять информацию, какой потомок начал вызов.

Отличить автора метода от вызывающего классаСамостоятельный пример
<?php
declare(strict_types=1);

class Report
{
    public static function label(): void
    {
        echo self::class, ' / ', get_called_class(), "\n";
    }
}
class CsvReport extends Report {}
CsvReport::label();
Ожидаемый результат
Report / CsvReport

self::class фиксирован местом объявления. get_called_class читает текущий контекст позднего связывания, а не имя файла и не весь стек.

Добавить тип отчёта к общей диагностикеСамостоятельный пример
<?php
declare(strict_types=1);

class Report
{
    protected static function auditLabel(): string
    {
        return 'Сборка ' . get_called_class();
    }
    public static function build(): string
    {
        return static::auditLabel();
    }
}
class SalesReport extends Report {}
class StockReport extends Report {}
echo SalesReport::build(), "\n";
echo StockReport::build(), "\n";
Ожидаемый результат
Сборка SalesReport
Сборка StockReport

Общий код получает нужную подпись для двух отчётов. Вызов static::auditLabel сохраняет late binding. Здесь нет файла журнала и секретных данных; реальный журнал передавайте отдельной зависимостью.

Не считайте это универсальным «кто меня вызвал»: явный нефоровардинг-вызов другого класса может изменить called class; parent::/self:: и static:: имеют правила позднего связывания. Если нужен путь выполнения, используйте debugger или безопасный backtrace. Разбор: статические вызовы и позднее связывание.

Рядом: get_class, get_parent_class. Как выбрать проверку.

is_aПроверить, является ли объект или разрешённое имя данным типом, включая сам тип и его потомков.

PHP 4.2+ · Core

is_a(mixed $object_or_class, string $class, bool $allow_string = false): bool

Проверить, является ли объект или разрешённое имя данным типом, включая сам тип и его потомков.

Что происходит в этом вызове
  1. Объект Courier реализует Delivery
  2. is_a($courier, Delivery::class) проверяет отношение типов
  3. true позволяет выбрать ветку, но не даёт пользователю право оформлять заказ
Вход / результатТочный смысл
$object_or_classОбычно объект. Строка рассматривается как имя типа только при allow_string=true; прочие неподходящие значения дают false.
$classИмя проверяемого класса или интерфейса. Импорт use к строке в кавычках не применяется; используйте ::class.
$allow_stringПо умолчанию false. true разрешает строку первым аргументом и возможную автозагрузку этого имени.
Результат / ошибкиtrue для самого типа, наследника или реализации интерфейса; иначе false. Ошибка загрузчика не гарантированно превращается в false. Имена типов сравниваются без учёта регистра.

Проверка интерфейса удобна на границе, где получено неизвестное значение. Внутри заранее известной системы параметр типа Delivery обычно делает ту же гарантию понятнее, ещё до тела функции.

Узнать подходящий объектСамостоятельный пример
<?php
declare(strict_types=1);

interface Delivery {}
class Courier implements Delivery {}
$delivery = new Courier;
var_dump(is_a($delivery, Delivery::class));
var_dump(is_a($delivery, Courier::class));
Ожидаемый результат
bool(true)
bool(true)

implements означает выполнение интерфейсного контракта. Для известного объекта оператор $delivery instanceof Delivery часто проще. is_a полезна, когда имя целевого типа хранится в переменной.

Проверить класс экспортёра в доверенной карте вариантовСамостоятельный пример
<?php
declare(strict_types=1);

interface Exporter { public function export(): string; }
class CsvExporter implements Exporter
{
    public function export(): string { return "Название;Цена\nЧай;25900"; }
}
$formats = ['csv' => CsvExporter::class];
$selected = $formats['csv'];
if (!is_a($selected, Exporter::class, true)) { throw new LogicException('Неподходящий экспортёр'); }
echo (new $selected())->export(), "\n";
Ожидаемый результат
Название;Цена
Чай;25900

Третий аргумент true разрешает строковое имя класса. По умолчанию is_a не проверяет такую строку как экземпляр. Имя берётся из своей карты, а не из URL. Проверка соответствия интерфейсу не заменяет авторизацию пользователя на сам экспорт.

Если цель — только наследник без самого базового класса, нужна is_subclass_of. Не включайте allow_string лишь ради «чтобы работало» для свободного ввода: автозагрузчик выполняет PHP-код. Проверка принадлежности типа не проверяет пользовательские права и состояние внешнего сервиса.

Рядом: is_subclass_of, class_exists, interface_exists. Как выбрать проверку.

class_aliasВременно поддержать старое имя класса при переименовании, сохранив тот же тип.

PHP 5.3+; internal classes PHP 8.3+ · Core

class_alias(string $class, string $alias, bool $autoload = true): bool

Временно поддержать старое имя класса при переименовании, сохранив тот же тип.

Что происходит в этом вызове
  1. Класс ShippingClient уже содержит реализацию
  2. class_alias связывает старое имя DeliveryClient с тем же типом
  3. new DeliveryClient создаёт ShippingClient; отдельного класса-потомка не появляется
Вход / результатТочный смысл
$classИмя исходного существующего класса. Допускаются также интерфейсы, трейты и enum как именованные типы.
$aliasНовое имя-псевдоним, включая namespace при необходимости; это строка, use к ней не применяется.
$autoloadПри true (default) PHP пробует загрузить исходный тип. false разрешает только уже объявленный.
Результатtrue при регистрации, false с E_WARNING, если исходный тип не найден или имя уже занято. Псевдоним регистрируется в нижнем регистре; типы регистронезависимы.
Версии / ограниченияФункция с PHP 5.3. Встроенные классы разрешены с PHP 8.3. Зарезервированные имена вроде self запрещены и могут завершить выполнение фатально; используйте фиксированные корректные имена.

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

Сохранить старое имя клиента после переименованияСамостоятельный пример
<?php
declare(strict_types=1);

class ShippingClient
{
    public function label(): string { return 'Доставка заказа готова'; }
}
if (!class_alias(ShippingClient::class, 'DeliveryClient', false)) {
    throw new RuntimeException('Не удалось зарегистрировать старое имя');
}
$legacyClient = new DeliveryClient();
echo $legacyClient->label(), "\n";
echo 'Реальный класс: ', get_class($legacyClient), "\n";
Ожидаемый результат
Доставка заказа готова
Реальный класс: ShippingClient

class_alias регистрирует второе имя того же класса. Старый код может создавать DeliveryClient, но объект имеет исходную идентичность класса ShippingClient. Это не наследование и не новая реализация. Третий false запрещает попытку автозагрузки исходного имени.

Один мост совместимости в bootstrapСамостоятельный пример
<?php
declare(strict_types=1);

namespace Shop\Shipping { class Client {} }
namespace {
    $legacy = 'LegacyDeliveryClient';
    if (!class_exists($legacy, false)) {
        class_alias(\Shop\Shipping\Client::class, $legacy, false);
    }
    $client = new LegacyDeliveryClient;
    echo get_class($client), "\n";
    var_dump(is_subclass_of($client, \Shop\Shipping\Client::class));
}
Ожидаемый результат
Shop\Shipping\Client
bool(false)

Проверка предотвращает повторную регистрацию в этом учебном bootstrap. В реальном проекте имя может быть занято чужим типом: не продолжайте молча, а проверьте контракт и завершите настройку с понятной ошибкой. Это не наследование, поэтому is_subclass_of относительно исходного типа даёт false.

Увидеть оба сигнала неудачной регистрацииСамостоятельный пример
<?php
declare(strict_types=1);

set_error_handler(static function (int $level, string $message): bool {
    echo $level === E_WARNING ? "warning\n" : "другая ошибка\n";
    return true;
});
try {
    var_dump(class_alias('MissingClient', 'LegacyClient', false));
} finally {
    restore_error_handler();
}
Ожидаемый результат
warning
bool(false)

Пример намеренно подавляет стандартный текст только в собственном временном обработчике, затем обязательно восстанавливает его. Нельзя принимать warning за успешный alias. Не регистрируйте псевдонимы из пользовательского ввода.

Рядом: class_exists, get_class, is_subclass_of. Как выбрать проверку.

get_class_methodsПолучить имена методов, доступных из текущей области, чтобы исследовать установленный клиент.

PHP 4+ · Core

get_class_methods(object|string $object_or_class): array

Получить имена методов, доступных из текущей области, чтобы исследовать установленный клиент.

Что происходит в этом вызове
  1. Client объявляет public listOrders и private token
  2. Внешний get_class_methods(Client::class) видит доступный public API
  3. Массив имён превращаем в диагностический список, методы не вызываем
Вход / результатТочный смысл
$object_or_classОбъект или полное имя существующего класса; строка может включить автозагрузку.
РезультатМассив имён методов, в том числе унаследованных доступных методов. Параметры, return-типы и исходный код в него не входят.
Область доступаСнаружи видны public, из класса могут быть видны его private/protected. Динамические имена, обслуживаемые __call, не перечисляются.
Ошибки / версииНеверный объект/имя, включая неизвестный класс, в PHP 8 приводит к TypeError. Методам не передаются аргументы и их тела не выполняются; загрузчик может выполняться.

Массив — список строк, а не набор callable. sort ниже нужен только для устойчивого порядка вывода: список исследования не должен зависеть от порядка объявления в исходном файле.

Увидеть публичный API клиентаСамостоятельный пример
<?php
declare(strict_types=1);

class Client
{
    public function listOrders(): array { return []; }
    private function token(): string { return 'demo'; }
}
$methods = get_class_methods(Client::class);
sort($methods);
echo implode(', ', $methods), "\n";
Ожидаемый результат
listOrders

Метод token объявлен, но недоступен внешнему коду. Строка listOrders не означает, что запрос был отправлен: тело метода вообще не запускалось.

Исследовать разницу внутреннего и внешнего обзораСамостоятельный пример
<?php
declare(strict_types=1);

class BaseClient { public function ping(): void {} }
class Client extends BaseClient
{
    private function sign(): void {}
    public static function internalNames(): array
    {
        $names = get_class_methods(self::class);
        sort($names);
        return $names;
    }
}
$outside = get_class_methods(Client::class);
sort($outside);
echo implode(', ', $outside), "\n";
echo implode(', ', Client::internalNames()), "\n";
Ожидаемый результат
internalNames, ping
internalNames, ping, sign

Внутренний вызов выполняется в области Client и видит sign. ping пришёл от родителя. Поэтому кеш такого списка, построенный внутри класса, нельзя бездумно выдавать как внешнее API.

Для документации с параметрами переходите к ReflectionMethod и исследованию клиента. Для известной операции используйте интерфейс и прямой вызов. Не превращайте этот список в публичный HTTP-диспетчер: существование метода не проверяет права пользователя.

Рядом: method_exists, get_class, class_exists. Как выбрать проверку.

get_class_varsПосмотреть доступные исходные значения свойств класса, не создавая объект и не запуская конструктор.

PHP 4+ · Core

get_class_vars(string $class): array

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

Что происходит в этом вызове
  1. Delivery задаёт fee=300 по умолчанию
  2. У экземпляра fee уже 700; static count изменён отдельно
  3. get_class_vars возвращает первоначальные значения описания класса
Вход / результатТочный смысл
$classПолное имя класса, только строка. Для объекта сначала get_class или $object::class. Неизвестное имя может включить autoload.
РезультатАссоциативный массив propertyName => defaultValue для видимых из текущей области свойств; включаются доступные static. Это defaults, а не текущее значение static.
Scope / значенияСнаружи доступны public; внутри собственной области также private/protected. Неназначенное нетипизированное поле имеет null; typed без значения здесь представлено null, хотя читать его у экземпляра нельзя.
Ошибки / PHP 8.4В PHP 8 неизвестный класс — TypeError; объект вместо строки не является допустимым входом (может дать Error преобразования). Virtual hook-свойство не имеет default storage и не включается; backed берётся без выполнения getter.

Функция помогает проверить настройку простого класса. Она не заменяет схему валидации или конструктор: defaults не описывают все ограничения, обязательность и правила изменения.

Отличить defaults от текущих данныхСамостоятельный пример
<?php
declare(strict_types=1);

class Delivery
{
    public $fee = 300;
    public static $count = 0;
}
$delivery = new Delivery;
$delivery->fee = 700;
Delivery::$count = 2;
$defaults = get_class_vars(Delivery::class);
echo $defaults['fee'], ' / ', $defaults['count'], "\n";
echo $delivery->fee, ' / ', Delivery::$count, "\n";
Ожидаемый результат
300 / 0
700 / 2

::$count читает общее static-свойство. get_class_vars возвращает default 0, а не текущие 2. ->fee относится к конкретному экземпляру.

Получить свой default для внутренней настройкиСамостоятельный пример
<?php
declare(strict_types=1);

class ExportSettings
{
    public $format = 'csv';
    private $separator = ';';
    public static function defaultSeparator(): string
    {
        $defaults = get_class_vars(self::class);
        return $defaults['separator'];
    }
}
$outside = get_class_vars(ExportSettings::class);
echo implode(', ', array_keys($outside)), "\n";
echo ExportSettings::defaultSeparator(), "\n";
Ожидаемый результат
format
;

Внутри класса доступен его private default. В реальном экспортере обычный метод или константа чаще яснее отражения всех полей; пример объясняет область доступа и почему результаты двух вызовов отличаются.

Начальная схема поля не означает готовность экземпляраСамостоятельный пример
<?php
declare(strict_types=1);

class Order { public int $number; }
$defaults = get_class_vars(Order::class);
echo $defaults['number'] === null ? "В описании нет присвоенного начального номера\n" : "Начальный номер задан\n";
$order = new Order();
$property = new ReflectionProperty(Order::class, 'number');
echo $property->isInitialized($order) ? "Номер можно читать\n" : "Экземпляр ещё требует инициализации номера\n";
Ожидаемый результат
В описании нет присвоенного начального номера
Экземпляр ещё требует инициализации номера

get_class_vars представляет такое поле через null в своём массиве, но int-свойство экземпляра не получило null и не стало читаемым. Для конкретного экземпляра используйте isInitialized, а обязательные данные передавайте конструктору.

Не применяйте функцию в каждой итерации по заказам: описание одного класса не меняется от заказа к заказу. Для подробной схемы используйте ReflectionProperty; для текущих значений public-свойств экземпляра — get_object_vars. Массив defaults не является безопасным набором разрешённых полей HTTP.

Рядом: get_object_vars, get_class, property_exists. Как выбрать проверку.

get_mangled_object_varsИсследовать сырое хранилище объекта, включая закрытые поля и их владельцев.

PHP 7.4+ · Core

get_mangled_object_vars(object $object): array

Исследовать сырое хранилище объекта, включая закрытые поля и их владельцев.

Что происходит в этом вызове
  1. Parent и Child имеют разные private-поля с одинаковым простым именем
  2. В хранилище ключ включает имя объявившего класса
  3. Сырые ключи различают поля; удаление префикса потеряет это различие
Вход / результатТочный смысл
$objectЛюбой объект; неподходящий тип — TypeError. Функция доступна с PHP 7.4.
РезультатМассив хранимых не-static свойств независимо от visibility; typed без значения пропускаются. Public-ключ обычный, protected начинается с NUL * NUL, private — NUL DeclaringClass NUL.
NUL / вложенностьNUL — байт 0, не четыре печатных символа \0. Вложенные объекты остаются теми же объектами: глубокого клонирования нет.
PHP 8.4Не вызывает get hooks и не включает virtual-свойства без хранилища. Сам по себе не инициализирует lazy-объект; результат может быть пустым/частичным.

Это низкоуровневая диагностика собственного контролируемого объекта. Private ограничивает обычный доступ, но не шифрует значение. Не используйте функцию как короткий способ «вывести объект в JSON» или записать его целиком в журнал.

Увидеть форму внутренних ключейСамостоятельный пример
<?php
declare(strict_types=1);

class Delivery
{
    public $label = 'Курьер';
    protected $zone = 'A';
    private $cost = 300;
}
$values = get_mangled_object_vars(new Delivery);
foreach ($values as $key => $value) {
    echo str_replace("\0", '|', $key), '=', $value, "\n";
}
Ожидаемый результат
label=Курьер
|*|zone=A
|Delivery|cost=300

Замена NUL на | нужна только для видимого учебного вывода; это не безопасная сериализация. Обычный внешний get_object_vars вернул бы только label.

Не объединить два разных private-поляСамостоятельный пример
<?php
declare(strict_types=1);

class BaseClient { private $mode = 'base'; }
class ReportClient extends BaseClient { private $mode = 'report'; }
$values = get_mangled_object_vars(new ReportClient);
echo $values["\0BaseClient\0mode"], "\n";
echo $values["\0ReportClient\0mode"], "\n";
Ожидаемый результат
base
report

Класс-владелец является частью ключа. Если отрезать префикс и оставить mode, одно значение перезапишет другое. Для конвертации в модель пишите явное отображение полей, а не угадывайте структуру чужого объекта.

PHP 8.4+: сырое хранилище и вычисляемый видСамостоятельный пример
<?php
declare(strict_types=1);

class Label
{
    public string $text = 'shop' {
        get => strtoupper($this->text);
    }
    public string $caption { get => 'Заголовок'; }
}
$label = new Label;
$raw = get_mangled_object_vars($label);
echo $raw['text'], "\n";
echo implode(', ', array_keys($raw)), "\n";
echo get_object_vars($label)['text'], "\n";
Ожидаемый результат
shop
text
SHOP

PHP 8.4+. text имеет хранилище и getter; raw читает shop напрямую. caption — virtual и в raw отсутствует. Высокоуровневый get_object_vars читает вычисляемое SHOP. Выбирать нужно нужный смысл данных, а не «короткую функцию».

PHP 8.4+: пустой raw-снимок не доказывает отсутствие полейСамостоятельный пример
<?php
declare(strict_types=1);

class Order { public int $number; }
$order = (new ReflectionClass(Order::class))->newLazyGhost(
    static function (Order $order): void { $order->number = 41; }
);
var_dump(property_exists($order, 'number'));
var_dump(get_mangled_object_vars($order));
echo get_object_vars($order)['number'], "\n";
Ожидаемый результат
bool(true)
array(0) {
}
41

PHP 8.4+. Структура number уже существует, но значение ещё не загружено. Сырой снимок не запускает initializer. get_object_vars после этого запрашивает значения и инициализирует объект.

Рядом: get_object_vars, get_class_vars, property_exists. Как выбрать проверку.

interface_existsПроверить именно объявление интерфейса, например при поддержке необязательного пакета.

PHP 5.0.2+ · Core

interface_exists(string $interface, bool $autoload = true): bool

Проверить именно объявление интерфейса, например при поддержке необязательного пакета.

Что происходит в этом вызове
  1. Пакет может объявить PaymentGateway
  2. interface_exists узнаёт, доступно ли описание контракта
  3. is_a($client, PaymentGateway::class) отдельно проверяет конкретного исполнителя
Вход / результатТочный смысл
$interfaceПолное имя интерфейса; сопоставление объявленных имён нечувствительно к регистру.
$autoloadtrue разрешает автозагрузку неизвестного имени, false проверяет только уже объявленное.
Результат / ошибкиtrue только для интерфейса. Существование класса с таким именем не подходит. Ошибки autoloader не поглощаются.
Версия / границаPHP 5.0.2+. Не проверяет, что конкретный объект implements этот интерфейс; это задача instanceof/is_a.

Интерфейс описывает обязательные методы без создания объекта. Условная проверка полезна на границе совместимости пакетов; внутри своего приложения зависимость обычно объявляют явно и считают обязательной.

Отличить интерфейс от реализующего классаСамостоятельный пример
<?php
declare(strict_types=1);

interface Delivery {}
class Courier implements Delivery {}
var_dump(interface_exists(Delivery::class, false));
var_dump(interface_exists(Courier::class, false));
var_dump(class_exists(Delivery::class, false));
Ожидаемый результат
bool(true)
bool(false)
bool(false)

Имя класса и имя интерфейса относятся к разным видам объявления. Наличие Courier не превращает его имя в интерфейс.

Явно выбрать режим без необязательного контрактаСамостоятельный пример
<?php
declare(strict_types=1);

$contract = 'OptionalVendor\\AuditSink';
if (interface_exists($contract, false)) {
    echo "Контракт внешнего аудита загружен\n";
} else {
    echo "Работаем с локальным учебным журналом\n";
}
Ожидаемый результат
Работаем с локальным учебным журналом

Имя вымышленное, режим выбран сознательно. false означает «ещё не объявлен», а не «не установлен». Если подключён Composer и разрешена загрузка, используйте default true. Для обязательного аудита нельзя молча выбирать запасной режим: завершите настройку с понятной ошибкой.

Рядом: class_exists, is_a, get_declared_interfaces. Как выбрать проверку.

trait_existsУзнать, объявлен ли трейт, не путая наличие трейта с его использованием конкретным классом.

PHP 5.4+ · Core

trait_exists(string $trait, bool $autoload = true): bool

Узнать, объявлен ли трейт, не путая наличие трейта с его использованием конкретным классом.

Что происходит в этом вызове
  1. trait DemoLog содержит общий метод
  2. trait_exists(DemoLog::class) проверяет наличие этого набора реализации
  3. Использование use DemoLog в Client — отдельный факт о Client
Вход / результатТочный смысл
$traitИмя трейта с namespace; объявленные имена проверяются без учёта регистра.
$autoloadtrue разрешает загрузку, false смотрит только текущие объявления.
Результат / ошибкиtrue для трейта, false для других видов/отсутствия. Ошибка загрузчика может прервать проверку.
Версия / границаPHP 5.4+. Трейт не является типом объекта для is_a/instanceof; он добавляет реализацию в классы при их объявлении.

Эта проверка нужна диагностике библиотек, а не каждому вызову метода. Если класс уже объявлен с use, реализация встроена в его состав; менять набор трейтов объекта во время работы таким способом нельзя.

Увидеть объявление общего методаСамостоятельный пример
<?php
declare(strict_types=1);

trait DemoLog { public function label(): string { return 'demo'; } }
class Client { use DemoLog; }
var_dump(trait_exists(DemoLog::class, false));
var_dump(class_exists(DemoLog::class, false));
echo (new Client)->label(), "\n";
Ожидаемый результат
bool(true)
bool(false)
demo

use внутри класса включает методы трейта. Это другое употребление use, чем импорт имени namespace.

Разделить наличие трейта и использование классомСамостоятельный пример
<?php
declare(strict_types=1);

trait DemoLog {}
class LoggedClient { use DemoLog; }
class PlainClient {}
var_dump(trait_exists(DemoLog::class));
foreach ([LoggedClient::class, PlainClient::class] as $name) {
    $used = class_uses($name);
    echo $name, ': ', isset($used[DemoLog::class]) ? 'есть' : 'нет', "\n";
}
Ожидаемый результат
bool(true)
LoggedClient: есть
PlainClient: нет

SPL class_uses возвращает непосредственно используемые трейты и не обходит автоматически родителей и вложенные трейты. Для полного дерева нужен отдельный обход. Нельзя делать is_a($client, DemoLog::class) вместо такой диагностики.

Если нужно обещать метод log всем клиентам, интерфейс выражает контракт, а трейт может отдельно переиспользовать реализацию. Практический путь: общий logger B24 и Telegram.

Рядом: get_declared_traits, interface_exists, class_exists. Как выбрать проверку.

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

PHP 8.1+ · Core

enum_exists(string $enum, bool $autoload = true): bool

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

Что происходит в этом вызове
  1. Status содержит допустимые состояния заказа
  2. enum_exists проверяет вид объявления Status
  3. Отдельно Status::tryFrom проверяет значение paid, а не существование типа
Вход / результатТочный смысл
$enumИмя перечисления с namespace; сопоставление объявленного имени регистронезависимо.
$autoloadПо умолчанию true, неизвестное имя может загрузиться. false не запускает загрузчики.
Результатtrue и для pure enum, и для backed enum; false для обычного класса или неизвестного имени.
Версия / границыPHP 8.1+. Не проверяет конкретный case/value; для строки значения backed enum используют tryFrom/from. Ошибки autoloader могут пройти наружу.

Перечисление задаёт ограниченный набор вариантов. ::Paid обозначает один case, а ::class — имя самого enum. Сначала различим типы, затем отдельно проверим значение из данных.

Enum тоже класс, но класс не всегда enumСамостоятельный пример
<?php
declare(strict_types=1);

enum Status { case Paid; }
class Settings {}
var_dump(enum_exists(Status::class, false));
var_dump(class_exists(Status::class, false));
var_dump(enum_exists(Settings::class, false));
Ожидаемый результат
bool(true)
bool(true)
bool(false)

PHP 8.1+. class_exists включает enum; для точного вопроса выбирайте enum_exists.

Отделить доступность enum от допустимости состоянияСамостоятельный пример
<?php
declare(strict_types=1);

enum Status: string { case New = 'new'; case Paid = 'paid'; }
$enum = Status::class;
if (enum_exists($enum, false)) {
    foreach (['paid', 'unknown'] as $value) {
        $status = Status::tryFrom($value);
        echo $status?->name ?? 'Недопустимый статус', "\n";
    }
}
Ожидаемый результат
Paid
Недопустимый статус

PHP 8.1+. : string связывает case со строковым значением; tryFrom возвращает case или null. ?-> не читает свойство у null, ?? даёт запасной текст. Проверка наличия enum сама по себе не валидирует unknown. Для параметризованного произвольного enum отдельно проверьте BackedEnum и ожидаемый тип значений.

Рядом: class_exists, get_declared_classes, is_a. Как выбрать проверку.

get_declared_classesПолучить снимок классов, уже известных текущему PHP-процессу.

PHP 4+ · Core

get_declared_classes(): array

Получить снимок классов, уже известных текущему PHP-процессу.

Что происходит в этом вызове
  1. До подключения файла снимаем набор имён
  2. require подключает учебный класс, выполняет верхний уровень файла
  3. Разность после/до показывает только новые объявления этого процесса
Вход / результатТочный смысл
ПараметрыНет. Функция не принимает папку проекта или namespace.
РезультатМассив имён объявленных классов: встроенных, пользовательских, enum и зарегистрированных class-alias; набор зависит от окружения и уже загруженных файлов. Занятое встроенным классом имя нельзя повторно объявить для своего класса.
Порядок / версииС PHP 7.4 конкретный порядок результата не гарантируется. Не сравнивайте вывод целиком с универсальным списком.
ГраницаНе сканирует файлы и не вызывает загрузчик ради обнаружения всех Composer-классов. Обычный успешный результат — массив, в том числе с множеством системных имён.

Снимок полезен для локальной диагностики: что действительно загрузилось при включении расширения проекта. Для реестра плагинов лучше явная карта или метаданные пакета, поскольку «всё известное процессу» включает посторонние типы.

Найти свой класс без печати всего окруженияСамостоятельный пример
<?php
declare(strict_types=1);

class Delivery {}
$names = get_declared_classes();
var_dump(in_array(Delivery::class, $names, true));
Ожидаемый результат
bool(true)

in_array с true проверяет точное совпадение строки. Полный массив зависит от установленных расширений, поэтому тестирует только собственное объявление.

Показать, что подключил один файлСамостоятельный пример
<?php
declare(strict_types=1);

$before = get_declared_classes();
require __DIR__ . '/exporters.php';
$added = array_values(array_diff(get_declared_classes(), $before));
sort($added);
echo implode(', ', $added), "\n";
Ожидаемый результат
CsvExporter, XmlExporter

array_diff оставляет новые значения, array_values пересобирает числовые ключи. Объявления намеренно вынесены в отдельный файл: без этого раннее объявление в том же файле могло бы уже присутствовать в первом снимке. Реальный подключаемый файл способен объявить и другие классы.

В списке также могут оказаться анонимные классы со служебными именами и aliases в нижнем регистре. Не превращайте имя из этого массива напрямую в путь, URL или команду. Если нужны только конкретные исполнители интерфейса, фильтруйте свою разрешённую карту через is_a/is_subclass_of.

Рядом: get_declared_interfaces, get_declared_traits, class_exists, is_subclass_of. Как выбрать проверку.

get_declared_interfacesИсследовать интерфейсы, объявленные к текущему моменту выполнения.

PHP 5+ · Core

get_declared_interfaces(): array

Исследовать интерфейсы, объявленные к текущему моменту выполнения.

Что происходит в этом вызове
  1. Подключаем файл контрактов интеграции
  2. В таблицу интерфейсов добавляется Delivery
  3. Разность снимков показывает новый контракт; исполнителя это ещё не создаёт
Вход / результатТочный смысл
ПараметрыНет. Функция не получает объект.
РезультатМассив имён всех объявленных интерфейсов, включая системные и загруженные пользовательские. Набор зависит от расширений и загрузки проекта.
Граница / версияPHP 5+. Не ищет незагруженные файлы; не отвечает, какие интерфейсы реализует отдельный объект. Для этого class_implements либо проверка известного контракта.

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

Найти объявленный контрактСамостоятельный пример
<?php
declare(strict_types=1);

interface Delivery {}
var_dump(in_array(Delivery::class, get_declared_interfaces(), true));
Ожидаемый результат
bool(true)

Объявление interface добавило имя в таблицу; new и implements здесь ещё не было.

Проверить загрузку файла контрактовСамостоятельный пример
<?php
declare(strict_types=1);

$before = get_declared_interfaces();
require __DIR__ . '/contracts.php';
$added = array_values(array_diff(get_declared_interfaces(), $before));
sort($added);
echo implode(', ', $added), "\n";
Ожидаемый результат
Delivery, PaymentGateway

Такой контроль полезен при исследовании bootstrap. В приложении обычно обращаются к известному имени напрямую; не выполняют полный обход интерфейсов на каждом HTTP-запросе.

Не полагайтесь на порядок списка. Добавление расширения меняет окружение, а загрузка файла — состояние текущего процесса. Для одного конкретного имени дешевле по смыслу и понятнее interface_exists; измерения времени здесь не подменяют выбор нужного вопроса.

Рядом: interface_exists, get_declared_classes, is_a. Как выбрать проверку.

get_declared_traitsПолучить имена трейтов, уже объявленных в этом процессе.

PHP 5.4+ · Core

get_declared_traits(): array

Получить имена трейтов, уже объявленных в этом процессе.

Что происходит в этом вызове
  1. Файл diagnostics.php объявляет трейт DemoLog
  2. get_declared_traits видит добавленное имя
  3. Класс может использовать его позже; сейчас это только наличие реализации
Вход / результатТочный смысл
ПараметрыНет. Функция не принимает имя класса.
РезультатМассив объявленных имён трейтов; если ни одного нет, пустой массив. Это снимок уже загруженного кода.
Граница / версияPHP 5.4+. Не сканирует Composer-пакеты, не загружает всё автоматически и не определяет, какие трейты использует конкретный класс.

Трейт переиспользует методы, но не является экземпляром или интерфейсным обещанием. Полный список нужен при исследовании загрузки, а не для каждого обычного вызова метода.

Найти доступную реализациюСамостоятельный пример
<?php
declare(strict_types=1);

trait DemoLog {}
var_dump(in_array(DemoLog::class, get_declared_traits(), true));
Ожидаемый результат
bool(true)

Трейт объявлен, хотя ни один класс его пока не использует.

Увидеть трейты подключённого модуляСамостоятельный пример
<?php
declare(strict_types=1);

$before = get_declared_traits();
require __DIR__ . '/diagnostics.php';
$added = array_values(array_diff(get_declared_traits(), $before));
sort($added);
echo implode(', ', $added), "\n";
Ожидаемый результат
DemoLog, RequestLabel

Временный fixture не пишет настоящий журнал. В большом проекте список служит диагностике: для явной зависимости подключите пакет и используйте его контракт.

Для одного имени применяют trait_exists; для непосредственно использованных трейтов класса — SPL class_uses, а полное дерево родителей/вложенных трейтов обходят отдельно. Сортировка нужна лишь для удобного устойчивого отображения.

Рядом: trait_exists, get_declared_classes, get_declared_interfaces. Как выбрать проверку.

__autoloadУзнать удалённый механизм автозагрузки в старом проекте и заменить его современной регистрацией загрузчика.

PHP 5; deprecated 7.2; removed 8.0 · Core

__autoload(string $class): void — исторический callback; удалён в PHP 8.0

Узнать удалённый механизм автозагрузки в старом проекте и заменить его современной регистрацией загрузчика.

Что происходит в этом вызове
  1. Нужен объект Delivery, но имя ещё не объявлено
  2. Современный loader получает строку Delivery и подключает известный файл
  3. После объявления PHP продолжает исходное new; именно оно создаёт объект
Вход / результатТочный смысл
$classPHP передавал имя ещё не найденного класса/интерфейса. Функцию определял разработчик; это был специальный callback, а не универсальная готовая функция загрузки.
Результат / действиеВозвращаемое значение не предоставляло класс: тело должно было подключить файл и объявить нужный тип. При сохранении неизвестного типа исходная операция могла завершиться ошибкой.
Жизненный циклМеханизм появился в PHP 5, deprecated с PHP 7.2, удалён в PHP 8.0. Определение function __autoload в современном PHP приводит к фатальной ошибке.
Современная заменаspl_autoload_register регистрирует callable и позволяет несколько загрузчиков; Composer создаёт готовый vendor/autoload.php.

В старом проекте вы можете увидеть function __autoload. Не запускайте его в PHP 8 и не пытайтесь спрятать определение за function_exists. Ниже оба примера используют действующую замену; они показывают, что делает автозагрузчик, прежде чем переходить к Composer.

Современная загрузка одного известного классаСамостоятельный пример
<?php
declare(strict_types=1);

$loader = static function (string $name): void {
    if ($name === 'Delivery') {
        require __DIR__ . '/Delivery.php';
    }
};
spl_autoload_register($loader);
try {
    $delivery = new Delivery;
    echo $delivery->label(), "\n";
} finally {
    spl_autoload_unregister($loader);
}
Ожидаемый результат
Курьер

Callback сохранён в $loader, register добавляет его в очередь. Он не создаёт Delivery: исходное new продолжает работу после загрузки. __DIR__ означает каталог данного файла. unregister в finally убирает только наш callback после опыта.

Два загрузчика мирно делят разные именаСамостоятельный пример
<?php
declare(strict_types=1);

$first = static function (string $name): void {
    if ($name === 'Shop\\Delivery') {
        require __DIR__ . '/Delivery.php';
    }
};
$second = static function (string $name): void {
    if ($name === 'Reports\\Sales') {
        require __DIR__ . '/Sales.php';
    }
};
spl_autoload_register($first);
spl_autoload_register($second);
try {
    echo get_class(new Shop\Delivery), "\n";
    echo get_class(new Reports\Sales), "\n";
} finally {
    spl_autoload_unregister($second);
    spl_autoload_unregister($first);
}
Ожидаемый результат
Shop\Delivery
Reports\Sales

Первый callback игнорирует чужое имя, поэтому второй получает шанс загрузить Reports\Sales. Для неизвестного собственного класса не требуется выдавать произвольный успешный результат. Не строите путь require напрямую из HTTP: здесь карта двух точных имён.

В реальном Composer-проекте обычно достаточно require __DIR__ . "/vendor/autoload.php"; он регистрирует загрузчик, а не немедленно загружает все классы. Точное соответствие пространства имён и файлов: PSR-4 и первый запуск. При миграции удалите/переименуйте старое определение, сохраните его ограниченную логику в callback, проверьте порядок загрузки и обработку неизвестных имён. Автозагрузчик не обновляет токены и не делает объект живущим между HTTP-запросами.

Рядом: class_exists, interface_exists, get_declared_classes. Как выбрать проверку.

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.