Лучший способ инициализации класса в плагине WordPress

22 окт. 2012 г., 12:22:32
Просмотры: 76.9K
Голосов: 108

Я создал плагин и, конечно же, хотел использовать хороший объектно-ориентированный подход. До сих пор я создавал класс, а затем сразу под ним создавал экземпляр этого класса:

class ClassName {

    public function __construct(){

    }
}

$class_instance = new ClassName();  

Я предполагаю, что существует более "WordPress-овский" способ инициализации этого класса. Затем я наткнулся на людей, которые говорят, что предпочитают иметь функцию init() вместо __construct(). Аналогично я обнаружил, что некоторые используют следующий хук:

class ClassName {

    public function init(){

    }
}
add_action( 'load-plugins.php', array( 'ClassName', 'init' ) );

Какой способ обычно считается лучшим для создания экземпляра класса WordPress при загрузке и получения к нему глобального доступа?

ПРИМЕЧАНИЕ: Интересно заметить, что хотя register_activation_hook() можно вызвать внутри __construct, его нельзя вызвать внутри init() во втором примере. Возможно, кто-то мог бы прояснить этот момент.

Редактирование: Спасибо за все ответы, очевидно, есть немало споров о том, как обрабатывать инициализацию внутри самого класса, но я думаю, есть общий консенсус, что add_action( 'plugins_loaded', ...); - это лучший способ запустить его...

Редактирование: Чтобы еще больше запутать ситуацию, я также видел такое использование (хотя я бы сам не использовал этот метод, потому что превращение хорошо организованного ОО-класса в функцию кажется противоречащим его смыслу):

// Запуск этого плагина
add_action( 'init', 'ClassName' );
function ClassName() {
    global $class_name;
    $class_name = new ClassName();
}
2
Комментарии

Что касается последнего редактирования, если это содержится в том же файле плагина, что и класс, это становится несколько бесполезным. Вы можете просто создать экземпляр класса, как описано в моем методе. Даже если это в отдельном файле, это все равно несколько бессмысленно. Единственный вариант использования, который я вижу, это если вы хотите создать функцию-обертку, которая позволяет создавать экземпляр класса за пределами файлов вашего плагина, например, внутри тем. Даже в этом случае, мне пришлось бы спросить, какая логика стоит за этим, потому что правильное использование условных выражений и хуков должно позволять тонкий контроль над созданием экземпляров, позволяя сосредоточиться на использовании плагина.

Adam Adam
23 окт. 2012 г. 19:14:39

Я в целом согласен с этим, но подумал, что стоит упомянуть, так как встречал такое в нескольких WP-плагинах.

kalpaitch kalpaitch
24 окт. 2012 г. 11:40:37
Все ответы на вопрос 5
7
92

Прибыв сюда ровно через 2 года после того, как был задан оригинальный вопрос, хочу отметить несколько моментов. (Не просите меня отмечать много моментов, никогда).

Правильный хук

Для инициализации класса плагина следует использовать правильный хук. Универсального правила нет, так как это зависит от функционала класса.

Использование очень раннего хука, такого как "plugins_loaded", часто не имеет смысла, потому что он срабатывает для админки, фронтенда и AJAX-запросов. Но зачастую лучше использовать более поздний хук, который позволяет инициализировать классы плагина только когда это действительно необходимо.

Например, класс, работающий с шаблонами, можно инициализировать на хуке "template_redirect".

Вообще говоря, очень редко классу требуется инициализация до срабатывания хука "wp_loaded".

Нет "Божественному классу"

Большинство примеров классов в старых ответах используют названия вроде "Prefix_Example_Plugin" или "My_Plugin"... Это указывает на наличие главного класса для плагина.

Что ж, если плагин не состоит всего из одного класса (в этом случае именование по названию плагина абсолютно оправдано), создание класса, управляющего всем плагином (например, добавляющего все необходимые хуки или инициализирующего все остальные классы плагина) можно считать плохой практикой, примером божественного объекта.

В объектно-ориентированном программировании код должен стремиться к S.O.L.I.D., где "S" означает "Принцип единственной ответственности".

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

Избегайте хуков в конструкторе

Этот аргумент уже был упомянут в других ответах, но я хочу подчеркнуть эту концепцию и сослаться на этот другой ответ, где она была довольно подробно объяснена в контексте unit-тестирования.

