A free and open-source book on ZF3 for beginners


3.4. Автозагрузка классов в PHP

Веб-приложение состоит из множества PHP-классов, и каждый класс, как правило, располагается в отдельном файле. Это приводит к необходимости включения (including) файлов.

Предположим, например, что у нас есть файл с именем Application.php, который содержит определение класса \Zend\Mvc\Application из предыдущего раздела. Перед тем как создать экземпляр этого класса где-либо в своем коде, вы должны включить содержимое файла Application.php (это можно сделать с помощью оператора require_once, передав ему полный путь к файлу):

<?php
require_once "/path/to/zendframework/zend-mvc/src/Application.php";

use Zend\Mvc\Application;

$application = new Application();

С увеличением размера вашего приложения включение каждого необходимого файла может вызывать трудности. Сам Zend Framework 3 состоит из сотен файлов, и загружать целую библиотеку и все ее зависимости таким образом довольно сложно. Более того, при запуске кода интерпретатору PHP потребуется процессорное время для обработки каждого включенного файла, даже если вы не создавали экземпляр его класса.

Для решения этой проблемы в PHP была введена опция автозагрузки классов. Функция spl_autoload_register() позволяет вам зарегистрировать функцию автозагрузки. Для сложных сайтов можно даже создать несколько функций автозагрузки, объединенные в стек.

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

3.4.1. Автозагрузчик карты классов

Чтобы дать вам представление о том, как выглядит функция автозагрузки, ниже мы привели ее упрощенную реализацию:

<?php
// Функция автозагрузки.
function autoloadFunc($className) 
{
    // Статический массив карты классов.
    static $classMap = [
        '\\Zend\\Mvc\\Application' => '/path/to/zendframework/zend-mvc/src/Zend/Mvc/Application.php',
        '\\Application\\Module' => '/path/to/app/dir/module/Application/Module.php',
        //...
    ];

    // Проверяем, находится ли такое имя класса в карте классов.
    if(isset(static::$classMap[$className])) {
        $fileName = static::$classMap[$className];
  
        // Проверяем, существует ли файл и возможно ли его прочитать.
        if (is_readable($fileName)) {
            // Подлючаем файл.
            require $fileName;
        }
    }
}

// Регистрируем нашу функцию автозагрузки.
spl_autoload_register("autoloadFunc");

В примере выше мы определяем функцию автозагрузки autoloadFunc(), которую в дальнейшем будем именовать автозагрузчиком карты классов (class map autoloader).

Автозагрузчик карты классов использует карту для определения соответствия между именем класса и абсолютным путем к PHP-файлу, содержащему этот класс. Карта классов - это всего лишь обычный PHP-массив, содержащий ключи и значения. Чтобы определить путь к файлу по имени класса, автозагрузчику просто нужно взять значение из массива карты классов. Очевидно, что автозагрузчик работает очень быстро. Однако, его минус в том, что вам необходимо поддерживать карту классов и обновлять ее каждый раз при добавлении в программу нового класса.

3.4.2. Стандарт PSR-4

Так как каждый поставщик библиотек использует свои собственные правила наименования и организацию файлов, вам придется регистрировать свою функцию автозагрузки для каждой зависимой библиотеки, что, конечно, может надоедать (и вообще, это ненужный труд). Чтобы решить эту проблему, был введен стандарт PSR-4.

PSR расшифровывается PHP Standards Recommendation (рекомендации стандартов PHP).

Стандарт PSR-4 определяет рекомендованную структуру кода, которую приложение или библиотека должны соблюдать, чтобы обеспечить совместимость механизмов автозагрузки.

Например, для класса Zend\Mvc\Application, у вас будет следующая структура каталогов:

/path/to/zendframework/zend-mvc/src
  /Zend
    /Mvc
      Application.php

Недостатком данной структуры является то, что вам придется размещать свои классы внутри ряда вложенных пустых директорий (Zend и Mvc).

Чтобы исправить это, PSR-4 позволяет сопоставить последовательности одного или нескольких пространств и подпространств имен "базовый каталог". Например, если у вас есть полностью определенное имя класса \Zend\Mvc\Application и вы определяете соответствие между \Zend\Mvc и каталогом "/path/to/zendframework/zend-mvc/src/", вы можете организовать файлы следующим образом:

/path/to/zendframework/zend-mvc/src
  Application.php

