Об автозагрузке в PHP

При написании приложений, код размещают в отдельных файлах. Чтобы код одного файла использовать в другом, его необходимо подключить через функцию require_once или include_once

Времена до автозагрузки

До PHP 5.0 (2005 г.) использовались подключение файлов через многократные вызовы фукнций require_once

require_once __DIR__ . '/A.php';
require_once __DIR__ . '/B.php';
require_once __DIR__ . '/C.php';

В определенный момент придумали механизм автоматического подключения классов при первом обращении - автозагрузку. Первой реализацией был метод __autoload

Магический метод __autoload

Данный метод объявлен УСТАРЕВШИМ начиная с PHP 7.2.0 и его использование крайне не рекомендовано.

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

function __autoload($classname) {
    $filename = "./". $classname .".php";
    require_once($filename);
}    
$a = new A();
$b = B::methodG();
$c = class_exists('C');

Уже при этом подходе здравый смысл подсказывал, что в одном файле должен находится один класс, а название класса должно совпадать с названием файла.

Стандартная функция __autoload() имеет ряд существенных недостатков:

  • нет возможности регистрации нескольких автозагрузчиков
  • нет возможности динамически активировать/деактивировать автозагрузчики

В PHP 5.1.2 (2006 г.) эти проблемы решили через фукнции автозагрузчики.

Автозагрузка через spl_autoload_register

PHP позволяет зарегистрировать любое число функций-автозагрузчиков с помощью функции spl_autoload_register. В случае обращения к несуществующему в данный момент классу, PHP будет вызывать по очереди все зарегистрированные автозагрузчики, передавая им имя класса. Если автозагрузчик знает, где лежит этот класс, он должен подключить файл с ним, PHP увидит, что класс появился, и продолжит выполнение программы. Иначе PHP вызовет следующий автозагрузчик. Если ни один автозагрузчик не подключит файл с классом, то будет выведена ошибка об обращении к несуществующему классу.

function libraryOne($classname) {
    $filename = "./path/one/". $classname .".php";
    require_once($filename);
}    

function libraryTwo($classname) {
    $filename = "../../path/two/". $classname .".php";
    require_once($filename);
}    

// регистрация
spl_autoload_register('libraryOne');
spl_autoload_register('libraryTwo');

...

// деактивация первой библиотеки
spl_autoload_unregister('libraryOne');

Если подключается сторонняя библиотека, то она может зарегистрировать свой автозагрузчик для загрузки своих классов. Таким образом, каждая библиотека может устанавливать свои правила для поиска файлов со своими классами.

Есть fallback для __autoload:

spl_autoload_register("__autoload");

Автозагрузка через composer и PSR-4

Дальше - лучше

Развитием идеи в одном файле один класс стал стандарт PSR-0 (англ.) (устаревший) и его развитие PSR-4 (англ.). PSR-4 говорит как надо называть классы, чтобы по полному имени класса узнать путь к файлу (чтобы потом его подключил автозагрузчик).

\<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>

Например класс Library\A\B\C следует помещать в файл Library/A/B/C.php. Надо понимать, что это лишь рекомендация, PHP не требует, чтобы имя класса совпадало с именем файла, а неймспейсы с именами папок. Но следование PSR-4 освобождает от написания своего автозагрузчика, можно взять готовый.

Пример пакета foo-bar по PSR-4

# namespace Foo\Bar
/path/to/packages/foo-bar/
    src/
        Baz.php             # Foo\Bar\Baz
        Qux/
            Quux.php        # Foo\Bar\Qux\Quux
    tests/
        BazTest.php         # Foo\Bar\BazTest
        Qux/
            QuuxTest.php    # Foo\Bar\Qux\QuuxTest

На сегодняшний день большинство PHP разработчиков пользуются загручиком из менеджера зависимостей Сomposer. Composer не является только лишь генератором автозагрузчика. Задачи, которые он выполняет намного шире.

Генерация и использование автозагрузчика из composer

В конфигурационном файле composer.json в разделе autoload необходимо указать корневой namespace проекта и корневую папку. Неймспейсы должны оканчиваться \\, чтобы избежать конфликтов. Например просто Foo будет соответствовать классам в пространстве FooBar, а вот Foo\\ и FooBar\\ уже различны.

Пример раздела в файле composer.json говорящий, что классы из namespace'a Foo находятся в папке src, а классы из App\Plugins надо искать в plugins/.

{
    "autoload": {
        "psr-4": {
            "Foo\\ ": "src/",
            "App\\Plugins\\": "plugins/"
        }
    }
}

Соответственно классы будут искаться так:

Foo\Baz -> src/Baz.php
Foo\Bar\Baz -> src/Bar/Baz.php
Foo\Bar\BazTest -> src/Bar/BazTest.php
App\Plugins\Some\Class -> plugins/Some/Class.php

После добавления информации в composer.json, надо выполнить команду гененрирации загрузчика

php composer.phar dump-autoload

или при установленном composer

composer dump-autoload

Команда сгенерирует файлы автозагрузчика и положит их в папку vendor. После этого надо подключить автозагрузчик в свои файлы и наслаждаться программированием.

require_once __DIR__ . '/vendor/autoload.php';

Таким образом, следование PSR-4 и использование composer позволяет не писать свой автозагрузчик.


Почитать:

Похожие записи

Кратко о внедрение зависимостей и сервис контейнере

Cтатья о том, что такое "Внедрение зависимостей" и "Сервис-контейнер" отталкиваясь от их реализации в PHP фреймворках. Статья написана по мотивам статей Фабьена Потенсье, ведущиго разработчика и идеолога фреймворка Symfony, а также документации фреймворка Laravel.

phpDocumentor на Ubuntu

Запись об установке и использовании phpDocumentor — системе документирования исходных текстов на PHP.