Почти 2015: PHP 5.2 для зомби

С 14 августа 2014 года поддержка PHP 5.3 прекращена. Он окончательно мертв. PHP 5.4 будет поддерживаться весь 2015 год, то есть еще год на момент написания.

Однако WordPress по-прежнему поддерживает PHP 5.2, но никто не должен писать ни строчки кода, поддерживающей эту версию, особенно если код ООП.

Есть несколько причин:

  • PHP 5.2 умер давно, для него не выпускаются исправления безопасности, значит он небезопасен
  • PHP 5.3 добавил множество возможностей, анонимные функции и пространства имен über alles
  • новые версии PHP намного быстрее. PHP бесплатен. Обновление бесплатно. Зачем использовать медленную, небезопасную версию, если можно бесплатно перейти на быструю и безопасную?

Если не хотите использовать PHP 5.4+, используйте хотя бы 5.3+

Пример

Пришло время пересмотреть старые ответы с учетом сказанного.

Раз нам больше не нужно заботиться о 5.2, мы можем и должны использовать пространства имен.

Для лучшего объяснения принципа единственной ответственности, мой пример будет использовать 3 класса: один делает что-то во фронтенде, другой в админке, а третий используется в обоих случаях.

Класс для админки:

namespace GM\WPSE\Example;

class AdminStuff {

   private $tools;

   function __construct( ToolsInterface $tools ) {
     $this->tools = $tools;
   }

   function setup() {
      // настройка класса, возможно добавление хуков
   }

}

Класс для фронтенда:

namespace GM\WPSE\Example;

class FrontStuff {

   private $tools;

   function __construct( ToolsInterface $tools ) {
     $this->tools = $tools;
   }

   function setup() {
      // настройка класса, возможно добавление хуков
   }

}

Интерфейс Tools:

namespace GM\WPSE\Example;

interface ToolsInterface {

   function doSomething();

}

И класс Tools, используемый двумя другими:

namespace GM\WPSE\Example;

class Tools implements ToolsInterface {

   function doSomething() {
      return 'done';
   }

}

Имея эти классы, я могу инициализировать их с помощью подходящих хуков. Например:

require_once plugin_dir_path( __FILE__ ) . 'src/ToolsInterface.php';
require_once plugin_dir_path( __FILE__ ) . 'src/Tools.php';

add_action( 'admin_init', function() {

   require_once plugin_dir_path( __FILE__ ) . 'src/AdminStuff.php';
   $tools = new GM\WPSE\Example\Tools;
   global $admin_stuff; // это не идеально, причина объяснена ниже
   $admin_stuff = new GM\WPSE\Example\AdminStuff( $tools ); 
} );

add_action( 'template_redirect', function() {

   require_once plugin_dir_path( __FILE__ ) . 'src/FrontStuff.php';
   $tools = new GM\WPSE\Example\Tools;
   global $front_stuff; // это не идеально, причина объяснена ниже
   $front_stuff = new GM\WPSE\Example\FrontStuff( $tools );    
} );

Инверсия зависимостей и внедрение зависимостей

В примере выше я использовал пространства имен и анонимные функции для инициализации разных классов на разных хуках, применяя сказанное ранее.

Обратите внимание, как пространства имен позволяют называть классы без префиксов.

Я применил еще одну концепцию, косвенно упомянутую ранее: Внедрение зависимостей — это один из методов применения принципа инверсии зависимостей, "D" в аббревиатуре SOLID.

Класс Tools "внедряется" в два других класса при их инициализации, что позволяет разделить ответственность.

Кроме того, классы AdminStuff и FrontStuff используют указание типов для объявления, что им нужен класс, реализующий ToolsInterface.

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

Однако пример выше можно улучшить. Давайте посмотрим как.

Автозагрузка

Хороший способ писать более читаемый ООП-код — не смешивать определения типов (интерфейсы, классы) с другим кодом и размещать каждый тип в отдельном файле.

Это правило также является частью стандартов кодирования PSR-11.

Однако, следуя этому, перед использованием класса нужно подключить файл, в котором он находится.

Это может быть утомительно, но PHP предоставляет функции для автоматической загрузки класса, когда он требуется, используя callback, который загружает файл на основе его имени.

