Перейти к содержимому
Теория · Файлы, пространства имён и Composer · урок 4 из 7

Composer и автозагрузка PSR-4

Своих книг библиотеке всегда мало. Нужной книги нет на полках — библиотекарь заказывает её в другой библиотеке, и через пару дней она стоит у вас.

С кодом то же самое. Запись в журнал, отправка писем, работа с датами — всё это уже написано и выложено другими разработчиками в виде пакетов.

Копировать чужие файлы вручную неудобно: у пакета бывают свои зависимости, у них свои, и всё это нужно держать в совместимых версиях. Эту работу делает Composer — менеджер зависимостей PHP: программа, которая скачивает пакеты проекта вместе со всем, что им нужно.

Composer ставит пакеты

Composer запускают в терминале, в папке проекта. Laravel Herd ставит его вместе с PHP, а в Linux его устанавливают отдельно, как описано в разделе «Подготовка компьютера» на странице курса:

TEXT
$ composer init
$ composer require monolog/monolog

Первая команда задаёт несколько вопросов и создаёт файл composer.json — список того, что нужно проекту. Вторая находит пакет Monolog для записи журналов в каталоге пакетов Packagist, скачивает его и дописывает в composer.json. После неё в проекте лежат:

  • composer.json — что проекту нужно: имена пакетов и допустимые версии;
  • composer.lock — какие версии установлены на самом деле;
  • папка vendor/ — скачанные пакеты и файл vendor/autoload.php.

В vendor/ окажется не только monolog, но и psr: Monolog сам зависит от пакета psr/log, и Composer скачал его без вашей просьбы.

Папку vendor/ не правят руками и не сохраняют в git — хранилище истории кода. Собрать vendor/ заново всегда можно командой composer install. А composer.json и composer.lock в git сохраняют.

composer.lock: install и update

composer.lock хранит точные версии всего установленного — и ваших пакетов, и их зависимостей. composer install читает этот файл и ставит ровно то, что в нём записано, поэтому у коллеги и на сервере окажется тот же код. composer update заново подбирает свежие версии в пределах composer.json и переписывает lock-файл. Эту команду запускают, когда сами решили обновить пакеты.

Автозагрузка по PSR-4

Composer загружает не только чужие классы, но и ваши. Автозагрузка — это механизм, который подключает файл класса сам, в момент, когда класс понадобился. Это помощник библиотекаря: по полному имени класса он находит полку и нужную книгу.

Правило, по которому имя класса превращается в путь к файлу, задают в composer.json:

TEXT
{
    "require": {
        "monolog/monolog": "^3.12"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}

Секция autoload говорит: всё, что начинается с App\, лежит в папке src/. В JSON обратная косая черта удваивается, как в PHP-строке 'App\\', а в самом имени она одна.

Такое правило превращения имени класса в путь называется PSR-4. Для класса App\Http\Kernel оно работает так:

  1. Префикс App\ заменяется папкой src/, от имени остаётся Http\Kernel.
  2. Каждая \ становится /: получается src/Http/Kernel.
  3. В конце добавляется .php: src/Http/Kernel.php.

Значит, класс лежит в файле src/Http/Kernel.php, а сам файл объявляет пространство имён, которое совпадает с папкой:

PHP
<?php

namespace App\Http;

class Kernel
{
    public function handle(): string
    {
        return 'ok';
    }
}

После правки секции autoload запустите composer dump-autoload: Composer перестроит автозагрузчик. Точка входа, файл index.php, подключает один-единственный файл — require __DIR__ . '/vendor/autoload.php';. Дальше new App\Http\Kernel() работает без всякого require: автозагрузчик найдёт src/Http/Kernel.php сам.

PSR-4 — один из стандартов PSR, о которых договорились авторы популярных PHP-проектов; PSR-12 из того же ряда описывает оформление кода.

Что происходит в момент new

vendor/autoload.php регистрирует функцию-автозагрузчик через spl_autoload_register. Когда PHP встречает класс, которого ещё нет, он не сдаётся сразу: сначала вызывает эту функцию с полным именем класса. Функция находит и подключает файл, класс появляется — и new продолжается.

В программе ниже класс объявлен прямо внутри автозагрузчика: так весь пример умещается в одном файле, хотя Composer подключил бы класс из отдельного. Нажмите «Следующий шаг» и следите, в какой момент появляется строка load App\User.

ВживуюАвтозагрузчик и newшаг 1 из 8 · рамок: 1

Всё, что объявлено в этом файле, получает приставку App\.

Следите, когда вызывается автозагрузчик и с каким именем

Что стоит запомнить после виджета:

  • автозагрузчик вызывается в момент new, когда класса ещё нет, и получает полное имя App\User, а не User;
  • второй new User() его уже не позовёт: класс объявлен, и искать нечего;
  • строка use App\Models\User; автозагрузчик не вызывает: она только сокращает имя.

Как PHP находит классы без require — частый вопрос на собеседовании. Ответ: он вызывает зарегистрированный автозагрузчик с полным именем класса, а автозагрузчик Composer превращает имя в путь по правилу PSR-4.

Здесь картинка с библиотекарем неточна. Настоящий библиотекарь заказывает книгу, когда вы попросили, а Composer работает только до запуска программы. Пока программа выполняется, ничего не скачивается: всё уже лежит в vendor/.

Частая ошибка

Пространство имён в файле не совпадает с его папкой:

PHP
<?php

namespace App; // ✗

class Kernel
{
    public function handle(): string
    {
        return 'ok';
    }
}
TEXT
Fatal error: Uncaught Error: Class "App\Http\Kernel" not found

Файл лежит в src/Http/Kernel.php, но объявляет класс App\Kernel. По имени App\Http\Kernel автозагрузчик находит и подключает этот файл, а нужного класса в нём нет. Пространство имён должно повторять путь от src/: namespace App\Http;.

Главное

  • composer require скачивает пакет со всеми зависимостями в vendor/; папку vendor/ в git не сохраняют, а composer.json и composer.lock сохраняют.
  • composer install ставит версии из composer.lock, а composer update подбирает новые и переписывает его.
  • PSR-4 превращает App\Http\Kernel в src/Http/Kernel.php: префикс меняется на папку, \ на /, в конце .php.
  • PHP вызывает автозагрузчик с полным именем класса в момент первого new, а vendor/autoload.php подключают один раз.