Все об автозагрузке в PHP
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:hometest.locwwwindex.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.
Ее перевод доступен здесь.
Comments