С пространствами имен это становится очень просто, потому что теперь можно сопоставить структуру папок со структурой пространств имен.

Это не только возможно, но и является еще одним стандартом PSR (точнее двумя: PSR-0, теперь устаревший, и PSR-4).

Следуя этим стандартам, можно использовать различные инструменты для автозагрузки, не создавая собственный автозагрузчик.

Должен сказать, что стандарты кодирования WordPress имеют другие правила для именования файлов.

Поэтому при написании кода для ядра WordPress разработчики должны следовать правилам WP, но при написании пользовательского кода это выбор разработчика. Однако использование стандартов PSR упрощает применение уже готовых инструментов2.

Глобальный доступ, реестр и паттерн сервис-локатора.

Одна из главных проблем при инициализации классов плагина в WordPress — как получить к ним доступ из разных частей кода.

Сам WordPress использует глобальный подход: переменные сохраняются в глобальной области видимости, делая их доступными везде. Каждый разработчик WP тысячи раз печатал слово global в своей карьере.

Этот подход я использовал в примере выше, но он зло.

Этот ответ уже слишком длинный, чтобы позволить мне подробнее объяснить почему, но чтение первых результатов в поиске по "глобальные переменные зло" — хорошее начало.

Но как можно избежать глобальных переменных?

Есть несколько способов.

Некоторые старые ответы здесь используют подход статического экземпляра.

public static function instance() {

  if ( is_null( self::$instance ) ) {
    self::$instance = new self;
  }

  return self::$instance;
}

Это просто и вполне нормально, но требует реализации паттерна для каждого класса, к которому нужен доступ.

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

Я уже объяснил, насколько плох божественный класс, поэтому подход со статическим экземпляром — хороший вариант, когда плагину нужно сделать доступным всего один-два класса.

Это не значит, что его можно использовать только для плагинов с парой классов. Фактически, при правильном использовании принципа внедрения зависимостей можно создавать довольно сложные приложения без необходимости делать глобально доступным большое количество объектов.

Однако иногда плагинам нужно сделать доступными несколько классов, и в этом случае подход со статическим экземпляром становится утомительным.

Другой возможный подход — использование паттерна реестра.

Вот очень простая его реализация:

namespace GM\WPSE\Example;

class Registry {

   private $storage = array();

   function add( $id, $class ) {
     $this->storage[$id] = $class;
   }

   function get( $id ) {
      return array_key_exists( $id, $this->storage ) ? $this->storage[$id] : NULL;
   }

}

Используя этот класс, можно хранить объекты в реестре по идентификатору, так что имея доступ к реестру, можно получить доступ ко всем объектам. Конечно, при первом создании объекта его нужно добавить в реестр.

Пример:

global $registry;

if ( is_null( $registry->get( 'tools' ) ) ) {
  $tools = new GM\WPSE\Example\Tools;
  $registry->add( 'tools', $tools );
}

