Блог о программировании

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

Категория: PHP
 5 июня 2016 г. 23:10

PHP-программисту, использующему ООП, довольно часто требуется так организовать классы, чтобы определение каждого из них находилось в отдельном файле. В таком случае программист нередко сталкивается с довольно нудной задачей: подключением используемых классов, которые используются в проекте. Чтобы автоматизировать данный процесс, в PHP 5.0 была предусмотрена возможность автозагрузки классов.


__autoload()

Начиная с версии PHP 5.0 была введена функция __autoload(). Способ работы ее совершенно прост: когда скрипт в коде натыкается на неизвестный ему класс, он вызывает функцию __autoload(), при условии, что она была определена, и передает ей название неизвестного класса в надежде, что эта функция подключит файл с определением данного класса. Таким образом, в функции __autoload() следует указать каким именно способом будет происходить поиск требуемых к подключению классов.


Пример:

Допустим, у нас есть несколько классов проекта в папке classes. Их названия и содержания таковы:

classes/MyClass1.php:

class MyClass1 {
    public function __construct() {
        echo "<p>executed MyClass1::__construct() method</p>";
    }
}

classes/MyClass2.php:

class MyClass2 {
    public function __construct() {
        echo "<p>executed MyClass2::__construct() method</p>";
    }
}

classes/MyClass3.php:

class MyClass3 {
    public function __construct() {
        echo "<p>executed MyClass3::__construct() method</p>";
    }
}

В головном скрипте нам нужно организовать их автоматическую загрузку. Для этого в головном скрипте мы объявляем функцию автозагрузки которая ищет подключаемые классы в папке classes. После чего можно использовать эти классы:

index.php:

function __autoload($aClassName) {
    $aClassFilePath = $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . 'classes' . DIRECTORY_SEPARATOR . $aClassName . '.php';
    if (file_exists($aClassFilePath)) {
        echo "<p>executing __aturoload() with aClassName = {$aClassName}</p>";
        require_once $aClassFilePath;
        return true;
    }
    return false;
}

$obj1 = new MyClass1();
$obj2 = new MyClass2();
$obj3 = new MyClass3();

В результате на странице мы увидим такую картину:

executing __aturoload() with aClassName = MyClass1
executed MyClass1::__construct() method

executing __aturoload() with aClassName = MyClass2
executed MyClass2::__construct() method

executing __aturoload() with aClassName = MyClass3
executed MyClass3::__construct() method

Как видно из примера, у нас объявлено несколько классов в отдельных файлах, а в главном скрипте нет ни одного вызова инструкций include/require. Причем, автозагрузка классов происходит по мере необходимости.

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


Использование автозагрузки из библиотеки SPL

Начиная с версии PHP 5.1.2 была внедрена новая система автозагрузки классов, которая была включена в библиотеку SPL(Standard PHP Library). Основное отличие новой системы автозагрузки в том, что можно определять сколько угодно функций автозагрузки. Все они формируют стек функций автозагрузки, в котором каждая последующая добавленная функция автозагрузки попадает в конец стека. Таким образом, при работе скрипта, когда он натыкается на неизвестный ему класс, он поочередно вызывает все функции автозагрузки, находящиеся в стеке до тех пор, пока не будет загружен требуемый класс.

Так, новая система автозагрузки включает целых 6 функций управления автозагрузкой:

  • spl_autoload_call — принудительно загружает класс по его имени, используя все доступные в системе автозагрузчики;
  • spl_autoload_extensions — возвращает/модифицирует расширения файлов, из которых происходит загрузка неинициализированных классов;
  • spl_autoload_functions — возвращает список всех зарегистрированных автозагрузчиков в системе;
  • spl_autoload_register — регистрация собственного автозагрузчика в стеке автозагрузки;
  • spl_autoload_unregister — удаление автозагрузчика из стека автозагрузки;
  • spl_autoload — основная функция автоматической загрузки классов. Именно она вызывается при обращении к классу, который еще не инициализирован. Данная функция активирует все автоматические загрузчики из стека в порядке их добавления.

Чтобы включить способ автозагрузки по умолчанию, нужно лишь вызвать функцию spl_autoload_register() без каких-либо параметров. После этого активизируется средство автозагрузки классов, которое автоматически вызывает функцию spl_autoload() при попытке создать в программе экземпляр неизвестного класса. В качестве параметра этой функции передается имя неизвестного класса, которое впоследствии преобразуется в имя файла. Механизм таков: имя класса приводится к нижнему регистру, и к нему по очереди добавляются все стандартные расширения( сначала .inc, потом .php ), после чего производится поиск файла в той же директории, где расположен скрипт, и найдя его, попытается загрузить содержимое этого файла, полагая, что в нем находится определение неизвестного класса. Также в реализации автозагрузки классов по-умолчанию поддерживаются пространства имен, при этом имени пакета ставится в соответствие имя каталога. И все-таки в автозагрузке по-умолчанию слишком много ограничений. Поэтому практически всегда разработчики пишут свои функции автозагрузки и добавляют их в стек.

