Александр Некрасов · Автономное руководство

PHP: полный мануал с примерами, функциями и ООП

Разделы этой страницы
  1. Пространства имён: одинаковые имена без конфликта
  2. Начало: название отдела перед названием элемента
  3. Два TITLE рядом: что именно перестаёт конфликтовать
  4. Где писать namespace и какие имена допустимы
  5. Составное имя: Catalog\Export не наследует Catalog
  6. Какие объявления получают префикс, а какие — нет
  7. Четыре формы имени: короткое, составное, полное, относительное
  8. Тот же namespace вокруг небольшой функции
  9. Несколько пространств в файле: две формы записи
  10. use и as: короткие имена двух классов Receipt
  11. Функция, константа и префикс импортируются по-разному
  12. Групповой импорт: сократить повторяющийся префикс
  13. Когда действует псевдоним: текст файла, а не условие выполнения
  14. require подключает файл, но не передаёт ему namespace и use
  15. Глобальное пространство и резервный поиск: функция найдётся, класс — нет
  16. __NAMESPACE__ и namespace\: название текущего контекста
  17. Имя из переменной: строка не проходит через таблицу use
  18. Алгоритм выбора имени: разберите запись до поиска файла
  19. Ошибки FAQ: конфликт псевдонима, неизвестная константа, регистр
  20. Импорт, подключение, автозагрузка: три разных шага
  21. Namespace в типе параметра, extends, интерфейсе и трейте
  22. Практика: страница заказа использует два независимых модуля
  23. Упражнение: подключить возвраты, не меняя доставку
  24. Откуда возникают эти ошибки в реальных проектах
  25. Когда два модуля назвали свою функцию одинаково
  26. namespace, обратный слеш и use
  27. Минимальный запуск: два title без конфликта
  28. Собрать строку отчёта внутри функции
  29. Почему use не заменяет require и автозагрузку
  30. Ошибки имён и самостоятельное изменение
  31. Код и руководство с собой
PHP 8.5.11149 глав4458 карточек справочника
Дерево руководства

Глава 57 · После функций и классов

Пространства имён: одинаковые имена без конфликта

Отделяем функции каталога от доставки, вводим use/as и наблюдаем, когда действительно загружается файл класса.

Начало: название отдела перед названием элемента

На сайте есть каталог и доставка. Оба модуля хотят назвать свой заголовок TITLE. Пространство имён добавляет к такому имени адрес отдела: Workshop\Catalog\TITLE и Workshop\Delivery\TITLE. Поэтому модули могут существовать в одной программе и не спорить за одно полное имя.

Сначала достаточно понимать вывод echo и строку в кавычках. const TITLE = 'Каталог'; задаёт именованное значение: const объявляет константу, TITLE — её имя без знака $, = связывает имя со значением, кавычки ограничивают текст, ; заканчивает инструкцию. Изменять такую константу повторным присваиванием нельзя. Подробности — в главе о константах.

ЗаписьКак её читать
namespace Workshop\Catalog;Последующие объявления относятся к Workshop\Catalog. Это инструкция языка, не вызов функции.
Workshop\CatalogДве части имени разделены обратной косой чертой. Она не означает переход в папку на диске.
echo TITLE;Прочитать здесь константу TITLE и отправить её значение в вывод.
"\n"Перевод строки в двойных кавычках; в примерах делает результат в консоли читаемым.
Как получится строка «Каталог»
  1. Файл объявляет namespace Workshop\Catalog
  2. const создаёт именованное значение Workshop\Catalog\TITLE = «Каталог»
  3. echo TITLE читает это значение в текущем пространстве
  4. В вывод попадает текст «Каталог», без namespace и PHP-кода
Один отдел и одно значениеСамостоятельный пример
<?php
namespace Workshop\Catalog;

const TITLE = 'Каталог';
echo TITLE, "\n";
Ожидаемый результат
Каталог

В программе создана константа с полным именем Workshop\Catalog\TITLE. Длинное имя организует PHP-код; посетителю сайта оно не показывается.

Два TITLE рядом: что именно перестаёт конфликтовать

В следующем примере фигурные скобки после namespace ограничивают его часть файла. Пустое имя в namespace { ... } обозначает глобальную часть — пространство без префикса. Начальная \ в обращении означает: взять полное имя от корня, не дописывать текущий namespace.

Каталог и доставка сохраняют собственные заголовкиСамостоятельный пример
<?php
namespace Workshop\Catalog {
    const TITLE = 'Каталог: товары';
}
namespace Workshop\Delivery {
    const TITLE = 'Доставка: пункты выдачи';
}
namespace {
    echo \Workshop\Catalog\TITLE, "\n";
    echo \Workshop\Delivery\TITLE, "\n";
}
Ожидаемый результат
Каталог: товары
Доставка: пункты выдачи

Здесь два разных полных имени, хотя последние части TITLE совпадают. namespace не позволяет дважды объявить одну и ту же константу под одним полным именем без последствий. На реальном сайте эти модули обычно лежат в отдельных файлах; один файл здесь нужен, чтобы пример можно было запустить сразу.

До namespace похожую задачу решают длинными именами вроде workshop_catalog_title. Это допустимо. С ростом библиотек и классов namespace даёт единое правило адресации, а use — короткие записи в конкретном файле. Сам алгоритм расчёта стоимости от этого быстрее не становится.

Где писать namespace и какие имена допустимы

Первое объявление namespace должно предшествовать исполняемым инструкциям файла. Перед ним допускается declare; комментарии и пробелы внутри открытого PHP-блока не мешают. Текст, пробел или UTF-8 BOM до <?php — уже данные вне PHP. Из-за них файл с namespace не пройдёт разбор.

ПравилоПрактическое следствие
Объявление без начальной \namespace Workshop\Catalog;; начальную черту используют в обращениях, не в объявлении.
Части — допустимые именаВыбирайте латинскую букву/подчёркивание в начале, затем буквы, цифры, подчёркивания. Пробел и дефис не подходят.
Не используйте зарезервированные именаНе стройте API вокруг ключевых слов. Пространство PHP и его подпространства зарезервированы для самого языка.
Форма namespace Name;Оно действует до следующего namespace или конца файла, в том числе если PHP-теги закрывались и снова открывались.
Только PHP-код в файле библиотекиОбычно опускают конечный ?>, чтобы случайный пробел после него не стал выводом.
Комментарий перед namespace допустимСамостоятельный пример
<?php
// Заголовок модуля. Пробелы находятся внутри PHP.

namespace Workshop\Welcome;

echo "Модуль загружен\n";
Ожидаемый результат
Модуль загружен

Перенос строки после открывающего тега — часть PHP-исходника. Проблема возникает с данными до тега, а не с обычным форматированием кода.

Неверно: вывод до первого namespaceФрагмент или команда
<?php
echo 'Сначала вывод';
namespace Workshop\Catalog;

Этот намеренно неверный файл не надо запускать как рабочее решение. На этапе разбора PHP сообщит: Namespace declaration statement has to be the very first statement or after any declare call in the script. Переставьте namespace до echo; проверьте также BOM и данные до открывающего тега.

declare(strict_types=1); можно поставить после открывающего тега перед namespace: директива задаёт правила типов для файла, а не создаёт namespace. Она разобрана в правилах типов. Сами namespaces существуют с PHP 5.3; все рабочие примеры этой главы рассчитаны на PHP 8.2+.

Составное имя: Catalog\Export не наследует Catalog

Допустим, в каталоге появился экспорт. Имя Workshop\Catalog\Export удобно человеку, но PHP не поднимается автоматически к Workshop\Catalog, если чего-то не нашёл. Каждое объявление namespace задаёт полный префикс; отдельное namespace Export; не дописывает его к предыдущему.

Экспорт читает подпись родительского модуля явноСамостоятельный пример
<?php
namespace Workshop\Catalog {
    const TITLE = 'Каталог';
}
namespace Workshop\Catalog\Export {
    const TITLE = 'CSV';
    echo TITLE, "\n";
    echo \Workshop\Catalog\TITLE, "\n";
}
Ожидаемый результат
CSV
Каталог

Первая строка взята из Workshop\Catalog\Export\TITLE. Вторая обращается к другому полному имени. Никакого наследования значения TITLE здесь нет.

Нельзя вложить один блок namespace {...} внутрь другого. Записывайте длинное имя одним объявлением. Кроме того, две части namespace не требуют двух папок на диске: это соглашение может задать Composer/PSR-4, а не сам синтаксис namespace.

Какие объявления получают префикс, а какие — нет

Namespace участвует в именах классов, интерфейсов, трейтов, перечислений, функций и констант уровня пространства. Методы и свойства принадлежат своему классу. Переменные $title не становятся Workshop\Catalog\$title.

Блок namespace не изолирует переменнуюСамостоятельный пример
<?php
namespace Workshop\Catalog {
    $title = 'Товары';
}
namespace Workshop\Delivery {
    echo $title, "\n";
    $title = 'Получение';
}
namespace Workshop\Catalog {
    echo $title, "\n";
}
Ожидаемый результат
Товары
Получение

Все присваивания находятся в одной внешней области выполнения файла. Возврат к имени Catalog не восстанавливает старую переменную. Локальные переменные функции изолируются функцией, а не namespace.

Для состояния товара используйте свойства объекта; для входных данных операции — параметры функции. Сравнение namespace с областями видимости переменных помогает избежать ложного ощущения изоляции.

Четыре формы имени: короткое, составное, полное, относительное

Пока в файле нет импортов use, мысленно подставляйте текущий namespace перед составным именем без начальной черты. Слово namespace\ означает «именно из текущего пространства». Формы ниже используются для констант, функций и классов; резервный поиск коротких функций и констант разберём отдельно.

Запись внутри Workshop\CatalogК какому имени обращается
TITLEКороткое: прежде всего Workshop\Catalog\TITLE.
Export\TITLEСоставное: Workshop\Catalog\Export\TITLE, если Export не импортирован как псевдоним.
\Workshop\Catalog\TITLEПолное: точно Workshop\Catalog\TITLE.
namespace\TITLEОтносительное: точно Workshop\Catalog\TITLE, без резервного глобального поиска.
Один модуль и его подразделСамостоятельный пример
<?php
namespace Workshop\Catalog\Export {
    const TITLE = 'Выгрузка';
}
namespace Workshop\Catalog {
    const TITLE = 'Товары';
    echo TITLE, "\n";
    echo Export\TITLE, "\n";
    echo \Workshop\Catalog\TITLE, "\n";
    echo namespace\TITLE, "\n";
}
Ожидаемый результат
Товары
Выгрузка
Товары
Товары

Первые две записи короче полного адреса. Последние две явно указывают, какое имя нужно. Внутри Workshop\Catalog запись Workshop\Catalog\TITLE без начальной черты попыталась бы добавить текущий префикс ещё раз.

Тот же namespace вокруг небольшой функции

Теперь отделу нужен не готовый заголовок, а подпись конкретного заказа. function title($number) объявляет функцию с одним параметром. Её тело в {...} выполняется при вызове title(41); return отдаёт строку обратно, а точка соединяет текст и число. Эти действия разложены в начале главы о функциях. Namespace меняет полное имя функции, но не способ передачи данных.

Вызов для заказа 41
  1. \Workshop\Orders\title(41): выбрать функцию по полному имени
  2. Параметр $number получает 41
  3. Тело соединяет «Заказ №» и 41, return отдаёт строку
  4. Строка возвращается в место вызова; echo выводит «Заказ №41»
Функция получает номер, namespace различает её имяСамостоятельный пример
<?php
namespace Workshop\Orders {
    function title($number)
    {
        return 'Заказ №' . $number;
    }
}
namespace {
    echo \Workshop\Orders\title(41), "\n";
    echo \Workshop\Orders\title(52), "\n";
}
Ожидаемый результат
Заказ №41
Заказ №52