if ( is_null( $registry->get( 'front' ) ) {
  $front_stuff = new GM\WPSE\Example\FrontStuff( $registry->get( 'tools' ) );    
  $registry->add( 'front', front_stuff );
}

add_action( 'wp', array( $registry->get( 'front' ), 'wp' ) );

Пример выше показывает, что для полезности реестр должен быть глобально доступным. Одна глобальная переменная только для реестра не очень плохо, но для противников глобальных переменников можно реализовать подход статического экземпляра для реестра или функцию со статической переменной:

function gm_wpse_example_registry() {
  static $registry = NULL;
  if ( is_null( $registry ) ) {
    $registry = new GM\WPSE\Example\Registry;
  }
  return $registry;
}

При первом вызове функция создаст реестр, при последующих — просто вернет его.

Еще один специфичный для WordPress метод сделать класс глобально доступным — возвращать экземпляр объекта из фильтра. Например:

$registry = new GM\WPSE\Example\Registry;

add_filter( 'gm_wpse_example_registry', function() use( $registry ) {
  return $registry;
} );

После этого везде, где нужен реестр:

$registry = apply_filters( 'gm_wpse_example_registry', NULL );

Еще один паттерн, который можно использовать — сервис-локатор. Он похож на реестр, но сервис-локаторы передаются в различные классы через внедрение зависимостей.

Главная проблема с этим паттерном в том, что он скрывает зависимости классов, делая код сложнее для поддержки и чтения.

DI-контейнеры

Независимо от метода, используемого для глобального доступа к реестру или сервис-локатору, объекты должны храниться там, а перед хранением их нужно инициализировать.

В сложных приложениях, где много классов и многие из них имеют несколько зависимостей, инициализация классов требует много кода, поэтому вероятность ошибок возрастает: код, которого нет, не может содержать ошибок.

В последние годы появились некоторые PHP-библиотеки, помогающие разработчикам легко инициализировать и хранить экземпляры объектов, автоматически разрешая их зависимости.

Эти библиотеки известны как контейнеры внедрения зависимостей, потому что они могут инициализировать классы, разрешая зависимости, а также хранить объекты и возвращать их при необходимости, действуя подобно реестру.

Обычно при использовании DI-контейнеров разработчики должны настраивать зависимости для каждого класса приложения, и тогда при первом запросе класса он инициализируется с нужными зависимостями, а тот же экземпляр возвращается при последующих запросах.

Некоторые DI-контейнеры также могут автоматически обнаруживать зависимости без конфигурации, используя рефлексию PHP.

Некоторые известные DI-контейнеры:

и многие другие.

Хочу отметить, что для простых плагинов с несколькими классами и небольшим количеством зависимостей, вероятно, не стоит использовать DI-контейнеры: подход со статическим экземпляром или глобально доступным реестром — хорошие решения. Но для сложных плагинов преимущества DI-контейнера становятся очевидными.

Конечно, даже объекты DI-контейнера должны быть доступны для использования в приложении, и для этого можно использовать один из уже рассмотренных методов: глобальная переменная, статическая переменная экземпляра, возврат объекта через фильтр и т.д.

Composer

Использование DI-контейнера часто означает использование стороннего кода. Сегодня в PHP просто скачивать внешние библиотеки (не только DI-контейнеры, но любой код, не являющийся частью приложения) и помещать их в папку приложения не считается хорошей практикой. Даже если мы авторы этого кода.

Разделение кода приложения от внешних зависимостей — признак лучшей организации, надежности и здравомыслия кода.

Composer — де-факто стандарт в PHP-сообществе для управления зависимостями. Далекий от того, чтобы быть мейнстримом в сообществе WP, это инструмент, который каждый PHP и WordPress разработчик должен хотя бы знать, если не использовать.

Этот ответ уже размером с книгу, чтобы позволить дальнейшее обсуждение, да и обсуждение Composer здесь, вероятно, не по теме. Он упомянут только для полноты.

Для получения дополнительной информации посетите сайт Composer, а также стоит прочитать этот мини-сайт, курируемый @Rarst.


1 PSR — стандарты правил PHP, выпущенные PHP Framework Interop Group

2 Composer (библиотека, которая будет упомянута в этом ответе) среди прочего также содержит утилиту автозагрузки.

27 окт. 2014 г. 00:48:49
Комментарии

Дополнительная заметка: PHP 5.3 также больше не поддерживается. Ответственный хостинг-провайдер предложит как минимум версию 5.4, если не сделает её версией по умолчанию.

Tom J Nowell Tom J Nowell
27 окт. 2014 г. 12:55:54

"С 14 августа 2014 года PHP 5.3 достиг конца жизненного цикла. Он определённо мёртв." Это первая строка под заголовком "PHP 5.2 для зомби" @TomJNowell

gmazzap gmazzap
27 окт. 2014 г. 13:50:49

Просто интересно, как бы выглядело указание на "много вещей"? ;-)

MikeSchinkel MikeSchinkel
25 дек. 2015 г. 11:15:58

В стремлении избежать глобальных переменных мне нравится идея шаблона Registry с функцией и статической переменной. При первом вызове функции она создаст экземпляр реестра, при последующих вызовах будет просто возвращать его.

Michael Ecklund Michael Ecklund
11 авг. 2017 г. 19:00:43

Главная проблема реестра в том, что он создает путаницу переменных, а опечатки могут вызывать множество проблем. Если вы используете предопределенные классы, все современные IDE предоставят вам обзор свойств класса и позволят легко выбрать нужное. Массивы — одни из наихудших переменных, так как вызывают множество проблем. Самая распространенная ошибка, которую я вижу в WordPress-плагинах — это "undefined index". Использование классов гарантирует, что все ваши свойства будут определены, когда они вам понадобятся. Реестры приводят к более грязной и ошибочной архитектуре.

