PHP Manual

Класи автозавантаження в PHP

09. 02. 2020

Я впевнений, що ви знаєте, що при програмуванні PHP-скриптів ми розбиваємо код на багато файлів і, щоб мати всі частини доступними, завантажуємо їх серією викликів `include`, `require` або краще `require_once`, що гарантує завантаження тільки один раз.

У коді це виглядає так:

require_once 'Router.php';
require_once 'Page.php';
require_once 'Paginator.php';

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

Рідне автозавантаження

На щастя, в PHP є вбудована підтримка так званого автозавантаження класів, тобто логіки в коді, яка завантажує файл класу тільки тоді, коли він вперше потрібен (зазвичай при першому створенні екземпляра).

Проста реалізація може виглядати наступним чином:

spl_autoload_register(function (string $className): void {
include 'src/' . $className . '.php';
});
$obj = new MyClass1();
$obj2 = new MyClass2();

При створенні екземпляру класу MyClass1 функція spl_autoload_register зчитує файл MyClass1.php з каталогу src. Ця реалізація передбачає, що кожен клас знаходиться в окремому файлі, який викликається за іменем класу або інтерфейсу.

Більш складні випадки автозавантаження

У реальному застосуванні може виникнути багато неприємних ситуацій, які ускладнюють автозавантаження, наприклад:

  • Клас або інтерфейс не існує взагалі
  • Файл вже завантажувався один раз
  • В одному файлі є декілька класів або інтерфейсів
  • Шлях до класу або інтерфейсу не співпадає з іменем
  • Розташування класу або інтерфейсу змінюється з часом

При цьому нам не потрібно програмувати власне рішення для всього цього, а можна використати вже існуюче рішення, яке було розроблено.

Якщо ви використовуєте Composer, ви, ймовірно, також використовуєте його рідне автозавантаження. Це пов'язано з тим, що коли ви встановлюєте будь-який пакунок, Composer автоматично генерує "карту класів", яка є оглядом класів та їх фізичного розташування.

Потім на початку коду (зазвичай в index.php) ви просто використовуєте:

require __DIR__ . '/vendor/autoload.php';

Однак автозавантаження генерується лише один раз при виклику команди composer dump, тому необхідно перегенерувати автозавантаження при кожній зміні програми.

RobotLoader - елегантне рішення для розробки

Для локальної розробки мені дуже подобається пакет nette/robot-loader, який автоматично обходить структуру каталогів і кешує розташування класів. Тобто, якщо ми завантажуємо клас, то він спочатку дивиться в кеш і тільки якщо його немає, то автоматично переіндексує проект. Таким чином, програмісту взагалі не потрібно відслідковувати, де знаходиться той чи інший файл або клас, а просто програмувати.

Установка через Composer:

composer require nette/robot-loader

Базове пояснення функціоналу описано в самій документації:

Подібно до того, як робот Google сканує та індексує веб-сторінки, RobotLoader сканує всі PHP-скрипти та зазначає, які класи, інтерфейси та ознаки він знайшов у них. Потім він кешує результати своїх досліджень і використовує їх у наступному запиті. Тож вам потрібно лише вказати, які каталоги переглядати і де кешувати.

Після цього він надзвичайно простий у використанні:

$loader = new Nette\Loaders\RobotLoader;
// додати каталоги, які RobotLoader повинен проіндексувати
$loader->addDirectory(__DIR__ . '/app');
$loader->addDirectory(__DIR__ . '/libs');
// встановити кешування на диск в директорію 'temp'
$loader->setTempDirectory(__DIR__ . '/temp');
$loader->register(); // запустити RobotLoader

Параметр $loader->setAutoRefresh(true or false) визначає, чи повинен RobotLoader переіндексувати файли при зустрічі з новим класом. На виробничих серверах це повинно бути вимкнено.

Все, тепер вам більше ніколи не доведеться мати справу з автозавантаженням.

Комбіноване рішення

При розробці реального проекту я використовую комбіноване рішення.

Як це працює в реальному житті, я завантажую встановлені пакунки через автозавантаження Composer (що дуже ефективно) і це вирішує проблему завантаження всіх класів в каталозі vendor.

Потім код для конкретного проекту розміщується в директорії app, де я обробляю автозавантаження лише декількох класів за допомогою RobotLoader. Важливо завжди тримати конкретне нанесення якомога меншим і якомога більше використовувати готові упаковки, це дуже сприяє повторному використанню.

Структура проекту виглядає наступним чином:

/app
Bootstrap.php <-- konfigurace
/model
UserForm.php <-- projektové třídy
RegisterFactory.php
...
/vendor
... <-- knihovny
/www
index.php <-- inicializace

Тестування автозавантаження

Іноді може статися так, що не всі файли завжди будуть завантажуватися і у вас виникнуть проблеми.

Для налагодження рекомендую функцію get_included_files().

Jan Barášek   Více o autorovi

Autor článku pracuje jako seniorní vývojář a software architekt v Praze. Navrhuje a spravuje velké webové aplikace, které znáte a používáte. Od roku 2009 nabral bohaté zkušenosti, které tímto webem předává dál.

Rád vám pomůžu:

Související články

1.
3.
Status:
All systems normal.
2024