Два вызова используют одну функцию Workshop\Orders\title. namespace не сохраняет значения параметра между вызовами и не превращает функцию в метод объекта.

Несколько пространств в файле: две формы записи

Вариант с точкой с запятой можно повторять; новый namespace переключает контекст дальнейшего исходного кода. Вариант с фигурными скобками делает границы заметнее и позволяет выделить глобальный блок без имени. Эти два стиля нельзя смешивать в одном файле.

Переключение контекста после следующего namespaceСамостоятельный пример
<?php
namespace Workshop\Shop;
const TITLE = 'Магазин';
echo TITLE, "\n";

namespace Workshop\Support;
const TITLE = 'Поддержка';
echo TITLE, "\n";
echo \Workshop\Shop\TITLE, "\n";
Ожидаемый результат
Магазин
Поддержка
Магазин

При смене пространства ранее объявленная константа никуда не исчезла: её можно прочитать по полному имени.

При скобочной форме весь PHP-код файла, кроме разрешённого начального declare, должен быть внутри блоков namespace. Глобальный исполняемый код помещают в namespace { ... }. Не пытайтесь переключиться в глобальное пространство записью namespace;.

Неверно: смешаны две формы namespaceФрагмент или команда
<?php
namespace Workshop\Catalog;
const TITLE = 'Товары';
namespace Workshop\Delivery {
    const TITLE = 'Получение';
}

Намеренно неверный файл: PHP сообщает Cannot mix bracketed namespace declarations with unbracketed namespace declarations. Выберите один стиль для всего файла.

Для обычного исходника проекта проще один namespace на файл. Несколько блоков удобны в коротком учебном примере или сгенерированном файле; возможность языка сама по себе не требует так организовывать весь проект.

use и as: короткие имена двух классов Receipt

Когда один файл работает с чеком магазина и квитанцией доставки, полные имена нужны для различения, но мешают чтению каждой строки. use Workshop\Shop\Receipt as ShopReceipt; вводит в этом файле короткое имя ShopReceipt. Без as псевдонимом станет последняя часть, здесь Receipt.

Для примера класс — описание объекта с методом label(). public разрешает вызывать метод снаружи; new ShopReceipt() создаёт объект, $receipt->label() вызывает его метод. Обе реализации просто возвращают подпись. Механика объектов подробно разобрана в базовой главе ООП.

Два коротких имени с явным назначениемСамостоятельный пример
<?php
namespace Workshop\Shop {
    class Receipt {
        public function label() { return 'Чек магазина'; }
    }
}
namespace Workshop\Delivery {
    class Receipt {
        public function label() { return 'Квитанция доставки'; }
    }
}
namespace Workshop\Page {
    use Workshop\Shop\Receipt as ShopReceipt;
    use Workshop\Delivery\Receipt as DeliveryReceipt;

    $purchase = new ShopReceipt();
    $delivery = new DeliveryReceipt();
    echo $purchase->label(), "\n";
    echo $delivery->label(), "\n";
}
Ожидаемый результат
Чек магазина
Квитанция доставки

use не создаёт новый класс и не переименовывает исходное объявление. new ShopReceipt() обращается к классу Workshop\Shop\Receipt. В другом файле этот псевдоним нужно объявить заново.

Имя справа после use уже задаётся от корня; текущий Workshop\Page к нему не добавляется. Начальная \ там допустима для полного имени, но лишняя. Выбирайте понятные псевдонимы по роли, а не A и B.

Функция, константа и префикс импортируются по-разному

Есть отдельные таблицы коротких имён для классов/пространств, функций и констант. Поэтому use Workshop\Money\format; не превращает вызов format() в вызов той функции. Для неё нужен use function; для константы — use const.

ЗаписьДальнейшее использование
use Workshop\Money\Price;Короткое имя класса Price; тем же обычным use импортируют интерфейс, трейт, enum.
use Workshop\Money as Money;Префикс Money\: например Money\format(900) или Money\CURRENCY.
use function Workshop\Money\format as money;Короткий вызов money(900).
use const Workshop\Money\CURRENCY as UNIT;Чтение UNIT без $ и без скобок.
Одна функция и константа в оформлении заказаСамостоятельный пример
<?php
namespace Workshop\Money {
    const CURRENCY = 'руб.';
    function format($amount) {
        return $amount . ' ' . CURRENCY;
    }
}
namespace Workshop\Checkout {
    use function Workshop\Money\format as money;
    use const Workshop\Money\CURRENCY as UNIT;
    use Workshop\Money as Money;

    echo money(900), "\n";
    echo UNIT, "\n";
    echo Money\format(1200), "\n";
    echo Money\CURRENCY, "\n";
}
Ожидаемый результат
900 руб.
руб.
1200 руб.
руб.

Обычный use Money задаёт первый сегмент составного имени; поэтому он подходит и для Money\format, и для Money\CURRENCY. Для коротких money() и UNIT использованы специальные импорты. Пример работает с целыми рублями ради краткости; расчёт денег с копейками — отдельная задача.

use function и use const доступны с PHP 5.6. Для встроенной функции можно явно написать use function strlen; внутри namespace: это закрепит глобальную функцию за коротким вызовом strlen(). Сам импорт не добавляет определение функции и не вызывает её.

Групповой импорт: сократить повторяющийся префикс

Несколько импортов можно разделить запятыми в одной инструкции или собрать под общим префиксом в {...}. В смешанной группе перед функцией ставится function, перед константой — const; перед классом ничего не добавляют. Закрывающая скобка группы заканчивается ;. Групповой use доступен с PHP 7.0.

Класс, функция и единица измерения из одного модуляСамостоятельный пример
<?php
namespace Workshop\Packaging {
    class Box {}
    function label($count) { return 'Коробок: ' . $count; }
    const UNIT = 'шт.';
}
namespace Workshop\Shipment {
    use Workshop\Packaging\{Box, function label as boxLabel, const UNIT};