D.A.H D.A.H
20 мая 2021 г. 11:47:31

Есть ли крупные WordPress-плагины, которые не используют God-класс, которые я могу посмотреть на GitHub? Насколько я могу судить, крупные вендоры, такие как Skyverge и другие, по-прежнему используют идею God-класса.

Kevin Chavez Kevin Chavez
10 авг. 2021 г. 09:04:22

Есть ли обновления на 2022 год? И также когда / где вызывается setup() для ваших классов?

Kevin Chavez Kevin Chavez
10 янв. 2022 г. 04:22:43
Показать остальные 2 комментариев
14
71

Хороший вопрос, существует несколько подходов, и выбор зависит от того, чего вы хотите достичь.

Я часто использую:

add_action( 'plugins_loaded', array( 'someClassy', 'init' ));

class someClassy {

    public static function init() {
        $class = __CLASS__;
        new $class;
    }

    public function __construct() {
           //здесь можно разместить нужную логику конструктора...
    }

    //и так далее...
}

Более подробный и глубокий пример, который появился в результате недавних обсуждений этой темы в чате, можно увидеть в этом gist от участника WPSE toscho.

Подход с пустым конструктором.

Вот выдержка преимуществ/недостатков из упомянутого gist, которая демонстрирует подход с пустым конструктором в полной мере.

  • Преимущества:

    • Юнит-тесты могут создавать новые экземпляры без автоматической активации хуков. Нет синглтона.

    • Не требуется глобальная переменная.

    • Любой, кто хочет работать с экземпляром плагина, может просто вызвать T5_Plugin_Class_Demo::get_instance().

    • Легко деактивировать.

    • По-прежнему реальное ООП: никакие рабочие методы не являются статическими.

  • Недостаток:

    • Возможно, сложнее для чтения?

На мой взгляд, недостаток довольно слабый, поэтому этот подход мне нравится больше всего, хотя я использую и другие. Кстати, несколько других экспертов наверняка поделятся своим мнением по этой теме в ближайшее время, потому что вокруг неё есть множество интересных точек зрения, которые стоит озвучить.


примечание: мне нужно найти gist-пример от toscho, где сравнивались 3 или 4 способа инициализации класса в плагине с разбором плюсов и минусов каждого. Ссылка выше ведёт на предпочтительный способ, но другие примеры дают хороший контраст к этой теме. Надеюсь, toscho всё ещё хранит этот файл.

Примечание: Ответ WPSE по этой теме с соответствующими примерами и сравнениями. Также это лучшее решение для инициализации класса в WordPress.

add_shortcode( 'baztag', array( My_Plugin::get_instance(), 'foo' ) );
class My_Plugin {

    private $var = 'foo';

    protected static $instance = NULL;

    public static function get_instance() {

        // создаём объект
        NULL === self::$instance and self::$instance = new self;

        return self::$instance; // возвращаем объект
    }

    public function foo() {

        return $this->var; // никогда не используйте echo или print в шорткоде!
    }
}
22 окт. 2012 г. 12:42:51
Комментарии

в чем разница между add_action('plugins_loaded',...); и add_action('load-plugins.php',...); В примере, который я взял, использовался последний вариант

kalpaitch kalpaitch
22 окт. 2012 г. 12:46:42

Я не уверен, но судя по названиям, хук plugins_loaded выполняется после загрузки плагинов, тогда как хук load-plugins.php привязан к самому процессу загрузки плагинов. Практически разница невелика, но теоретически могут возникнуть проблемы, если вы вызываете методы, когда не все плагины загружены. Поэтому я бы предпочел использовать plugins_loaded. Опять же, я просто интерпретирую их названия.

Tim S. Tim S.
22 окт. 2012 г. 13:00:16

Насколько я понимаю, load-plugins.php, хоть и работает, связан с основным файлом update.php и не является частью стандартных действий, на которые можно полагаться при последовательности событий во время инициализации. По этой причине я предпочитаю использовать хуки, которые действительно применяются, в данном случае plugins_loaded. Это то, что я часто называю быстрым снимком происходящего согласно Справочнику действий. Мое объяснение не является исчерпывающим.

