Помните, что WordPress использует шаблон проектирования, управляемый событиями, и хотя мы часто ссылаемся на действия и фильтры, концепция сводится к крючкам. Поток управления через программу выглядит примерно так:
- Выполнить программу,
- Всякий раз, когда программа натыкается на хук (в WordPress мы увидим
do_actionилиapply_filters), перебираем все зарегистрированные хуки, - Верните управление обратно в программу,
- Выполнять до конца.
Это не полностью отличается от шаблона Publisher/Subscriber (или PubSub для краткости), но есть ключевое отличие: шаблон, управляемый событиями, просто сигнализирует о том, что что-то произошло, и если есть перехватчики, они сработают. Паттерн PubSub скажет зарегистрированному подписчику что-то сделать.
В любом случае, вернемся к хукам в WordPress. Легче всего сохранить две концепции крючков, которые у нас есть, если думать о них так:
- Действия нужны для того, чтобы что-то делать,
- Фильтры предназначены для обработки данных.
Если вы хотите подходить к разработке WordPress в объектно-ориентированном стиле, тесная связь вашего кода с ядром WordPress путем регистрации ваших классов через хуки для основного приложения не является хорошей идеей.
Другими словами, не регистрируйте свою бизнес-логику в WordPress. Держите их отдельно. Вот лакмусовая бумажка того, тесно ли ваша работа связана с WordPress: если вы не можете запустить модульный тест для своего класса без загрузки WordPress, значит, он тесно связан.
Итак, каково решение? Делегация.
Логика домена в WordPress
Логика домена и бизнес-логика, насколько я понимаю, взаимозаменяемы, поэтому, если вы читали предыдущие сообщения об этом, и я говорил о них по-разному, вы знаете, почему.
Затем идея делегирования логики из WordPress классу логики домена в WordPress реализуется промежуточным классом, который отвечает за следующее:
- Подписка на хук,
- Делегирование работы классу.
Я знаю, что классы должны хорошо делать «одну вещь», но что, если эта одна вещь — делегирование?
передавать (полномочия, функции и т. д.) другому в качестве агента
И чтобы правильно передать функциональность другому агенту или, в нашем случае, классу, вы должны иметь возможность знать, что вы делегируете. Иногда, чтобы сделать что-то одно, нужно знать несколько единиц информации.
Так как же это выглядит на практике? Представьте, что у вас есть [AbstractSubscriber](https://github.com/tommcfarlin/remove-empty-shortcodes/blob/master/src/Subscriber/AbstractSubscriber.php)to, который возьмет имя хука в свой конструктор:
<?php
/*
* This file is part of Remove Empty Shortcodes.
*
* (c) Tom McFarlin <tom@tommcfarlin.com>
*
* This source file is subject to the GPL license that is bundled
* with this source code in the file LICENSE.
*/
namespace TomMcFarlinRESCSubscriber;
use TomMcFarlinUtilitiesRegistry;
/**
* An abstract implementation of a subscriber that requires a hook and the ability to
* start the class.
*/
abstract class AbstractSubscriber
{
/**
* @var string a reference to the hook to which the subscriber should be registered
*/
protected $hook;
/**
* @var Registry a reference to the simple container used to maintain plugin objects
*/
protected $registry;
/**
* @param string $hook the hook to which the subscriber is registered
*/
public function __construct(string $hook)
{
$this->hook = $hook;
$this->registry = apply_filters('rescRegistry', null);
}
/**
* @return string the hook to which the subscriber is registered
*/
public function getHook(): string
{
return $this->hook;
}
abstract public function load();
}
И затем, как только это будет сделано, loadфункция отправит работу классу, ответственному за фактическую обработку.
Возьмем, к примеру, этот код из «Удалить пустые шорткоды» :
<?php
/*
* This file is part of Remove Empty Shortcodes.
*
* (c) Tom McFarlin <tom@tommcfarlin.com>
*
* This source file is subject to the GPL license that is bundled
* with this source code in the file LICENSE.
*/
namespace TomMcFarlinRESCWordPress;
/**
* Processes the post content by looking to see if any orphaned shortcode
* exists and then removes it from displaying it from the user.
*/
class PostContentProcessor
{
/**
* A reference to the Shortcode Manager for processing orphaned shortcodes.
*/
private $shortcodeManager;
/**
* Initializes the class by setting up a reference to the Registry and the
* Shortcode Manager.
*/
public function __construct()
{
$registry = apply_filters('rescRegistry', null);
$this->shortcodeManager = $registry->get('shortcodeManager');
}
/**
* @param string $content the filtered post content
*
* @return string $content the filtered post content without the shortcode
*/
public function run(string $content): string
{
return $this->shortcodeManager->processShortcodes($content);
}
}
Класс подписывается на определенное событие, например [the_content](https://developer.wordpress.org/reference/functions/the_content/), а затем делегирует работу классу обработки пост-контента.
Это буквально позволяет загрузочному файлу плагина создавать экземпляр делегата. Затем делегат подключается к WordPress, и когда WordPress достигает нужной точки выполнения, делегат передает ответственность классу, ответственному за его обработку.
Вся эта архитектура не только полностью повторно используемая (см. выше использование абстрактного класса), но и позволяет нам отделить доменную логику от WordPress и протестировать ее изолированно.
Подробнее о разделении ответственности
Я написал несколько других сообщений о разделении интересов:
- Разделение проблем с шаблонами WordPress
- Разделение задач с помощью запросов и вспомогательных функций
- Программирование WordPress: разделение проблем