    echo Box::class, "\n";
    echo boxLabel(3), ' ', UNIT, "\n";
}
Ожидаемый результат
Workshop\Packaging\Box
Коробок: 3 шт.

Box::class возвращает строку полного имени класса; объект не создаётся. Оператор ::class здесь нужен, чтобы увидеть результат разрешения имени. Пустой класс Box — только маркер типа упаковки, не готовая модель склада.

Также допустимы группы только функций use function Workshop\Packaging\{label, total}; и только констант use const Workshop\Packaging\{UNIT, LIMIT};, если такие имена существуют при обращении к ним. Группа не означает «подключить всё пространство»: use Workshop\Packaging\*; не поддерживается. Несколько отдельных строк часто легче читать и менять.

Когда действует псевдоним: текст файла, а не условие выполнения

PHP разбирает импорты при компиляции исходника. В переменную нельзя положить имя, а затем написать use $name;. Нельзя поместить импорт namespace внутрь функции, метода или if. Размещайте импорты сразу после namespace, до мест их использования.

use влияет на последующие записи имёнСамостоятельный пример
<?php
namespace Workshop\Page;

echo Card::class, "\n";
use Workshop\Catalog\Card;
echo Card::class, "\n";
Ожидаемый результат
Workshop\Page\Card
Workshop\Catalog\Card

В обоих случаях ::class вычисляет строку имени без требования, чтобы класс существовал. Первая запись разобрана до импорта; вторая — после. Это учебная демонстрация, а не рекомендуемый порядок строк. «При компиляции» не означает «задним числом для всего файла».

Неверно: namespace-use внутри функцииФрагмент или команда
<?php
namespace Workshop\Page;
function render() {
    use Workshop\Catalog\Card;
}

Намеренно неверный синтаксис: unexpected token "use". Перенесите импорт за пределы функции, перед её объявлением. Внутри класса use может иметь другой смысл — подключение трейта; это не импорт namespace.

Повторённый namespace не сохраняет прежние импортыСамостоятельный пример
<?php
namespace Workshop\Page {
    use Workshop\Catalog\Card;
    echo Card::class, "\n";
}
namespace Workshop\Page {
    echo Card::class, "\n";
}
Ожидаемый результат
Workshop\Catalog\Card
Workshop\Page\Card

Оба блока названы Workshop\Page, однако таблица импорта второго блока начинается заново. Как и в предыдущем опыте с ::class, существование самих классов здесь не проверяется.

Импорт действует в текущем файле и блоке namespace; при новом блоке его пишут заново. У ключевого слова use ещё две роли: захват переменных замыканием и подключение трейта внутри класса. Окружение записи определяет её смысл.

require подключает файл, но не передаёт ему namespace и use

Чтобы запустить следующий пример вручную, создайте рядом с основным PHP-файлом три приложенных файла. require __DIR__ . '/vendor.php'; выполняет файл по пути: __DIR__ — папка текущего исходника, точка соединяет части строки. Namespace и импорты в каждом файле разбираются самостоятельно.

Импорт CallerCard не переезжает в подключаемый файлСамостоятельный пример
<?php
namespace Workshop\Page;
use Workshop\Vendor\Card as CallerCard;

require __DIR__ . '/vendor.php';
require __DIR__ . '/page-helper.php';
require __DIR__ . '/global-helper.php';

echo CallerCard::class, "\n";
echo helperName(), "\n";
echo \outsideName(), "\n";
Ожидаемый результат
Workshop\Vendor\Card
Workshop\Page\CallerCard
Глобальная функция

В page-helper.php то же имя namespace, но нет use CallerCard, поэтому его запись CallerCard::class даёт Workshop\Page\CallerCard. global-helper.php не объявляет namespace, поэтому outsideName — глобальная функция. Пример намеренно возвращает строку имени, не создаёт отсутствующий Workshop\Page\CallerCard.

В одном namespace можно объявлять разные элементы в разных файлах. Подключение файла повторно не должно повторно объявлять те же классы и функции; для файлов определений применяют require_once или управляют загрузкой через Composer. При этом переменные подключаемого файла подчиняются области выполнения include/require, а не этим правилам имён.

Глобальное пространство и резервный поиск: функция найдётся, класс — нет

Если короткая функция или константа не импортирована, PHP сначала проверяет текущее пространство, затем глобальное. В родительские составные пространства он не заходит. У классов такого резервного перехода к глобальному имени нет.

Локальная функция отличается от одноимённой глобальнойСамостоятельный пример
<?php
namespace {
    const SHOP_NAME = 'Магазин';
    function label() { return 'Общая подпись'; }
}
namespace Workshop\Checkout {
    function label() { return 'Оформление'; }

    echo label(), "\n";
    echo \label(), "\n";
    echo SHOP_NAME, "\n";
    echo strlen('order'), "\n";
}
Ожидаемый результат
Оформление
Общая подпись
Магазин
5

label() нашлась локально, поэтому глобальная не нужна. \label() выбрана явно. SHOP_NAME и strlen отсутствуют в Workshop\Checkout и найдены глобально. strlen здесь считает байты ASCII-строки order.

Во втором примере try запускает проверяемый код; catch (\Error $error) перехватывает ошибку языка, чтобы показать понятный результат. Это контролируемая учебная ошибка. Механизм try/catch разобран отдельно.

Встроенный DateTimeImmutable надо адресовать явноСамостоятельный пример
<?php
namespace Workshop\Calendar;
use DateTimeImmutable as Date;

try {
    new DateTimeImmutable('2026-10-01');
} catch (\Error $error) {
    echo "Нет класса Workshop\\Calendar\\DateTimeImmutable\n";
}

$date = new Date('2026-10-01', new \DateTimeZone('UTC'));
echo $date->format('Y-m-d'), "\n";
Ожидаемый результат
Нет класса Workshop\Calendar\DateTimeImmutable
2026-10-01