Для соответствующего стандарту PSR-4 кода, мы можем написать и зарегистрировать автозагрузчик, который будем именовать "стандартным" автозагрузчиком:

<?php

// "Стандартный" автозагрузчик.
function standardAutoloadFunc($className) 
{
    // Заменить префикс пространства имен на базовую директорию.
    $prefix = '\\Zend\\Mvc';
    $baseDir = '/path/to/zendframework/zend-mvc/src/';
    if (substr($className, 0, strlen($prefix)) == $prefix) {
        $className = substr($className, strlen($prefix));
        $className = $baseDir . $className;
    }

    // Заменить разделители пространства имен на разделители директорий.
    $className = str_replace('\\', DIRECTORY_SEPARATOR, $className);
  
    // Добавить расширение .php.
    $fileName = $className . ".php";
  
    // Проверить, что файл существует и его можно прочесть.
    if (is_readable($fileName)) {
        // Включить файл.
        require $fileName;
    } 
}

// Register the autoloader function.
spl_autoload_register("standardAutoloadFunc");

Стандартный автозагрузчик работает следующим образом: Предполагая, что пространство имен класса имеет соответствие один к одному со структурой каталогов, функция вычисляет путь к PHP-файлу, преобразовывая обратные слеши (разделители пространств имен) в прямые (разделители пути) и соединяя полученный путь с абсолютным путем к каталогу, где расположена библиотека. Затем функция проверяет, существует ли уже такой PHP-файл, и, если он существует, включает его с помощью оператора require.

Очевидно, что стандартный автозагрузчик работает медленнее, чем автозагрузчик карты классов. Однако, его преимущество в том, что вам не нужно поддерживать карту классов, что очень удобно при написании нового кода и добавлении новых классов к вашему приложению.

Zend Framework 3 работает в соответствии со стандартом PSR-4, что делает возможным автозагрузку всех его компонентов. Он также совместим с другими библиотеками, следующими PSR-4, такими как Doctrine и Symfony.

3.4.3. Автозагрузчик, предоставляемый Composer'ом

Composer может генерировать автозагрузчики (как автозагрузчик карты классов, так и стандартный PSR-4 автозагрузчик) для кода пакетов, которые он устанавливает. Zend Framework 3 использует реализацию механизмов автозагрузки, предоставляемую Composer'ом. При установке пакетов Composer автоматически создает файл APP_DIR/vendor/autoload.php, использующий функцию spl_autoload_register(), чтобы зарегистрировать автозагрузчик. Таким образом, все PHP-классы, находящиеся в каталоге APP_DIR/vendor , корректно загружаются автоматически.

Для автозагрузки PHP-классов, расположенных в ваших собственных модулях (например, в модуле Application), вам нужно будет указать ключ autoload в файле composer.json:

"autoload": {
    "psr-4": {
        "Application\\": "module/Application/src/"
    }
},

Затем все, что нужно будет сделать, это включить этот файл во входном скрипте index.php вашего веб-сайта:

// Автозагрузка Composer'a
include __DIR__ . '/../vendor/autoload.php';

Файл autoload.php генерируется каждый раз как вы устанавливаете пакет с помощью Composer. Помимо этого, чтобы заставить Composer сгенерировать файл autoload.php, вы можете выполнить команду dump-autoload:

php composer.phar dump-autoload

3.4.4. PSR-4 и структура исходных каталогов

В Zend Skeleton Application вы можете наблюдать стандарт PSR-4 на практике. Для стандартного модуля вашего сайта, модуля Application, PHP-классы, зарегистрированные стандартным автозагрузчиком, хранятся в каталоге APP_DIR/module/Application/src ("src" - сокращение от "source" - "источник").

Мы будем называть каталог src исходным каталогом модуля.

Для примера рассмотрим файл IndexController.php модуля Application (рисунок 3.2).

Рисунок 3.2. Структура каталогов скелетного приложения соответствует стандарту PSR-4 Рисунок 3.2. Структура каталогов скелетного приложения соответствует стандарту PSR-4

Как видите, он содержит класс IndexController 1, принадлежащий пространству имен Application\Controller. Чтобы соблюдать стандарт PSR-4 и использовать стандартный автозагрузчик с этим классом, нужно поместить его в директорию Controller в исходном каталоге модуля.

1) Класс IndexController - это стандартный контроллер для скелетного сайта. Мы будем говорить о контроллерах в главе Модель-представление-контроллер.


Top