Adam Adam
22 окт. 2012 г. 13:00:28

Вы ищете этот ответ?

fuxia fuxia
22 окт. 2012 г. 16:02:13

@toscho Да, именно это. Я помню, мы рассматривали это в чате, но также в виде Gist. Но суть та же. Спасибо за это!

Adam Adam
22 окт. 2012 г. 16:37:25

Очень полезно, и add_shortcode() кажется гораздо более простым способом включить вывод определенной части этого класса, чем получение экземпляра, так сказать. Возможно ли использовать шорткод как в файлах шаблонов, так и в редакторе WordPress?

kalpaitch kalpaitch
22 окт. 2012 г. 18:47:22

@kalpaitch Если шорткод подходит под ваш случай использования, то да, echo do_shortcode('[my-shortcode]'); в своей базовой форме сработает, когда нужно вызвать шорткод вне традиционного экрана редактора записей.

Adam Adam
22 окт. 2012 г. 18:59:45

Мне нравится этот подход в стиле singleton. Однако, я сомневаюсь насчет использования plugins_loaded в качестве хука для инициализации. Этот хук предназначен для выполнения после загрузки всех плагинов. Подключаясь после него, вы в некотором роде перехватываете этот хук и можете столкнуться с конфликтами или проблемами последовательности запуска с другими плагинами или темами, которые используют plugins_loaded. Я бы не использовал никакой хук для запуска метода инициализации. Архитектура плагинов задумана для инлайнового выполнения, а не через действия.

Tom Auger Tom Auger
24 окт. 2012 г. 00:25:56

Я чаще всего использую хук init, но понимаю вашу точку зрения насчет plugins_loaded, это имеет смысл. Также логично, если вашей целью было перехватить этот хук для данной задачи. Я не совсем разделяю ваш взгляд на использование хуков в плагинах, но мне было бы интересно узнать больше о вашей позиции по этой теме. Может быть, у вас есть рекомендуемые материалы? Ссылки? Дополнительные примеры, с какими проблемами последовательности мы можем столкнуться? Спасибо, дружище..

Adam Adam
24 окт. 2012 г. 00:46:22

@Tom Auger, я полностью понимаю вашу точку зрения о загрузке с использованием хука plugins_loaded. Будет ли лучше использовать первый метод из вопроса для инициализации класса, или если вы предлагаете альтернативный метод, не могли бы вы включить его в ответ?

kalpaitch kalpaitch
24 окт. 2012 г. 11:38:07

Обратите внимание, что если вы используете register_activation_hook(), вам нужно вызвать эту функцию до того, как сработает действие plugins_loaded.

Geert Geert
31 окт. 2012 г. 11:52:33

@Geert, согласен, я обнаружил, что эти хуки активации всё равно должны быть размещены в __contruct

kalpaitch kalpaitch
2 нояб. 2012 г. 17:29:56

В качестве дополнительной информации ознакомьтесь с этой записью от @mikeschinkel и обсуждением в комментариях. http://hardcorewp.com/2012/using-classes-as-code-wrappers-for-wordpress-plugins/#comment-149

bueltge bueltge
3 янв. 2013 г. 13:24:59

@bueltge Спасибо за ссылку, обязательно прочитаю. Похоже на интересное обсуждение!

Adam Adam
3 янв. 2013 г. 13:53:39
Показать остальные 9 комментариев
4
11

Я использую следующую структуру:

Prefix_Example_Plugin::on_load();

/**
 * Пример начальной загрузки плагина на основе класса.
 */
class Prefix_Example_Plugin {

    /**
     * Инициализирует хуки (только init) и вызывает методы, которые должны выполниться сразу.
     */
    static function on_load() {

        // здесь можно добавить аварийный выключатель (если определена константа disable, то завершить выполнение)

        add_action( 'init', array( __CLASS__, 'init' ) );
    }

    /**
     * Дальнейшая настройка хуков, загрузка файлов и т.д.
     *
     * Обратите внимание, что для методов-хуков имя обычно совпадает с названием хука (если возможно).
     */
    static function init(  ) {


    }
}

Примечания:

  • имеет определенное место для методов, которые должны выполняться сразу
  • легко отключить/переопределить для доработок (достаточно отцепить один метод init)
  • я не думаю, что мне когда-либо требовался объект класса плагина - это требует его отслеживания и т.д.; по сути это фейковое пространство имен по задумке, а не ООП (в большинстве случаев)

