<? phpukraine СПІВБЕСІДИ
Пошук по платформі
CORE PHP · JUNIOR ЧАСТО ПИТАЮТЬ

Як працює автозавантаження класів через Composer і PSR-4?

Composer реєструє свій завантажувач через `spl_autoload_register`, і при першій згадці класу PSR-4 перетворює його повне імʼя на шлях: найдовший збіг префікса замінюється базовою текою, решта імені стає підтеками й файлом. Classmap натомість заздалегідь сканує теки й будує таблицю «клас - файл», а `dump-autoload -o` перетворює PSR-4-правила на таку саму таблицю, щоб прибрати пошук по файловій системі.

Створив клас у `src/Service/Mailer.php`, а PHP каже `Class not found`. Куди дивитись?
Локально все працює, на сервері той самий код падає з `Class "App\Repository\UserRepository" not found`. Чому?
У чому різниця між `psr-4` і `classmap` у composer.json і коли потрібен другий?
Що робить `composer dump-autoload -o` і чому це радять запускати на продакшені?
composer psr-4 autoload classmap deploy namespaces

Усе починається з vendor/autoload.php. Цей файл піднімає Composer\Autoload\ClassLoader і реєструє його метод loadClass через spl_autoload_register. Далі PHP працює ліниво: поки код не згадав клас, нічого не підключається. Щойно інтерпретатор зустрічає невідоме імʼя, скажімо в new PhpUkraine\Hiring\Domain\Vacancy, він передає це повне імʼя зареєстрованим завантажувачам. Composer шукає в масиві PSR-4-правил найдовший префікс, що з ним збігається. Знайшовши, відрізає його, замінює зворотні слеші в залишку на /, додає .php і приклеює до базової теки. Виходить один конкретний шлях, який перевіряється через file_exists. Ніякого сканування й ніякого «пошуку по проєкту»: одне рядкове перетворення й одна перевірка.

Звідси й вимога, щоб неймспейс повторював структуру тек. Завантажувач обчислює шлях, а не розшукує файл десь на диску. Клас, оголошений як namespace PhpUkraine\Service; у файлі src/Services/Mailer.php, не буде знайдений ніколи, бо правило "PhpUkraine\\": "src/" дає шлях src/Service/Mailer.php. Найпідступніша версія цієї помилки - регістр. Імена класів у PHP регістронезалежні, тож локально на macOS чи Windows файл mailer.php спокійно віддається під імʼя Mailer, а на Linux-сервері та сама збірка падає з Class not found. Тому назва файла має точно збігатися з іменем класу, включно з великими літерами, а Vacancy.php і vacancy.php варто вважати різними файлами навіть тоді, коли ваша файлова система так не вважає.

Classmap вирішує іншу задачу. Composer будує таблицю під час dump-autoload: обходить перелічені теки, розбирає файли токенайзером, запамʼятовує, у якому файлі оголошено який клас, і кладе результат у vendor/composer/autoload_classmap.php. Пошук перетворюється на звертання до масиву. Розплата за це - статичність: файл, доданий після дампу, у карті не зʼявиться. Тому classmap тримають для коду, який PSR-4 у принципі не описує: міграції без неймспейсів, legacy з кількома класами в одному файлі, згенеровані файли з довільними іменами. Для нового коду він не потрібен.

На продакшені ці два механізми зводяться разом. composer install --no-dev --optimize-autoloader (він же -o) проходить PSR-4-теки й розгортає правила в classmap: тепер кожен клас проєкту й вендорів знаходиться за одне читання масиву замість склеювання шляху й file_exists на кожен неймспейс-кандидат. Оптимізація стосується лише пошуку файлів, а не швидкості самого коду, але на застосунку з півтори тисячею класів на запит різниця в кількості stat-викликів відчутна. Класи, яких у карті немає, все ще шукаються старим способом. Прибрати й цей фолбек можна через --classmap-authoritative: карта стає єдиною правдою, чого в ній немає - того не існує. Режим найшвидший і водночас найкрихкіший, тому на дев-машині його не вмикають, а на деплої карту обовʼязково перебудовують після кожної зміни коду.