Пример:

Допустим, у нас есть два различных(classes и lib) каталога, в котором хранятся некоторые классы, и для автозагрузку классов из каждого каталога мы хотим сделать собственную функцию автозагрузки(исключительно в качестве примера).

classes/MyClass1.php:

class MyClass1 {
    public function __construct() {
        echo "<p>executed MyClass1::__construct() method</p>";
    }
}

classes/MyClass2.php:

class MyClass2 {
    public function __construct() {
        echo "<p>executed MyClass2::__construct() method</p>";
    }
}

classes/MyClass3.php:

class MyClass3 {
    public function __construct() {
        echo "<p>executed MyClass3::__construct() method</p>";
    }
}

index.php:

//Сначала убедимся, что в стеке автозагрузки не зарегистрировано ниодной функции
var_dump(spl_autoload_functions());

//функция автозагруки, загружающая классы из папки classes:
function loadFromClasses($aClassName) {
    $aClassFilePath = $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . 'classes' . DIRECTORY_SEPARATOR . $aClassName . '.php';
    if (file_exists($aClassFilePath)) {
        echo "<p>executing __aturoload() with aClassName = {$aClassName}</p>";
        require_once $aClassFilePath;
        return true;
    }
    return false;
}

//функция автозагруки, загружающая классы из папки libs:
function loadFromLibs($aClassName) {
    $aClassFilePath = $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . 'libs' . DIRECTORY_SEPARATOR . $aClassName . '.php';
    if (file_exists($aClassFilePath)) {
        echo "<p>executing __aturoload() with aClassName = {$aClassName}</p>";
        require_once $aClassFilePath;
        return true;
    }
    return false;
}

//регистрируем обе функции автозагрузки
spl_autoload_register('loadFromClasses');
spl_autoload_register('loadFromLibs');

//Проверим, что в стеке функций автозагрузки присутствуют две функции:
var_dump(spl_autoload_functions());

$object1 = new MyClass1();
$object2 = new MyClass3();

//удалим первую функцию автозагрузки
spl_autoload_unregister('loadFromClasses');

//В стеке должна остаться одна функция:
var_dump(spl_autoload_functions());

$object3 = new MyClass2(); //здесь будет ошибка

В результате на странице получим:

boolean false 
array (size=2) 0 => string 'loadFromClasses' (length=15)                    1 => string 'loadFromLibs' (length=12) 
executing __aturoload() with aClassName = MyClass1

executed MyClass1::__construct() method

executing __aturoload() with aClassName = MyClass3

executed MyClass3::__construct() method

array (size=1) 0 => string 'loadFromLibs'                                        (length=12)
Fatal error: Class 'MyClass2' not found in Z:\home\test.loc\www\index.php on line 44

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


Переход с функции __autoload() на использование SPL-автозагрузки

В языки PHP функция __autoload() является устаревшей, поэтому в будущих версиях PHP поддержка этой функции не гарантируется. С использованием данной функции в проекте связана еще одна проблема - отключается возможность использования SPL-автозагрузки. Чтобы не попасться в ситуацию, когда обнаружится, что функция __autoload() не вызывается PHP-интерпретатором, или в случае необходимости поддержки SPL-автозагрузки, можно просто вызвать функцию spl_autoload_register() с переданной в качестве аргумента строкой "__autoload", как показано ниже:

spl_autoload_register("__autoload");

Вывод

Механизм автоматической загрузки классов - это еще один крайне востребованный инструмент в арсенале PHP-разработчика. Использование этого механизма встречается повсеместно: будь то код какого-либо небольшого проекта или новомодный фреймворк.
Понимание механизма автозагрузки классов в php дает значительные преимущества освоившему ее разработчику. По большому счету задача внедрения функций автозагрузки - автоматизировать операцию повсеместного вызова инструкций require/include. Конечно, также знание этих нюансов дает более глубокое познание о работе PHP-скриптов, знакомство с php-фреймворками будет происходить полее мягко и т.д.


Дополнительно

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

Теги:  php  автозагрузка  php7 

Поделиться статьей

Оставить комментарий