Зачем PHP нужны namespace
Namespace в PHP — это часть имени класса, интерфейса, trait, enum, функции или константы. Он не создаёт отдельный модуль в смысле Composer-пакета и сам по себе не загружает файлы. Его задача проще: дать коду уникальные имена и не заставлять весь проект жить в одном глобальном списке User, Config, Logger и Helper.
После Классы, объекты и видимость и Наследование, интерфейсы и трейты это следующий слой организации: класс уже имеет контракт и поведение, а namespace отвечает на вопрос «где этот класс живёт в проекте и как его отличить от одноимённого класса из другой библиотеки?».
<?php
declare(strict_types=1);
namespace App\Billing;
final class Invoice
{
public function __construct(public readonly int $amount) {}
}Полное имя класса здесь — App\Billing\Invoice. В другом месте проекта вполне может быть App\Documents\Invoice; PHP не перепутает их, потому что это разные имена.
Объявление namespace в файле
Обычно файл начинается так: <?php, затем declare(strict_types=1);, затем namespace. Перед namespace не должно быть обычного исполняемого кода; declare — нормальное исключение. На практике это хорошо сочетается с правилом «один файл — один основной класс», которое потом подхватывает Autoloading и PSR-4.
<?php
declare(strict_types=1);
namespace App\Catalog;
final class Product
{
public function __construct(
public readonly string $sku,
public readonly string $name,
) {}
}PHP разрешает объявлять одно и то же пространство имён в нескольких файлах. Это не значит, что все классы нужно складывать в один огромный файл; наоборот, в современных проектах namespace обычно распределён по дереву директорий.
src/
Catalog/
Product.php App\Catalog\Product
ProductRepository.php
Billing/
Invoice.php App\Billing\InvoiceСвязь App\Catalog\Product → src/Catalog/Product.php не является правилом самого языка. Это соглашение автозагрузки, чаще всего PSR-4, и его настраивает Composer. Namespace даёт имя, autoloader решает, из какого файла это имя загрузить.
flowchart TD
A[Код в файле: namespace App\\Catalog] --> B{Имя в выражении}
B --> C[new Product]
B --> D[new \\App\\Billing\\Invoice]
B --> E[use App\\Model\\User; new User]
B --> F[strlen('php')]
C --> C1[App\\Catalog\\Product]
D --> D1[App\\Billing\\Invoice]
E --> E1[App\\Model\\User]
F --> F1[Сначала App\\Catalog\\strlen, затем глобальная strlen]
G[Composer PSR-4] --> H[App\\ -> src/]
H --> C1
H --> E1Полные, абсолютные и относительные имена
В PHP важно различать три формы имени.
Product — неполное имя. Внутри namespace App\Catalog оно обычно означает App\Catalog\Product, если не было импорта через use.
Billing\Invoice — квалифицированное имя без начального слеша. Внутри App\Catalog оно будет интерпретировано как App\Catalog\Billing\Invoice, если первый сегмент не импортирован.
\App\Billing\Invoice — абсолютное имя. Начальный \ говорит: «начинай от глобального пространства имён, не от текущего namespace».
<?php
declare(strict_types=1);
namespace App\Catalog;
$product = new Product('book-1', 'PHP Handbook');
// App\Catalog\Product
$invoice = new Billing\Invoice(1500);
// App\Catalog\Billing\Invoice, если Billing не импортирован
$invoice = new \App\Billing\Invoice(1500);
// App\Billing\InvoiceАбсолютные имена полезны точечно, но если они встречаются в каждой строке, код быстро становится шумным. Для обычного файла лучше импортировать зависимости через use.
use: импорт и alias
use в начале файла создаёт короткое имя для внешнего класса, интерфейса, trait, enum, функции, константы или целого namespace. Это не require и не include: use не читает файл с диска. Он только объясняет компилятору, как разворачивать имя в этом файле.
<?php
declare(strict_types=1);
namespace App\Registration;
use App\Model\User;
use App\Support\EmailNormalizer;
use DateTimeImmutable;
use RuntimeException;
use function App\Support\normalize_email;
use const App\Config\DEFAULT_LOCALE;
final class RegisterUser
{
public function __invoke(string $email): User
{
$normalized = normalize_email($email);
if ($normalized === '') {
throw new RuntimeException('Empty email');
}
return new User(
email: $normalized,
locale: DEFAULT_LOCALE,
registeredAt: new DateTimeImmutable(),
);
}
}Здесь User разворачивается в App\Model\User, DateTimeImmutable и RuntimeException — в глобальные классы PHP, normalize_email() — в импортированную функцию, а DEFAULT_LOCALE — в импортированную константу. Для функций и констант нужно явно писать use function и use const; обычный use относится к class-like именам: классам, интерфейсам, trait и enum, а также к импортам namespace.
Если два имени конфликтуют, используют alias через as.
<?php
declare(strict_types=1);
namespace App\Import;
use App\Billing\Invoice as BillingInvoice;
use App\Documents\Invoice as DocumentInvoice;
final class ImportResult
{
public function __construct(
public readonly BillingInvoice $billing,
public readonly DocumentInvoice $document,
) {}
}Alias должен помогать читать код, а не прятать происхождение класса. BillingInvoice и DocumentInvoice лучше, чем Invoice1 и Invoice2.
Три разных use, которые не надо путать
Ключевое слово use в PHP перегружено. В namespace оно импортирует имена. В классе оно подключает trait, как в Наследование, интерфейсы и трейты. В замыкании оно захватывает переменные из внешней области, что относится к Функции, замыкания и callable.
use App\Support\Slugger; // импорт имени
final class Post
{
use HasTimestamps; // подключение trait
}
$prefix = 'post';
$makeSlug = function (string $title) use ($prefix): string {
return $prefix . '-' . strtolower($title); // захват переменной
};Одинаковое слово, три разных контекста. Ошибка начинается там, где ожидают, что use App\Support\Slugger «подключит файл» или что use ($prefix) как-то связан с namespace. Нет: это разные механики языка.
Глобальные классы, функции и константы
Внутри namespace неполное имя класса не откатывается в глобальное пространство. Если написать new ArrayObject() внутри App\Catalog, PHP будет искать App\Catalog\ArrayObject, если класс не импортирован.
<?php
declare(strict_types=1);
namespace App\Catalog;
use ArrayObject;
$items = new ArrayObject(['book', 'course']);Для функций и констант поведение другое: если strlen() или INI_ALL не найдены в текущем namespace, PHP может обратиться к глобальной функции или константе. Несмотря на это, в проектном коде лучше быть последовательным: импортировать проектные функции через use function, а встроенные функции вызывать привычно, если нет риска конфликта.
<?php
declare(strict_types=1);
namespace App\Text;
function strlen(string $value): int
{
return \strlen($value) + 100;
}
echo strlen('php'); // 103: локальная функция App\Text\strlen
echo \strlen('php'); // 3: глобальная функция PHPТакой пример полезен для понимания, но в реальном коде переопределять имена встроенных функций без сильной причины не стоит: это сбивает читателя и усложняет тесты.
Практические правила для проекта
Держите namespace близким к директориям. Если Composer настроен как "App\\": "src/", то App\Billing\Invoice естественно живёт в src/Billing/Invoice.php. Это облегчает автозагрузку, поиск по проекту и работу IDE.
Импортируйте зависимости явно. Длинная строка new \Vendor\Package\Deep\Nested\Client() допустима в маленьком примере, но в классе сервиса лучше видеть список зависимостей сверху. Это тот же эффект, что у аккуратных сигнатур интерфейсов: файл сразу показывает, с чем он связан.
Не используйте wildcard-импорты — в PHP их нет для use, и это хорошо. Явные импорты делают конфликты видимыми. Групповой синтаксис допустим, но в большинстве команд читаемее обычный список по одной строке.
use App\Http\{Request, Response};
use App\Security\{CsrfToken, CurrentUser};Следите за регистром имён. Сам PHP исторически местами терпим к регистру class-like имён, но PSR-4 требует ссылаться на классы case-sensitive, а файл должен совпадать с именем класса. На macOS ошибка может не проявиться локально и всплыть на Linux-сервере.
См. также
- Autoloading и PSR-4 — как Composer связывает namespace, FQCN и путь к
.php-файлу. - Composer, Packagist и composer.json — где настраивается
autoload.psr-4. - Классы, объекты и видимость — базовая модель класса, свойств и методов.
- Наследование, интерфейсы и трейты — контракты и trait, где слово
useимеет другой смысл. - Enum в PHP — enum тоже является class-like сущностью и живёт в namespace.
- Атрибуты и Reflection — как читать полные имена классов, атрибутов и методов во время выполнения.
- PSR-1, PSR-12 и стиль кода — как оформлять
namespace,useи объявления классов в общем стиле проекта.