Отказ от ответственности Я пока не использую unit-тесты (слишком много дел) и слышал, что статические методы могут быть менее предпочтительными для них. Проверьте этот момент самостоятельно, если вам нужно тестировать код.

22 окт. 2012 г. 13:10:19
Комментарии

Я знаю, что люди, увлекающиеся unit-тестированием, не очень любят статические решения и синглтоны. Я считаю, что если вы полностью понимаете, чего пытаетесь достичь, используя статические методы, и хотя бы осознаёте последствия такого подхода, то вполне допустимо их реализовывать. Хорошие обсуждения на эту тему можно найти на [SO]

Adam Adam
22 окт. 2012 г. 13:21:34

Это заставило меня серьёзно задуматься. Так зачем тогда использовать классы, а не вернуться к простым функциям с префиксами? Разве мы делаем это только для более чистых имён функций/методов? Имеет ли вложенность со "static" перед ними такую уж большую разницу в читаемости? Вероятность конфликта имён примерно такая же, как и для имени класса, если использовать правильные префиксы, или я что-то упускаю?

James Mitch James Mitch
28 мая 2013 г. 08:52:37

@JamesMitch да, методы класса со static - это по сути просто функции с фейковым пространством имён, как это используется в WP. Однако классы имеют некоторые преимущества перед чистыми функциями даже в этом случае, например автозагрузку и наследование. В последнее время я перехожу от статических методов к реальным инстанцируемым объектам, организованным через контейнер внедрения зависимостей.

Rarst Rarst
28 мая 2013 г. 13:35:23

Интересная идея, но она может быть применена только к основному классу плагина. Одна из самых больших проблем многих WordPress-плагинов заключается в том, что они загружают тонны кода, который никогда не используется для запроса. Чтобы избежать загрузки большого количества фактически неиспользуемого кода, вам нужно разделить функциональность на меньшие части, используя пространства имен и классы, а затем задействовать автозагрузку, которая загружает только те файлы, которые действительно используются. Такой подход требует более тщательной разработки плагина, чтобы убедиться, что у вас есть логика, основанная на запросах (код используется всегда для одного запроса и завершает работу после этого).

D.A.H D.A.H
20 мая 2021 г. 11:21:55
2

Все зависит от функциональности.

Однажды я создал плагин, который регистрировал скрипты при вызове конструктора, поэтому мне пришлось подключить его к хуку wp_enqueue_scripts.

Если вы хотите вызвать его при загрузке файла functions.php, вы также можете создать экземпляр самостоятельно $class_instance = new ClassName();, как вы упомянули.

Возможно, стоит учитывать скорость и использование памяти. Я не знаю конкретных случаев, но могу представить, что в некоторых ситуациях есть неиспользуемые хуки. Создавая экземпляр на этом хуке, вы можете сэкономить некоторые ресурсы сервера.

22 окт. 2012 г. 12:28:35
Комментарии

Круто, спасибо за это. Полагаю, в вышеупомянутом вопросе есть еще два момента. Другой заключается в том, подходит ли __construct или init() является лучшим способом инициализации класса.

kalpaitch kalpaitch
22 окт. 2012 г. 12:40:28

Ну, я бы выбрал статический метод init(), чтобы экземпляр класса вызывался в области видимости класса, а не в другой области, где можно случайно перезаписать существующие переменные.

Tim S. Tim S.
22 окт. 2012 г. 12:54:10
1

Я знаю, что это уже пару лет как устарело, но тем временем php 5.3 поддерживает анонимные методы, поэтому я придумал вот такой вариант:

add_action( 'plugins_loaded', function() { new My_Plugin(); } );

И почему-то он мне нравится больше всего. Я могу использовать обычные конструкторы и мне не нужно определять какие-либо методы "init" или "on_load", которые нарушают мои ООП-структуры.

25 сент. 2017 г. 20:56:58
Комментарии

Вы можете использовать как анонимные функции, так и анонимные классы. Анонимные классы подходят, если ваш класс выполняет ограниченные задачи и вам не требуется его сериализация.

D.A.H D.A.H
20 мая 2021 г. 11:24:50