Короткое DateTimeImmutable без импорта означало бы класс текущего namespace. Псевдоним Date указывает на встроенный глобальный класс. DateTimeZone выбран полным именем. Часовой пояс задан явно, поэтому результат не зависит от настройки сервера.

То же правило класса применяется в extends, implements, типах параметров/результатов и catch. Например, catch (Exception $e) без импорта внутри вашего namespace не обозначает встроенный \Exception. Импортируйте его или используйте полное имя.

__NAMESPACE__ и namespace\: название текущего контекста

__NAMESPACE__ — специальная константа с именем текущего пространства без начальной обратной черты; в глобальной части это пустая строка. А namespace\ внутри имени принудительно начинает поиск от текущего namespace, минуя псевдоним первого сегмента.

Импорт Tools и местный Tools — разные адресаСамостоятельный пример
<?php
namespace Workshop\Vendor\Tools {
    const LABEL = 'Инструменты библиотеки';
}
namespace Workshop\Page\Tools {
    const LABEL = 'Инструменты страницы';
}
namespace Workshop\Page {
    use Workshop\Vendor\Tools as Tools;

    echo __NAMESPACE__, "\n";
    echo Tools\LABEL, "\n";
    echo namespace\Tools\LABEL, "\n";
}
namespace {
    echo '[', __NAMESPACE__, "]\n";
}
Ожидаемый результат
Workshop\Page
Инструменты библиотеки
Инструменты страницы
[]

Обычное Tools\LABEL заменяет Tools импортом. namespace\Tools\LABEL выбирает именно Workshop\Page\Tools\LABEL. В последней строке пустое имя окружено скобками, чтобы его было видно.

На практике __NAMESPACE__ полезен в диагностике и при сборке полного строкового имени. Он не возвращает путь к папке: для папки нужен __DIR__. В глобальном пространстве namespace\strlen означает глобальную strlen.

Имя из переменной: строка не проходит через таблицу use

Если имя класса лежит в строке, new $className() использует содержимое строки. PHP не дописывает текущий namespace и не заменяет псевдоним внутри текста. Card::class, напротив, разбирается как запись имени в исходнике и возвращает уже полное имя.

Передать полный класс через ::classСамостоятельный пример
<?php
namespace Workshop\Catalog {
    class Card {}
}
namespace Workshop\Page {
    use Workshop\Catalog\Card as Item;

    $className = Item::class;
    $card = new $className();
    echo $card::class, "\n";

    $shortName = 'Item';
    try {
        new $shortName();
    } catch (\Error $error) {
        echo "Строка Item не является псевдонимом use\n";
    }
}
Ожидаемый результат
Workshop\Catalog\Card
Строка Item не является псевдонимом use

Item::class и строка 'Item' — разные выражения с разными значениями. $card::class читает имя реального класса уже созданного объекта. Полное строковое имя допускается как с начальной обратной чертой, так и без неё.

В двойных кавычках \n означает перевод строки, а не две буквы имени. Для вручную записанного полного имени используйте корректное экранирование 'Workshop\\Catalog\\Card'; ещё надёжнее взять его через Card::class. Короткое строковое имя в class_exists() или callable также не получает импорт автоматически.

Динамически выбрать функцию и константу по полным именамСамостоятельный пример
<?php
namespace Workshop\Text {
    const LABEL = 'Заказ';
    function title($number) { return LABEL . ' №' . $number; }

    $functionName = __NAMESPACE__ . '\\title';
    $constantName = __NAMESPACE__ . '\\LABEL';
    echo $functionName(41), "\n";
    echo constant($constantName), "\n";
}
Ожидаемый результат
Заказ №41
Заказ

Вызов через $functionName получает строку Workshop\Text\title. constant() читает именованную константу по строке Workshop\Text\LABEL. В отличие от echo LABEL, содержимое строки constant() не разрешается через use const.

Не собирайте произвольный класс, путь require или callable прямо из HTTP-параметра. Если посетитель выбирает формат отчёта, сопоставьте допустимые короткие значения с заранее известными ClassName::class или функциями. Такой явный выбор обработчика ограничивает действие запроса и делает код проверяемым.

Алгоритм выбора имени: разберите запись до поиска файла

Для диагностики сначала определите полное имя, которое PHP пытается использовать. Только потом проверяйте существование определения и автозагрузку. Иначе можно долго искать верный файл для неверного имени.

ФормаПравило
Начинается с \Полное имя от корня; импорты и текущий namespace не меняют его.
Начинается с namespace\Подставляется текущий namespace; в глобальном контексте этот префикс убирается.
Есть \ внутри, нет её в началеПервый сегмент заменяется подходящим импортом класса/пространства. Это правило действует и для составных имён функций/констант.
Такого импорта первого сегмента нетК составному имени добавляется текущий namespace. Резервного поиска в глобальном пространстве нет.
Короткое имя с подходящим импортомИспользуется таблица своей категории: классы/пространства, функции или константы.
Короткий класс без импортаТекущее пространство + имя. При необходимости автозагрузчик получает именно этот полный класс.
Короткая функция/константа без импортаТекущее пространство, затем глобальное. Отсутствие обоих даёт Error.

Имена в строках относятся к отдельному случаю, описанному выше. Не применяйте к ним эту подстановку исходника. Часть решений делается при компиляции; резервный поиск короткой функции/константы относится к выполнению. Поэтому 'namespace' не равен универсальному поиску по папкам.

Ошибки FAQ: конфликт псевдонима, неизвестная константа, регистр

Импорт не может спорить с объявлением того же короткого имени класса в том же файле/контексте. Возьмите другой псевдоним. Если класс текущего namespace объявлен в другом файле, такой псевдоним технически допустим: он меняет короткую запись только здесь, а не удаляет тот класс. Но назвать их явно по роли обычно понятнее.

Неверно: объявленный класс совпал с псевдонимомФрагмент или команда
<?php
namespace Workshop\Page;
use Workshop\Catalog\Card;
class Card {}

Намеренно неверный файл: PHP не позволит объявить Card, потому что имя уже занято импортом. Например, измените импорт на as CatalogCard.

Если определение в другом файле: псевдоним меняет только короткое обращениеСамостоятельный пример
<?php
namespace Workshop\Page;
use Workshop\Catalog\Card;

require __DIR__ . '/local-card.php';
require __DIR__ . '/external-card.php';

$imported = new Card();
$local = new namespace\Card();
echo $imported->label(), "\n";
echo $local->label(), "\n";
Ожидаемый результат
Карточка каталога
Карточка страницы

Здесь допустимый, но намеренно запутывающий случай из FAQ: объявление Workshop\Page\Card находится в другом файле. Импорт заставляет короткое Card выбрать Workshop\Catalog\Card; namespace\Card явно выбирает местный класс. В рабочем коде лучше назвать импорт CatalogCard.

Неверно: блок namespace вложен в другойФрагмент или команда
<?php
namespace Workshop {
    namespace Catalog {
        const TITLE = 'Товары';
    }
}

Намеренно неверный файл: Namespace declarations cannot be nested. Правильное единое объявление: namespace Workshop\Catalog { ... }.

В PHP 8+ неизвестная константа даёт Error и при короткой, и при полной записи. Исторический пункт FAQ PHP.net о превращении голого неизвестного имени в строку описывает старое поведение до PHP 8.0; повторять его как правило для современного кода нельзя.

Обе неизвестные константы — ошибки PHP 8+Самостоятельный пример
<?php
namespace Workshop\Catalog;

try {
    echo UNKNOWN_TITLE;
} catch (\Error $error) {
    echo "Короткая константа не определена\n";
}
try {
    echo \Workshop\Catalog\UNKNOWN_TITLE;
} catch (\Error $error) {
    echo "Полная константа не определена\n";
}
Ожидаемый результат
Короткая константа не определена
Полная константа не определена

Здесь ошибку намеренно перехватываем для учебного вывода. В обычном проекте это часто опечатка или забытое подключение файла, которые надо исправить, а не подавить.

Неверно: объявление const trueФрагмент или команда
<?php
namespace Workshop\Catalog;
const true = 'Да';

Намеренно неверная попытка объявить специальное значение как константу через const; PHP отклоняет имя true. Также нельзя объявлять здесь const false или const null. Используйте своё предметное имя, например ENABLED_LABEL.

Конечная часть имени пользовательской константы чувствительна к регистру; namespace в строковом квалифицированном имени регистронезависим. С уже объявленными классами и функциями PHP обычно разрешает обращения без учёта регистра, но это не причина менять написание: автозагрузка и файловая система могут требовать точного совпадения. Сохраняйте регистр namespace, класса и имени файла согласно принятому соглашению.

Импорт, подключение, автозагрузка: три разных шага

В этом примере class_exists($name, false) проверяет, объявлен ли класс уже сейчас. Второй аргумент false отключает попытку автозагрузки при самой проверке. Тернарная запись условие ? 'Да' : 'Нет' выбирает подпись результата. Ни use, ни Card::class файл не выполняют.

Имя известно до require, класс появляется после негоСамостоятельный пример
<?php
namespace Workshop\Page;
use Workshop\Catalog\Card;

echo class_exists(Card::class, false) ? "До: есть\n" : "До: нет\n";
require __DIR__ . '/catalog-card.php';
echo class_exists(Card::class, false) ? "После: есть\n" : "После: нет\n";
$card = new Card();
echo $card->label(), "\n";
Ожидаемый результат
До: нет
После: есть
Карточка товара

Файл catalog-card.php объявляет класс; только после require его экземпляр можно создать. Удобство короткого Card здесь обеспечивает use, а доступность определения — выполненный файл.

Что происходит с new Card() при настроенной автозагрузке
  1. use связывает запись Card с Workshop\Catalog\Card
  2. new требует определение Workshop\Catalog\Card
  3. Если класс не загружен, зарегистрированный загрузчик получает полное имя и подключает подходящий файл
  4. Когда определение найдено, PHP создаёт объект; если нет — возникает Error

Сам namespace не знает, где лежит файл. Composer и PSR-4 задают соответствие префикса и папки. require 'vendor/autoload.php'; регистрирует загрузчик; это не создание всех объектов. Классовая автозагрузка не запускается от отсутствующей обычной функции или константы: их файл подключают явно или через Composer autoload.files.

Полезно сначала проверить полное имя через SomeAlias::class, затем наличие класса и подключение загрузчика, затем регистр/путь в настройках. Не добавляйте случайные require в каждый метод: это скрывает причину и усложняет зависимости.

Namespace в типе параметра, extends, интерфейсе и трейте

После знакомства с интерфейсами, трейтами и наследованием правило имени остаётся тем же. В следующем примере interface задаёт обещанный метод, trait даёт его реализацию, extends выбирает родителя, implements — интерфейс. Параметр Labelled $item разрешает передать объект, выполняющий этот договор.

Один импорт работает во всех классовых контекстахСамостоятельный пример
<?php
namespace Workshop\Contracts {
    interface Labelled {
        public function label(): string;
    }
}
namespace Workshop\Shared {
    trait LabelText {
        public function label(): string { return 'Заказ готов'; }
    }
    class BaseMessage {}
}
namespace Workshop\Orders {
    use Workshop\Contracts\Labelled;
    use Workshop\Shared\LabelText;
    use Workshop\Shared\BaseMessage;

    class ReadyMessage extends BaseMessage implements Labelled {
        use LabelText;
    }
    function render(Labelled $item): string {
        return $item->label();
    }
    echo render(new ReadyMessage()), "\n";
}
Ожидаемый результат
Заказ готов

