Пространства имён и use
В прошлом уроке файлы стали книгами на полках. Чем больше книг, тем чаще совпадают названия: в библиотеке найдутся пять книг «Основы». Их различает полка: «Основы» с полки «Химия» и «Основы» с полки «Музыка» — разные книги.
С классами то же самое. Ваш проект объявляет класс User, и пакет, который вы скачали, тоже объявляет User. Два класса с одним именем PHP не примет: на втором объявлении скрипт остановится с фатальной ошибкой, потому что имя уже занято.
Поэтому у классов есть полки. Пространство имён — это приставка, которую PHP добавляет к имени каждого класса, объявленного в файле.
Полное имя класса
Пространство имён объявляют первой инструкцией после <?php: слово namespace и имя полки. Части имени разделяют обратной косой чертой \:
<?php
namespace App\Models;
class User
{
public function __construct(public string $name) {}
}
$u = new User('Anna');
echo User::class, "\n"; // App\Models\User
echo get_class($u), "\n"; // App\Models\UserДавайте разберём по строкам:
namespace App\Models;ставит приставкуApp\Models\на всё, что объявлено в этом файле.- Поэтому
class Userобъявляет класс с полным именемApp\Models\User. - Внутри того же пространства к классу можно обращаться коротко:
new User('Anna'). User::classиget_class($u)показывают настоящее имя класса — полное.
Короткое имя User — только удобство для записи. PHP везде работает с полным именем, поэтому App\Models\User и Shop\Auth\User — два разных класса, и они друг другу не мешают.
Имена пространств пишут в PascalCase. Первая часть — обычно название проекта или компании: App, Monolog, Symfony.
use: короткое имя
Из другого пространства к классу обращаются по полному имени: new \App\Models\User('Anna'). Обратная косая черта в начале значит «искать от корня, а не на текущей полке». Писать полное имя в каждой строке долго, поэтому в начале файла ставят use — закладку с коротким именем:
<?php
namespace App\Http;
use App\Models\User;
use App\Models\Order as Purchase;
echo User::class, "\n"; // App\Models\User
echo Purchase::class, "\n"; // App\Models\OrderЧто здесь происходит:
use App\Models\User;говорит, что в этом файлеUserзначитApp\Models\User;- слово
asдаёт закладке другое имя:PurchaseзначитApp\Models\Order; - закладки действуют только в своём файле, и в другом файле их нужно поставить заново.
Имя через as выбирают, когда два класса из разных пространств называются одинаково: use Shop\Auth\User as AuthUser;.
Здесь картинка расходится с PHP. Настоящую закладку кладут в книгу, которая стоит на полке. А use не подключает файл и не проверяет, есть ли такой класс.
В этой программе классов User и Order нет вовсе, а имена всё равно напечатались. Строка new User() в ней закончилась бы ошибкой Class "App\Models\User" not found.
С функциями и константами иначе: если в текущем пространстве их нет, PHP берёт глобальные. Поэтому strlen, count и PHP_INT_MAX работают внутри namespace App без всякого use.
Встроенные классы: \Exception
Встроенные классы PHP — Exception, RuntimeException, InvalidArgumentException — лежат в глобальном пространстве, в корне. Внутри namespace App короткое имя Exception значит App\Exception: PHP ищет класс на текущей полке и в корень не заглядывает.
Особенно коварно это в catch. PHP не проверяет, существует ли класс из catch, а только сравнивает с ним брошенный объект. Блок с несуществующим классом не срабатывает, и никакой ошибки нет:
<?php
namespace App;
try {
throw new \InvalidArgumentException('empty name');
} catch (InvalidArgumentException $e) {
echo 'first', "\n";
} catch (\InvalidArgumentException $e) {
echo 'second: ', $e->getMessage(), "\n"; // second: empty name
}Разберём по шагам:
throwбросает объект глобального класса\InvalidArgumentException.- Первый
catchждётApp\InvalidArgumentException. Такого класса нет, и блок пропускается молча. - Второй
catchназывает глобальный класс через\и ловит исключение.
Исправить первый catch можно двумя способами: написать \InvalidArgumentException или поставить после namespace строку use InvalidArgumentException;. Почему catch (Exception $e) внутри пространства имён ничего не ловит — частый вопрос на собеседовании.
Частая ошибка
Встроенный класс создали без обратной косой черты:
<?php
namespace App;
throw new Exception('empty name'); // ✗Fatal error: Uncaught Error: Class "App\Exception" not foundВ отличие от catch, new требует, чтобы класс существовал. PHP ищет App\Exception, не находит его и бросает Error. Исправление — throw new \Exception('empty name'); или строка use Exception; после namespace.
Главное
namespace App\Models;добавляет приставку к каждому классу файла: полное имя класса —App\Models\User.use App\Models\User;— закладка с коротким именем на один файл; файл с классом она не подключает.- Внутри пространства имён имя класса без
\ищется на текущей полке, поэтому встроенные классы пишут как\Exceptionили подключают черезuse. catchс несуществующим классом молча не срабатывает, аnewс таким классом бросаетError.