Автоматическая загрузка классов через стандартную библиотеку PHP

4 сентября 2015
Документация по автозагрузке: https://www.php.net/manual/ru/language.oop5.autoload.php
Что бы не было путаницы, тут и далее под термином «элемент» будут пониматься классы, трейты и интерфейсы.

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

Но когда число файлов уходит за пару десятков, то становится трудно контролировать, что бы подключались только используемые файлы. Ведь иначе оперативная память будет наполняться неиспользуемым кодом. Для длительного и объёмного проекта со множеством элементов удобно использовать подключение по факту надобности или другими словами — автоматическую загрузку. Это экономит внимание разработчика, оперативную память и процессорную нагрузку сервера.

Для создания автоматической загрузки элементов в стандартной библиотеке PHP используется функция spl_autoload_register($autoload, $throw=true, $prepend=false), которая добавляет функцию-обработчик в конец специальной очереди загрузок. Когда запрашиваемый элемент (класс, трейт, интерфейс) не найден, то начинается последовательный вызов обработчиков из очереди.

Аргументы функции spl_autoload_register($autoload, $throw=true, $prepend=false):
Документация https://www.php.net/manual/ru/function.spl-autoload-register.php
  • callable $autoload — функция-обработчик. При вызове обработчика в первый параметр передаётся название требуемого элемента. Затем по названию в теле функции можно выполнить подключение файла с элементом;
  • bool $throw — флаг срабатывания исключения, если обработчик не удалось добавить в очередь;
  • bool $prepend — флаг добавления обработчика в начало очереди;


Пример использования

Рассмотрим случай подключения в скрипт одного интерфейса и двух классов.

Интерфейс E находится в файле /module/aaa/bbb/e.php
namespace AAA\BBB;

interface E
{
	public function write_str($str);
}

Класс X находится в файле /module/aaa/bbb/x.php
namespace AAA\BBB;

class X implements E
{
	public $s = 'Тест 1';

	public function write_str($str)
	{
		echo $str;
	}
}

Класс Y находится в файле /module/aaa/bbb/y.php
namespace AAA\BBB;

class Y
{
	public static $s = 'Тест 2';
}

Вариант прямого подключения элементов:
require_once($_SERVER['DOCUMENT_ROOT'].'/module/aaa/bbb/e.php');	//интерфейс E
require_once($_SERVER['DOCUMENT_ROOT'].'/module/aaa/bbb/x.php');	//класс X
require_once($_SERVER['DOCUMENT_ROOT'].'/module/aaa/bbb/y.php');	//класс Y

$a = new \AAA\BBB\X();
$a->write_str('Привет'); //выведет 'Привет'

echo '<br>';
echo \AAA\BBB\Y::$s; //выведет 'Тест 2'

Вариант подключения элементов с помощью автоматической загрузки:
spl_autoload_register(function($e_name){

	//Выводим имя запрашиваемого элемента (класса, интерфейса, трейта)
	echo $e_name.'<br>';

	//Переводим имя в нижний регистр, т.к элементы, включая пространства имён, регистронезависимые,
	//поэтому нужно приводить имена в единый вид
	$e_name = mb_strtolower($e_name, 'utf-8');

	//символ разделения директории \ пишем как \\, т.к символ экранируется в строках PHP
	if(mb_strpos($e_name, 'aaa\\', 0, 'utf-8')===0)
	{
		$f_path = str_replace('\\', '/', $e_name);
		require_once($_SERVER['DOCUMENT_ROOT'].'/module/'.$f_path.'.php');
	}
});

//Используем псевдоним пространства имён для показа того, что при запросе элемента
//псевдоним в автозагрузчик не передаётся, а заменяется на оригинальное название пространства имён
use \AAA\BBB as DDD;

$a = new DDD\X();
$a->write_str('Привет'); //выведет 'Привет'

echo '<br>';
echo DDD\Y::$s; //выведет 'Тест 2'

В результате работы предыдущего скрипта на экран будет выведено:
AAA\BBB\X
AAA\BBB\E
Привет
AAA\BBB\Y
Тест 2