Автозагрузка классов для WordPress
Если вы используете у себя в проектах сотни require
или include
, то вам точно стоит прочесть эту статью.
Зачем нужна автозагрузка?
Автозагрузка (autoload) нужна для того, чтобы навсегда избавится от require, include и постоянного изменения порядка их подключения.
Рассмотрим два вариант autoload:
- composer
- spl_autoload
Autoload своих классов через composer
В файл composer.json нужно добавить директиву autoload и в нее classmap с перечнем папок, в которых нужно искать классы, интерфейсы и прочее.
{ ... "autoload": { "classmap": [ "folder1", "folder2" ] } ... }
После этого нужно обязательно обновить autoload composer’а с помощью следующей команды:
composer dump-autoload
Или
composer dumpautoload
После этого в /vendor/composer/autoload_classmap.php появляется массив ключ=значение, где ключ — это полное название класса, а значение — это путь к данному классу. Сам файл выглядит примерно так:
// 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:
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
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 немного поменялся. Вот основные отличия:
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
Источник: WP Punk.