Внешний use LabelText задаёт псевдоним полного имени трейта; use LabelText внутри класса применяет сам трейт. Это две разные операции с одним написанием ключевого слова. : string обещает строковый результат метода/функции.

Перечисление также получает namespace и импортСамостоятельный пример
<?php
namespace Workshop\Orders {
    enum Status: string {
        case Paid = 'Оплачен';
    }
}
namespace Workshop\Page {
    use Workshop\Orders\Status as OrderStatus;
    echo OrderStatus::Paid->value, "\n";
}
Ожидаемый результат
Оплачен

enum (PHP 8.1+) описывает конечный набор вариантов. ::Paid выбирает вариант, ->value читает его строковое значение. Импорт здесь адресует тип enum, а не делает импорт отдельной строковой константы. Различия enum и констант изучаются в главе о перечислениях.

Если нужны атрибуты, имя их класса разрешается по тем же правилам. Namespace не меняет public/protected/private, правила наследования или типизацию: он только помогает выбрать, какой именно тип указан.

Практика: страница заказа использует два независимых модуля

Модуль продажи знает сумму заказа; доставка знает свой статус. У обоих есть функция summary(). Страница должна вывести обе подписи. Создайте основной файл и два приложенных файла; основной файл подключит определения, назначит понятные псевдонимы и передаст каждому модулю его данные.

ДанныеКому передаютсяРезультат
41 и 1250orderSummary → Workshop\Orders\summaryЗаказ №41: 1250 руб.
41 и «В пункте выдачи»deliverySummary → Workshop\Delivery\summaryДоставка заказа №41: В пункте выдачи
Одна страница, две функции summary, независимые аргументыСамостоятельный пример
<?php
namespace Workshop\Page;
use function Workshop\Orders\summary as orderSummary;
use function Workshop\Delivery\summary as deliverySummary;

require __DIR__ . '/orders.php';
require __DIR__ . '/delivery.php';

$orderNumber = 41;
echo orderSummary($orderNumber, 1250), "\n";
echo deliverySummary($orderNumber, 'В пункте выдачи'), "\n";
Ожидаемый результат
Заказ №41: 1250 руб.
Доставка заказа №41: В пункте выдачи

Это вывод текста в консоль, без сетевого запроса и без HTML-шаблона. В HTTP-странице внешние данные надо валидировать, а текст при вставке в HTML экранировать. Namespace решает конфликт имён, но сам не проверяет стоимость и не защищает HTML.

Когда модуль доставки заменится библиотекой, вы сможете поменять её подключение и импорт, сохранив понятные имена на странице — если контракт вызова совпадает. Разницу аргументов и результата придётся согласовать явно: alias не адаптирует API. См. проверку входных данных и объектные зависимости, когда у клиента появится состояние.

Упражнение: подключить возвраты, не меняя доставку

Добавьте третий модуль Workshop\Returns со своей summary($number). На странице назовите её returnSummary и выведите «Возврат заказа №41 принят». Сначала запишите полное имя функции на бумаге. Затем определите, какой файл объявляет функцию, и только после этого добавьте короткий импорт.

Возможное решение: отдельное имя и отдельный файлСамостоятельный пример
<?php
namespace Workshop\Page;
use function Workshop\Returns\summary as returnSummary;

require __DIR__ . '/returns.php';
echo returnSummary(41), "\n";
Ожидаемый результат
Возврат заказа №41 принят

Функция summary в модуле возвратов не конфликтует с summary доставки. Подключение и имя видны в начале страницы. Если убрать require, use останется корректной инструкцией, но вызвать неопределённую функцию не получится.

Проверьте себя: почему строка 'returnSummary' не годится для динамического вызова этой функции? Почему use Workshop\Returns; не разрешает короткий summary(41)? Что изменится, если записать Returns\summary(41)? Ответы: строка не проходит импорт; обычный use задаёт префикс, а не таблицу функций; составная запись использует этот префикс.

Для небольшого скрипта с одной функцией дополнительный namespace может быть необязателен. Для повторно используемого пакета собственный префикс предотвращает случайные совпадения с приложением и другими пакетами. Стиль импортов выбирайте единый; влияние на скорость измеряйте на реальной нагрузке, а не выводите из длины имени.

Откуда возникают эти ошибки в реальных проектах

В обсуждениях разработчиков повторяются конкретные затруднения: автор считает use загрузкой, ожидает наследования контекста подключаемым файлом или передаёт строку с псевдонимом вместо полного имени. Примеры выше написаны заново для этих ситуаций; правила языка сверены с PHP Manual, а ответы участников не используются как нормативная спецификация.

Старые ответы могут описывать PHP до появления use function или до Error для неизвестных констант. Сопоставляйте дату совета с версией PHP и точной формой имени. Следующие сохранённые практические блоки этой главы закрепляют выбор имён и связь с автозагрузкой.

Когда два модуля назвали свою функцию одинаково

Каталог и доставка независимо завели функцию title. Обе нужны в одном приложении, но глобальное имя не может обозначать две разные функции. Пространство имён добавляет смысловую часть: Bridge\Catalog\title и Bridge\Delivery\title.

Результат: вы вызовете нужное объявление по полному имени, дадите ему короткий псевдоним и увидите разницу между именем и загрузкой файла. Сначала нужны функции; пример с загрузчиком также использует класс, new и свойство объекта.

Каждый пример запускается отдельно. Скачайте его архив, распакуйте целиком, откройте терминал в этом каталоге и выполните php example.php. Сравните вывод с блоком «Ожидаемый результат». Не объединяйте примеры в один файл: некоторые намеренно повторяют имена функций и классов.

namespace, обратный слеш и use

namespace Bridge\Catalog;
use Bridge\Delivery\Calculator as DeliveryCalculator;
ЗаписьСмысл
namespace Bridge\Catalog;Объявления ниже относятся к этому пространству имён. Сам каталог на диске не создаётся.
\Bridge\Catalog\title()Вызвать функцию по полному имени от корня.
use … as DeliveryCalculator;Дать чужому имени локальный псевдоним. Без as берётся его последняя часть.
use function …;Импортировать функцию; обычный use не делает такой импорт автоматически.
use const …;Импортировать константу. Имя константы по-прежнему чувствительно к регистру.

