
Если вы используете у себя в проектах сотни require
или include
, то вам точно стоит прочесть эту статью.
Зачем нужна автозагрузка?
Автозагрузка (autoload) нужна для того, чтобы навсегда избавится от require, include и постоянного изменения порядка их подключения.
Рассмотрим два вариант autoload:
- composer
- spl_autoload
Autoload своих классов через composer
В файл composer.json нужно добавить директиву autoload и в нее classmap с перечнем папок, в которых нужно искать классы, интерфейсы и прочее.
01020304050607080910 | { ... "autoload" : { "classmap" : [ "folder1" , "folder2" ] } ... } |
После этого нужно обязательно обновить autoload composer’а с помощью следующей команды:
1 | composer dump-autoload |
Или
1 | composer dumpautoload |
После этого в /vendor/composer/autoload_classmap.php появляется массив ключ=значение, где ключ — это полное название класса, а значение — это путь к данному классу. Сам файл выглядит примерно так:
123456789 | // autoload_classmap.php @generated by Composer $vendorDir = dirname(dirname( __FILE__ )); $baseDir = dirname( $vendorDir ); return array ( 'My_Namespace\\Example1' => $baseDir . '/folder1/class-example1.php' , 'My_Namespace\\Example2' => $baseDir . '/folder2/class-example2.php' , ); |
Свой автозагрузчик с помощью spl_autoload
Иногда проект может быть достаточно большой включать в себя плагины, mu-плагины и темы. При работе с мультисайтом/ами вполне возможна такая ситуация.
Столкнувшись с такой проблемой, решил сделать небольшой mu-плагин, который будет подгружать все нужные мне файлы сам. Решил отойти от composer’а, чтобы не тянуть его везде, где он мне нужен.
Меняем namespace на путь к файлу
Следующий пример написан с соблюдением WPCS:
010203040506070809101112131415161718192021222324 | class Autoload { private $prefix = 'My_Namespace' ; public function __construct() { spl_autoload_register( [ $this , 'autoload' ] ); } private function autoload( string $class ): void { if ( 0 === strpos ( $class , $this ->prefix ) ) { $plugin_parts = explode ( '\\' , $class ); $name = array_pop ( $plugin_parts ); $name = preg_match( '/^(Interface|Trait)/' , $name ) ? $name . '.php' : 'class-' . $name . '.php' ; $path = implode( '/' , $plugin_parts ) . '/' . $name ; $path = strtolower ( str_replace ( [ '\\' , '_' ], [ '/' , '-' ], $path ) ); $path = WP_CONTENT_DIR . '/plugins/' . $path ; require_once $path ; } } } new Autoload(); |
С помощью ф-ции spl_autoload_register мы добавляем autoload, который будет срабатывать каждый раз, когда вызывается неизвестная ф-ция, класс или интерфейс.
Обязательно проверяем на то, чтобы все классы начинались с $this->prefix, который в примере My_Namespace. Затем формируем нужный путь к файлу и подключаем его. Все вроде бы хорошо, но есть одна проблема с тем, что при большом кол-ве классов слишком много действий вместо просто подключения файлов. Для этого нужно сделать механизм кеширования. Попробуем сделать что-то, вроде classmap от composer’а.
Classmap для spl_autoload
010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354 | class Autoload { private $map_file ; private $map ; private $prefix = 'My_Namespace' ; private $has_been_update = false; public function __construct() { $this ->map_file = __DIR__ . '/classmap.php' ; $this ->map = @ include $this ->map_file; $this ->map = is_array ( $this ->map ) ? $this ->map : []; spl_autoload_register( [ $this , 'autoload' ] ); add_action( 'shutdown' , [ $this , 'update_cache' ] ); } private function autoload( string $class ): void { if ( 0 === strpos ( $class , $this ->prefix ) ) { if ( $this ->map[ $class ] && file_exists ( $this ->map[ $class ] ) ) { require_once $this ->map[ $class ]; } else { $this ->has_been_update = true; $plugin_parts = explode ( '\\' , $class ); $name = array_pop ( $plugin_parts ); $name = preg_match( '/^(Interface|Trait)/' , $name ) ? $name . '.php' : 'class-' . $name . '.php' ; $path = implode( '/' , $plugin_parts ) . '/' . $name ; $path = strtolower ( str_replace ( [ '\\' , '_' ], [ '/' , '-' ], $path ) ); $path = WP_CONTENT_DIR . '/plugins/' . $path ; $this ->map[ $class ] = $path ; require_once $path ; } } } public function update_cache(): void { if ( ! $this ->has_been_update ) { return ; } $map = implode( "\n" , array_map ( function ( $k , $v ) { return "'$k' => '$v'," ; }, array_keys ( $this ->map ), array_values ( $this ->map ) ) ); file_put_contents ( $this ->map_file, '<?php return [' . $map . '];' ); } } |
Добавляем 3 свойства:
- $map_file — путь к файлу classmap
- $map — classmap
- $has_been_update — свойство, которое проверяет обновился ли classmap с последней загрузки страницы.
Метод autoload немного поменялся. Вот основные отличия:
1 | if ( $this ->map[ $class ] && file_exists ( $this ->map[ $class ] ) ) { // Подключаем файл, который мы нашли в classmap. Проверка file_exists нужна на случай, если мы захотим удалить, переместить или переименовать файл. } else { $this ->has_been_update = true; // classmap нужно обновить ... $this ->map[ $class ] = $path ; // Обновляем classmap ... } |
В метод update_cache, который срабатывает на событие shutdown обновляем сам файл classmap’а, если он был изменен с последней загрузки.
Полный пример кода с поддержкой WPCS можно посмотреть на github’e: https://github.com/mdenisenko/WP-Autoload
Смотрите также
- Построение интерактивной карты с Raphaël
- WordPress Coding Standards (WPCS)
- Принципы SOLID
- Создание Gutenberg блоков при помощи плагина ACF Pro