Пространства имён в PHP: типичные ошибки и как их не ловить
Fatal error: Class not found, автолоадер не находит ваш класс, а use не даёт ожидаемого эффекта. Разбираем частые ошибки при работе с namespace в PHP 8.x и почему они возникают.

Вы добавили namespace App\Service; в начало файла, подключили класс через use, а PHP выбрасывает Fatal error: Uncaught Error: Class "App\Service\Mailer" not found. Файл на месте, имя написано верно, а класс не грузится. Разбираем частые ошибки при работе с пространствами имён в PHP 8.x: почему автолоадер молчит, чем global namespace ломает вызовы функций и где use работает не так, как кажется.
Материал для тех, кто уже пишет на PHP и упирается в поведение namespace. Примеры проверены на PHP 8.2 и 8.3, поведение автозагрузки описано по стандарту PSR-4.
Ошибка 1. namespace не первый в файле
Объявление namespace должно быть первой инструкцией в файле — до неё допустима только declare(strict_types=1);. Любой вывод, пробел до открывающего тега или инструкция ломают всё:
<?php
// PHP 8.3
function helper() {} // объявление ДО namespace
namespace App\Util; // Fatal error: Namespace declaration statement has to be the very first statement
Частный случай — «пустая строка» перед <?php, которую вы не видите в редакторе. BOM в начале файла или перенос строки после закрывающего ?> в подключаемом файле дают тот же результат: PHP уже что-то вывел, и объявление пространства имён становится невалидным.
Как ловить. Настройте редактор не сохранять BOM (UTF-8 without BOM) и уберите закрывающий ?> в файлах, содержащих только PHP-код — это рекомендация PSR-12 именно по этой причине.
Ошибка 2. Путь к файлу не совпадает с namespace
Composer с автозагрузкой PSR-4 сопоставляет пространство имён с директорией. Если в composer.json написано:
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
то класс App\Service\Mailer должен лежать в файле src/Service/Mailer.php. Регистр имеет значение на Linux: src/service/Mailer.php отработает на macOS и Windows, но упадёт с Class not found на боевом сервере под Linux. Это классический баг «у меня локально работает».
Автолоадер не читает содержимое файла. Он вычисляет путь по имени класса и подключает файл по этому пути. Если файла там нет — класса нет, даже если он объявлен где-то рядом.
Второй частый промах — имя класса не совпадает с именем файла. Файл Mailer.php должен содержать класс Mailer, а не MailerService.
Как ловить. После добавления или переноса класса выполните:
composer dump-autoload
Эта команда пересобирает карту автозагрузки. Если после неё класс всё равно не находится — проблема в пути или регистре, а не в кеше.
Ошибка 3. Забытый ведущий слеш и разрешение имён
Внутри пространства имён PHP считает любое имя без ведущего \ относительным к текущему namespace. Это главный источник загадочных ошибок с функциями и классами стандартной библиотеки.
<?php
namespace App\Service;
class Report
{
public function build(): string
{
// PHP ищет App\Service\DateTime — а его нет
$now = new DateTime(); // Error: Class "App\Service\DateTime" not found
return $now->format('Y-m-d');
}
}
Правильно — либо ведущий слеш, либо use:
<?php
namespace App\Service;
use DateTime; // импорт из глобального пространства
class Report
{
public function build(): string
{
$now = new DateTime(); // теперь это \DateTime
return $now->format('Y-m-d');
}
}
С функциями и константами тонкость другая. Классы без слеша PHP всегда считает относительными, а вот для функций и констант работает fallback в глобальное пространство: если App\Service\strlen() не найдена, PHP вызовет глобальную strlen(). Поэтому вызовы вроде strlen($s) из namespace работают. Но у этого fallback есть цена — лишний поиск на каждом вызове.
Микрооптимизация: слеш перед функцией
Явный \strlen($s) внутри пространства имён убирает поиск в текущем namespace. На горячем цикле разница есть, хотя для большинства приложений она незаметна. Если пишете хайлоад — измеряйте на своём коде, а не переписывайте всё подряд.
Ошибка 4. use не переименовывает вызовы в строках и внутри других namespace
use действует только в пределах одного файла и только на код, а не на строковые литералы. Типичный промах:
<?php
namespace App\Http;
use App\Service\Mailer;
// не сработает — строка не разрешается через use
$class = 'Mailer';
$obj = new $class(); // ищет App\Http\Mailer
Динамическое создание по строке требует полного имени с ведущим слешем: $class = \App\Service\Mailer::class; — конструкция ::class подставит полностью разрешённое имя с учётом use, и это надёжнее ручной строки.
use в глобальном пространстве
Если файл не объявляет свой namespace, он находится в глобальном пространстве. use App\Service\Mailer; здесь работает, но new Mailer() без use будет искать \Mailer, а не класс из App\Service. В файлах без namespace всегда обращайтесь к классам по полному имени или через явный импорт.
Ошибка 5. Несколько классов в одном файле и группировка
PSR-4 требует один класс на файл. Если положить два класса в Mailer.php, автолоадер найдёт только тот, чьё имя совпадает с именем файла. Второй класс не загрузится, пока не подключится первый. Это порождает баги, зависящие от порядка обращения к классам.
Групповой импорт с PHP 7 и выше уменьшает шум, но не отменяет правил разрешения:
<?php
namespace App\Http;
use App\Service\{Mailer, Report, Logger};
use function App\Helper\{format_date, slugify}; // импорт функций
use const App\Config\{DEBUG, VERSION}; // импорт констант
Обратите внимание: функции и константы импортируются отдельными формами use function и use const. Обычный use импортирует только классы, интерфейсы и трейты. Попытка импортировать функцию обычным use не даст ошибки на этапе компиляции, но и не сработает при вызове.
Сводка: где что ломается
| Симптом | Частая причина | Что проверить |
|---|---|---|
| Class not found, файл существует | Путь или регистр не совпадают с namespace | Соответствие PSR-4, регистр букв, composer dump-autoload |
| Namespace declaration has to be the very first statement | Вывод или код до namespace, BOM | Кодировка без BOM, убрать закрывающий ?> |
| Class "App\...\DateTime" not found | Забыт ведущий слеш для встроенного класса | Добавить \ или use DateTime; |
| new $class() ищет не тот класс | Строка не разрешается через use | Полное имя или ::class |
| Импортированная функция не работает | Использован use вместо use function | Заменить на use function |
Что делать дальше
Включите строгую типизацию и статический анализ — PHPStan или Psalm ловят большинство ошибок с именами классов до запуска, включая несуществующие импорты и неправильный регистр. Проверьте секцию autoload в composer.json: держите один префикс на одну директорию и не смешивайте PSR-4 с ручными files без необходимости. И приучите CI гонять composer dump-autoload --optimize при сборке — на проде оптимизированная карта классов убирает лишние обращения к файловой системе на каждый autoload.
Проектная работа: От проблемы до защиты: продукт, этапы, вопросы комиссии
Спросить за 15 ₽Источники: PHP Manual: Namespaces, PSR-4: Autoloader (PHP-FIG)
Частые вопросы
У меня всё работает локально, но на сервере Class not found. Почему?
Помогает ли composer dump-autoload, если класс не находится?
Нужен ли ведущий слеш перед встроенными функциями вроде strlen внутри namespace?
Отличается ли поведение namespace между PHP 7 и PHP 8?
Почему use не работает для класса, имя которого лежит в строке?
Можно ли держать несколько классов в одном файле при PSR-4?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.