Кілька місць, де спотикаються найчастіше. autoload-dev з Tests\ не потрапляє у продакшн-дамп при --no-dev, і це навмисно; саме тому забутий прапорець тягне тестові класи у веб. Секція files до автозавантаження взагалі не належить: перелічені файли підключаються одразу при завантаженні vendor/autoload.php, бо функції й константи spl_autoload_register не ловить. PSR-0 у composer.json ще зустрічається в старих пакетах, але офіційно оголошений застарілим, тож у своєму коді використовуйте лише PSR-4. І одне спостереження, яке економить години: під час оптимізованого дампу Composer 2 друкує does not comply with psr-4 autoloading standard. Skipping. для кожного класу з невідповідним неймспейсом. Це попередження в логах збірки - готовий список файлів, які зламаються на сервері, ще до того, як хтось відкриє сторінку.

// composer.json - чотири різні механізми в одній секції
{
  "autoload": {
    "psr-4": {
      "PhpUkraine\\": "src/",               // префікс закінчується на \, база - на /
      "PhpUkraine\\Hiring\\": "src/Hiring/" // виграє найдовший збіг, не порядок
    },
    "classmap": ["database/migrations"],    // сканується під час dump-autoload
    "files": ["src/helpers.php"],           // require на кожному запиті
    "exclude-from-classmap": ["**/Fixtures/"]
  },
  "autoload-dev": {
    "psr-4": { "Tests\\": "tests/" }        // не потрапляє в dump з --no-dev
  }
}

# Як резолвиться PhpUkraine\Hiring\Domain\Vacancy:
#   найдовший збіг префікса   -> "PhpUkraine\Hiring\" => src/Hiring/
#   залишок імені класу       -> Domain\Vacancy
#   \ замінюється на /, плюс .php
#   підсумковий файл          -> src/Hiring/Domain/Vacancy.php
#   файла немає -> завантажувач мовчить, PHP кидає Error: Class not found

# Продакшн-збірка: PSR-4-правила розгортаються в готову таблицю
composer install --no-dev --optimize-autoloader
# результат видно тут, разом з правилами, що реально задеплоєні
#   vendor/composer/autoload_classmap.php
#   vendor/composer/autoload_psr4.php

# Жорсткіший режим: карта - єдине джерело правди, stat-викликів немає зовсім
composer dump-autoload --classmap-authoritative
Що PSR-4 не сканує диск: він робить рядкове перетворення імені класу на шлях, тому неймспейс і тека мають збігатися символ у символ.
Що при кількох правилах виграє найдовший збіг префікса, а не порядок у composer.json.
Що новий клас у вже описаному PSR-4-неймспейсі працює без `dump-autoload`, а нова секція в `autoload` або новий файл у `classmap` потребує перегенерації.
Що `-o` (`--optimize-autoloader`) розгортає PSR-4-правила в classmap і прибирає `file_exists`-перевірки, але для класів поза картою фолбек на файлову систему лишається.
Що `--classmap-authoritative` прибирає й цей фолбек: чого немає в карті - того не існує, і на деплої карту треба будувати заново.
Що імена класів у PHP регістронезалежні, а файли на Linux - ні, звідси класичне «локально працює, на сервері ні».
Запускати `composer dump-autoload` після кожного нового класу, не розуміючи, що в звичайному режимі PSR-4 знаходить його й так.
Ставити `--classmap-authoritative` на дев-машині й потім годину шукати, чому щойно створений клас «не існує».
Писати `"App\": "src/App/"` і одночасно тримати файли в `src/`, а потім додавати клас у `classmap`, щоб «полагодити».
Вважати, що `-o` прискорює сам код: він економить лише stat-виклики на пошуку файлів, а не виконання.
Класти клас `Mailer` у файл `mailer.php` і покладатися на те, що воно завелося на macOS.
Забувати `--no-dev` на продакшені й тягнути в карту `Tests\`, а потім дивуватися, що PHPUnit-класи доступні у вебі.
Ховати ініціалізацію в `files`-автозавантаженні десятками файлів: вони підключаються на кожному запиті незалежно від того, чи потрібні.
ПОРАДА

Скажіть коротку формулу: «PSR-4 - це рядкове перетворення імені класу на шлях, classmap - готова таблиця, а `-o` перетворює перше на друге». Далі згадайте регістр файлів на Linux і різницю між `-o` та `--classmap-authoritative`: саме ці дві деталі відрізняють людину, яка деплоїла, від людини, яка читала документацію.

оновлено 6 вересня 2026 · ліцензія CC-BY-SA-4.0 Знайшли неточність? Напишіть →
ПЕРЕВІРТЕ СЕБЕ

Звичайний PSR-4-завантажувач нічого не сканує: він робить рядкове перетворення повного імені класу на шлях і перевіряє наявність файла. Сканування тек відбувається лише під час побудови classmap - тобто на `dump-autoload`, а не в рантаймі.