Автозагрузка классов для WordPress

Click here to view original web page at www.kobzarev.com
Обложка к записи Автозагрузка классов для WordPress

Если вы используете у себя в проектах сотни require или include, то вам точно стоит прочесть эту статью.

Зачем нужна автозагрузка?

Автозагрузка (autoload) нужна для того, чтобы навсегда избавится от require, include и постоянного изменения порядка их подключения.

Рассмотрим два вариант autoload:

  • composer
  • spl_autoload

Autoload своих классов через composer

В файл composer.json нужно добавить директиву autoload и в нее classmap с перечнем папок, в которых нужно искать классы, интерфейсы и прочее.

01020304050607080910{..."autoload": {"classmap": ["folder1","folder2"]}...}

После этого нужно обязательно обновить autoload composer’а с помощью следующей команды:

1composer dump-autoload

Или

1composer 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:

010203040506070809101112131415161718192021222324class 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

010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354class 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 немного поменялся. Вот основные отличия:

1if ( $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

Смотрите также

  1. Построение интерактивной карты с Raphaël
  2. WordPress Coding Standards (WPCS)
  3. Принципы SOLID
  4. Создание Gutenberg блоков при помощи плагина ACF Pro