Пространства имён в PHP: чек-лист перед продакшеном
Autoloader молчит на локали и падает Class not found в проде. Разбираем, что проверить в namespace, use и composer.json до деплоя, чтобы регистр файлов и кеш классов не уронили сборку.

Класс работает на вашей машине и выдаёт Fatal error: Uncaught Error: Class "App\Service\Payment" not found на продакшене. Причина почти всегда одна: несоответствие между пространством имён, физическим путём файла и тем, что прописано в composer.json. На macOS и Windows файловая система нечувствительна к регистру, на большинстве Linux-серверов — чувствительна. Ниже — чек-лист для PHP 8.x и Composer 2.x, который ловит эти проблемы до того, как их найдёт пользователь.
Почему namespace ломается именно в проде, а не локально
Пространство имён в PHP — это логическая метка внутри файла, объявленная строкой namespace App\Service;. Оно никак не связано с расположением файла на диске напрямую. Связь создаёт автозагрузчик: PSR-41 договаривается, что префиксу App\ соответствует папка src/, а дальше сегменты namespace один в один ложатся на подпапки, а имя класса — на имя файла.
Пока файловая система прощает регистр, ошибка «имя класса Payment, а файл payment.php» невидима. На Linux-сервере с ext4 или xfs эта же строка выбросит fatal error. То же с лишними или недостающими сегментами namespace: локально Composer нередко работает через authoritative classmap из кеша и находит класс по старой карте, а на чистом сервере — уже нет.
Разница между dev и prod почти всегда в трёх вещах: регистр символов, содержимое папки vendor и наличие оптимизированной карты классов. Всё остальное — производные.
Чек-лист: что проверить перед деплоем
- Регистр namespace и имени файла совпадают посимвольно. Класс
App\Service\PaymentGatewayобязан лежать вsrc/Service/PaymentGateway.php. Неservice, неpaymentgateway.php. Проверка: команда ниже находит расхождения без деплоя. - Блок
autoload.psr-4вcomposer.jsonпокрывает все ваши префиксы. Каждому корневому namespace соответствует ровно одна папка (или несколько через массив), и путь заканчивается слэшем. - Собрана оптимизированная карта классов. Без неё автозагрузчик на каждый неизвестный класс лезет в файловую систему, что и по скорости плохо, и маскирует часть ошибок.
- Нет ручных
requireвперемешку с автозагрузкой. Двойное подключение файла с объявлением класса даётCannot declare class, already declared. - Импорты
useуказывают на существующие классы. Опечатка вuse App\Modle\User;не видна до момента обращения к классу. - Папка
vendorсобрана на том же мажоре PHP, что и прод. Расхождение версий вplatformломает часть зависимостей молча.
Как найти расхождения регистра до деплоя
Первое, что стоит запустить, — валидацию манифеста и проверку автозагрузки в строгом режиме. Composer 2.x умеет отдельно проверять, что все классы из ваших PSR-4 путей реально резолвятся:
# проверяем корректность composer.json
composer validate --strict
# собираем карту классов и предупреждаем о нарушениях PSR-4
composer dump-autoload --optimize --strict-psr
Флаг --strict-psr заставляет Composer вывести список классов, чьё имя или расположение не соответствует правилам PSR-4, — включая несовпадение регистра. Именно это чаще всего и стреляет на Linux. Если команда завершилась без предупреждений, регистр и структура папок в порядке. Точное поведение флагов сверяйте с документацией вашей версии Composer — набор опций между 2.x-минорами дополнялся.
Оптимизация автозагрузки для прода
На продакшене классы не появляются и не исчезают между запросами, поэтому дешёвый способ ускорить старт — заранее построить карту и запретить автозагрузчику ходить в файловую систему за неизвестными классами:
# карта классов + запрет поиска мимо карты
composer install --no-dev --optimize-autoloader --classmap-authoritative
Флаг --classmap-authoritative означает: если класса нет в карте — считать, что его нет вообще, не проверяя диск. Это ускоряет автозагрузку, но у него есть цена — см. раздел про подводные камни. Флаг --no-dev выкидывает dev-зависимости, чтобы на проде не оказалось PHPUnit и отладочных пакетов.
Частые ошибки и что делать
Class "App\Service\X" not found, а файл на месте. Сравните посимвольно namespace в шапке файла, путь к файлу и префикс вcomposer.json. Затем пересоберите карту:composer dump-autoload -o. На локали проверяйте регистр вручную — файловая система его прощает, а прод нет.- Класс работал, после добавления нового —
not found. С включённымclassmap-authoritativeновые классы не попадают в карту без пересборки. После каждого деплоя нуженcomposer dump-autoload -oлибо полныйcomposer install. Cannot redeclare class. Где-то остался ручнойrequireилиincludeна файл, который уже подхватывает автозагрузчик. Уберите ручное подключение.- Класс из глобального пространства не находится внутри namespace. Внутри файла с
namespace App;обращение кDateTimeищетApp\DateTime. Пишите\DateTimeили добавьтеuse DateTime;. composer validateругается на устаревший lock. Файлcomposer.lockне соответствуетcomposer.json. Обновите зависимости на dev-машине и закоммитьте оба файла — на прод они едут вместе.
Подводные камни classmap-authoritative в проде
Авторитетная карта классов — самый частый источник инцидентов «вчера работало, сегодня 500». Механика простая: PHP держит карту в памяти через OPcache, и пока карта не пересобрана, автозагрузчик буквально не видит новые файлы, даже если они физически лежат в проекте.
Отдельный слой проблемы — сам OPcache. Даже пересобранная карта не подхватится, если PHP-FPM закешировал старую версию файла автозагрузчика. Здесь помогают два подхода:
| Стратегия деплоя | Что делать с кешем | Риск |
|---|---|---|
| Деплой в ту же папку (in-place) | Перезапустить или перезагрузить PHP-FPM после dump-autoload | Короткое окно, когда файлы новые, а карта старая |
| Атомарный деплой через symlink | Переключить симлинк на новый релиз, затем reload FPM | OPcache может держать путь по старому inode — проверяйте opcache.revalidate_freq |
Если релизы редкие и вы не готовы автоматизировать пересборку карты, безопаснее использовать --optimize-autoloader без --classmap-authoritative: карта ускоряет известные классы, но для отсутствующих в ней автозагрузчик всё же проверит диск. Вы теряете часть выигрыша по скорости, но не получаете «невидимых» новых классов.
Правило для CI: composer dump-autoload -o --strict-psr в пайплайне — дешёвый предохранитель. Он падает на регистре и структуре папок до того, как это увидит прод.
Что делать дальше
Добавьте composer validate --strict и composer dump-autoload -o --strict-psr отдельным шагом в CI до сборки образа — тогда несовпадение регистра свалит пайплайн, а не продакшен. В скрипт деплоя пропишите пересборку карты и reload PHP-FPM после переключения релиза. На проде мониторьте частоту fatal-ошибок с текстом Class not found — их всплеск после релиза почти всегда указывает на непересобранную карту или незасквоженный OPcache. За точным набором флагов вашей версии Composer идите в официальную документацию раздела autoloading: набор опций между минорами 2.x менялся.
Тендерный специалист 44-ФЗ / 223-ФЗ: Разбор PDF-документации закупки, поиск «заточек» под поставщика, чек-лист заявки.
Спросить за 20 ₽Источники: Composer — Autoloading and dump-autoload, PHP: Namespaces — Manual
Частые вопросы
У меня Composer 2.2, а не последний — команды из статьи сработают?
Я пересобрал карту, но класс всё равно not found — что дальше?
Чем PSR-4 отличается от PSR-0 и нужно ли о старом стандарте помнить?
Грозит ли classmap-authoritative проблемами при частых деплоях?
Нужно ли коммитить composer.lock в репозиторий?
Почему на macOS всё работает, а на сервере падает регистр?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.