Лучший способ инициировать класс в WP плагине?

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

class ClassName {

    public function __construct () {

    }
}

$ class_instance = new ClassName ();

Я предполагаю, что есть более WP-способ, чтобы инициировать этот класс, а затем я встретил людей, говорящих, что они предпочитают иметь функцию init (), чем конструкцию __ () ) один. И аналогичным образом я нашел несколько человек, использующих следующий крючок:

class ClassName {

    public function init () {

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

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

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

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

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

//Запускаем этот плагин
add_action ('init', 'ClassName');
function ClassName () {
    global $ class_name;
    $ class_name = new ClassName ();
}
80 голосов | спросил kalpaitch 22 +04002012-10-22T13:22:32+04:00312012bEurope/MoscowMon, 22 Oct 2012 13:22:32 +0400 2012, 13:22:32

5 ответов


52

Хороший вопрос, существует ряд подходов, и это зависит от того, чего вы хотите достичь.

Я часто делаю;

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

class someClassy {

    public static function init () {
        $ class = __CLASS__;
        новый $ class;
    }

    public function __construct () {
           //строим то, что вы видите здесь ...
    }

    //и т.д...
}

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

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

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

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

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

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

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

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

    •   
    • Все еще реальный ООП: никакие рабочие методы не являются статическими.

    •   
  •   
  • Неудобство:

         
    • Может быть, труднее читать?
    •   
  •   

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


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

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

add_shortcode ('baztag', array (My_Plugin :: get_instance (), 'foo'));
класс My_Plugin {

    private $ var = 'foo';

    protected static $ instance = NULL;

    public static function get_instance () {

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

        return self :: $ instance; //вернуть объект
    }

    public function foo () {

        return $ this-> var; //никогда не эхо или печатать в коротком коде!
    }
}
ответил userabuser 22 +04002012-10-22T13:42:51+04:00312012bEurope/MoscowMon, 22 Oct 2012 13:42:51 +0400 2012, 13:42:51
70

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

Правильный крючок

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

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

например. класс, который делает материал для шаблонов, может быть создан на "template_redirect".

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

Нет класса Бога

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

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

В объектно-ориентированном программном коде должен быть SOLID , где «S» означает «принцип единой ответственности» .

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

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

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

Почти 2015: PHP 5.2 предназначен для зомби

С 14 августа 2014 года PHP 5.3 достиг своего конца жизни . Это определенно мертво. PHP 5.4 будет поддерживаться на все 2015 год, это означает, что на следующий год я пишу.

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

Существуют разные причины:

  • PHP 5.2 давно мертв, для него не выпущены исправления безопасности, это означает, что он не безопасен
  • PHP 5.3 добавил множество функций к PHP, анонимные функции и < a href = "http://php.net/manual/en/language.namespaces.php" rel = "noreferrer"> пространства имен über alles
  • более новые версии PHP намного быстрее . PHP бесплатный. Обновление бесплатное. Зачем использовать более медленную, небезопасную версию, если вы можете использовать более быструю и безопасную версию бесплатно?

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

Пример

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

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

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

Класс администратора:

namespace GM \ WPSE \ Example;

class AdminStuff {

   частные инструменты $;

   функция __construct (ToolsInterface $ tools) {
     $ this-> tools = $ tools;
   }

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

}

Внешний класс:

namespace GM \ WPSE \ Example;

class FrontStuff {

   частные инструменты $;

   функция __construct (ToolsInterface $ tools) {
     $ this-> tools = $ tools;
   }

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

}

Интерфейс инструментов:

namespace GM \ WPSE \ Example;

интерфейс ToolsInterface {

   функция doSomething ();

}

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

namespace GM \ WPSE \ Example;

класс Tools реализует 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;
   глобальный $ admin_stuff; //это не идеально, причина объясняется ниже
   $ admin_stuff = новый 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;
   глобальный $ front_stuff; //это не идеально, причина объясняется ниже
   $ front_stuff = new GM \ WPSE \ Example \ FrontStuff ($ tools);
});

Инверсия зависимостей & Инъекция зависимостей

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

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

Я применил другое понятие, косвенно упомянутое выше: Инъекция зависимостей , это один из методов применения Принципа инверсии зависимостей , «D» в аббревиатуре SOLID.

Класс Tools «вводится» в других двух классах при их создании, поэтому таким образом можно отделить ответственность.

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

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

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

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

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

Это правило также является одним из Стандарты кодирования PSR-1 1 .

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

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

Использование пространств имен становится очень простым, потому что теперь можно сопоставить структуру папок с структурой пространства имен.

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

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

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

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

Шаблоны глобального доступа, реестра и локатора сервисов.

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

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

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

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

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

Существуют разные способы.

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

public staticfunction instance () {

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

  return self :: $ instance;
}

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

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

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

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

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

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

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

namespace GM \ WPSE \ Example;

класс Реестр {

   private $ storage = array ();

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

   функция get ($ id) {
      return array_key_exists ($ id, $ this-> хранилище)? $ this-> storage [$ id]: NULL;
   }

}

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

Пример:

глобальный реестр $;

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

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

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

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

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, которые помогают разработчикам PHP легко создавать экземпляры объектов и сохранять их, автоматически разрешая свои зависимости.

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

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

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

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

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

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

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

Composer

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

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

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

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

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


1 PSR - это правила стандартов PHP, выпущенные группой PHP Framework Interop

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

ответил gmazzap 27 +03002014-10-27T01:48:49+03:00312014bEurope/MoscowMon, 27 Oct 2014 01:48:49 +0300 2014, 01:48:49
10

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

Prefix_Example_Plugin :: on_load ();

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

    /**
     * Захватывает init (ничего больше) и вызывает то, что нужно запустить сразу.
     * /
    статическая функция on_load () {

        //если нужно, то здесь вызывается kill switch (если отключить константу, а затем вернуть)

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

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


    }
}

Примечания:

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

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

ответил Rarst 22 +04002012-10-22T14:10:19+04:00312012bEurope/MoscowMon, 22 Oct 2012 14:10:19 +0400 2012, 14:10:19
3

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

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

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

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

ответил Tim S. 22 +04002012-10-22T13:28:35+04:00312012bEurope/MoscowMon, 22 Oct 2012 13:28:35 +0400 2012, 13:28:35
0

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

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

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

ответил Basti 25 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowMon, 25 Sep 2017 20:56:58 +0300 2017, 20:56:58

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132