namespace должен находиться в начале PHP-кода; перед ним допустим declare. HTML, пробел до <?php или BOM могут нарушить это правило. Обычная переменная $price не получает отдельное пространство имён: переменные по-прежнему следуют правилам области видимости.

Минимальный запуск: два title без конфликта

Наглядная модель

Короткое имя совпало, полные имена различаются

У каталога и доставки есть функция title. Пространство имён показывает, какую из двух вызвать.

  • Bridge\Catalog\title()

    Возвращает строку «Каталог товаров».

  • Bridge\Delivery\title()

    Возвращает строку «Доставка заказов».

Namespace организует имена в программе. Файлы на диске подключаются отдельно: use не является загрузчиком.

Каталог и доставка могут иметь свою функцию titleСамостоятельный пример
<?php
declare(strict_types=1);

namespace Bridge\Catalog {
    function title(): string
    {
        return 'Каталог товаров';
    }
}

namespace Bridge\Delivery {
    function title(): string
    {
        return 'Доставка заказов';
    }
}

namespace {
    echo \Bridge\Catalog\title(), "\n";
    echo \Bridge\Delivery\title(), "\n";
}
Ожидаемый результат
Каталог товаров
Доставка заказов

Короткое имя title совпало, полные имена различаются. Начальный обратный слеш означает полное имя от корня. Фигурные скобки позволяют показать три пространства в одном запускаемом файле. В обычном проекте один файл чаще относится к одному namespace; не смешивайте записи со скобками и без них в одном файле.

Собрать строку отчёта внутри функции

Короткие имена класса, функции и константы в отчётеСамостоятельный пример
<?php
declare(strict_types=1);

namespace Bridge\Catalog {
    const CURRENCY = 'RUB';

    function wholeRubles(int $amount): string
    {
        if ($amount < 0) {
            throw new \InvalidArgumentException('Сумма меньше нуля');
        }
        return (string) $amount;
    }

    class Product
    {
        public function title(): string
        {
            return 'Чай';
        }
    }
}

namespace Bridge\Report {
    use Bridge\Catalog\Product as CatalogProduct;
    use function Bridge\Catalog\wholeRubles as formatAmount;
    use const Bridge\Catalog\CURRENCY;

    function itemLine(int $rubles): string
    {
        $product = new CatalogProduct();
        return $product->title() . ': ' . formatAmount($rubles) . ' ' . CURRENCY;
    }

    echo itemLine(150), "\n";
}
Ожидаемый результат
Чай: 150 RUB

itemLine вызывает импортированную функцию, создаёт объект по псевдониму класса и читает импортированную константу. Цена в этом маленьком примере задана целым числом рублей: это форматирование строки, а не универсальный расчёт денег. use действует в области файла/namespace; его не размещают внутри itemLine. Импорт не переименовывает исходное объявление.

В примере new CatalogProduct() создаёт объект класса, а $product->title() вызывает его метод. Ни namespace, ни use не запускают этот метод сами. Для глобального класса исключения написано \InvalidArgumentException: начальный слеш исключает поиск одноимённого класса внутри Bridge\Catalog.

Почему use не заменяет require и автозагрузку

В имени класса есть обратные слеши, но PHP не обязан автоматически превратить их в путь. Для одного файла можно явно написать require. Для многих классов регистрируют загрузчик. Следующий опыт печатает состояние до и после new, поэтому разница видна без догадок.

use даёт имя, загрузчик находит файл классаСамостоятельный пример
<?php
declare(strict_types=1);

use Bridge\Catalog\Product;

echo class_exists(Product::class, false) ? "Уже загружен\n" : "До new класс не загружен\n";

spl_autoload_register(static function (string $class): void {
    $files = [
        Product::class => __DIR__ . '/src/Product.php',
    ];
    if (isset($files[$class])) {
        require $files[$class];
    }
});

$product = new Product('Чай');
echo $product->title, "\n";
echo class_exists(Product::class, false) ? "После new класс загружен\n" : "Класс не найден\n";
Ожидаемый результат
До new класс не загружен
Чай
После new класс загружен

В архиве есть src/Product.php. До new инструкция use и строка Product::class не загрузили его. spl_autoload_register зарегистрировал функцию-помощник; при new PHP попросил найти ещё неизвестный класс. Помощник подключил фиксированный путь через require. Это маленькая разрешённая карта одного класса, не реализация PSR-4 и не повод собирать путь из произвольного пользовательского имени.

В настоящем проекте обычно используют Composer и его загрузчик. Правило PSR-4 связывает префикс пространства имён с каталогом; его нужно настроить, а файл vendor/autoload.php — подключить. Имена и регистр каталогов должны соответствовать правилу. Автозагрузка классов сама по себе не подгружает произвольные функции и константы по первому обращению. Для файлов с ними выбирают явное подключение или отдельное правило Composer files.

Ошибки имён и самостоятельное изменение

  • Не вставляйте use внутрь функции. use для импорта отличается от use ($variable) у замыкания и use TraitName внутри класса.
  • Не передавайте строку с коротким псевдонимом туда, где ожидается полное динамическое имя класса: строка не получает магический импорт.
  • Не делайте предположение «имя совпало с путём — PHP сам найдёт файл»: сначала должен существовать загрузчик.

Попробуйте сами. В отчёте появились два класса Product: Catalog\Product и Supplier\Product. Как различать их в одном файле?

Разбор и проверка

Импортируйте их как CatalogProduct и SupplierProduct. Создавайте каждый через нужный псевдоним; проверьте полные имена через CatalogProduct::class и SupplierProduct::class. Это решает конфликт имён, но оба файла всё равно должны подключаться явно либо загрузчиком.

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.