Автозагрузка классов для 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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *