Как подойти к современной разработке WordPress (часть 2)
Опубликовано: 2022-03-11WordPress — наиболее широко используемая технология создания сайтов на планете, и на то есть веские причины. Тем не менее, унаследованный код в своей основе представляет собой беспорядок, и эта проблема передается сторонним разработчикам. Некоторые разработчики используют это как предлог для того, чтобы срезать углы в своем собственном PHP-коде WordPress, но в долгосрочной перспективе такой подход обходится дороже для всех изменений, кроме самых тривиальных.
В первой части нашей серии, состоящей из двух частей, мы сосредоточились на общих инструментах проекта и рабочего процесса, а затем на фронтенд-разработке. Теперь пришло время взять быка за рога и бороться с PHP: в частности, как следовать передовым методам работы с внутренним кодом WordPress. Вы можете думать об этом как о учебнике по PHP/WordPress, но немного более сложном, с предположением, что вы уже выполнили некоторую внутреннюю настройку WordPress.
Итак, какие современные принципы разработки программного обеспечения и функции PHP обеспечат вам наибольшую ценность вашего времени? Ниже приведены 10 практик разработки WordPress и PHP, которые я очень рекомендую.
Современная передовая практика разработки WordPress № 1: следуйте «разделению интересов»
Разделение ответственности означает, что части PHP-кода WordPress, имеющие разную функциональность или предназначение, не должны смешиваться друг с другом. Вместо этого они должны быть организованы в отдельные разделы или модули, передавая данные друг другу через определенный интерфейс. ( Интерфейс — это набор определенных параметров, которые модуль принимает в качестве входных данных и которые он выводит обратно.) Тесно связанным термином является принцип единой ответственности : каждый модуль кода (или функция) должен отвечать только за одну вещь.
Конечной целью следования этим принципам является создание кода, который является модульным и, следовательно, удобным для сопровождения, расширяемым и пригодным для повторного использования.
Это было довольно многословно, так что давайте посмотрим на пример WordPress PHP (из ядра WordPress), в котором все запутано. Этот стиль кодирования часто называют «спагетти-кодом», потому что понять его внутреннюю работу практически невозможно. Выдержка ниже была отредактирована для краткости; однако исходный стиль и форматирование были сохранены.
$id = isset( $_REQUEST['id'] ) ? intval( $_REQUEST['id'] ) : 0; <table class="form-table"> <?php $blog_prefix = $wpdb->get_blog_prefix( $id ); $sql = "SELECT * FROM {$blog_prefix}options WHERE option_name NOT LIKE %s AND option_name NOT LIKE %s"; $query = $wpdb->prepare( $sql, $wpdb->esc_like( '_' ) . '%', '%' . $wpdb->esc_like( 'user_roles' ) ); $options = $wpdb->get_results( $query ); foreach ( $options as $option ) { if ( strpos( $option->option_value, "\n" ) === false ) { ?> <tr class="form-field"> <th scope="row"><label for="<?php echo esc_attr( $option->option_name ); ?>"><?php echo esc_html( ucwords( str_replace( '_', ' ', $option->option_name ) ) ); ?></label></th> <?php if ( $is_main_site && in_array( $option->option_name, array( 'siteurl', 'home' ) ) ) { ?> <td><code><?php echo esc_html( $option->option_value ); ?></code></td> <?php } else { ?> <td><input class="<?php echo $class; ?>" name="option[<?php echo esc_attr( $option->option_name ); ?>]" type="text" value="<?php echo esc_attr( $option->option_value ); ?>" size="40" <?php disabled( $disabled ); ?> /></td> <?php } ?> </tr> <?php } } // End foreach </table>
Во-первых, это совершенно непонятно. И мне нравится, что единственный комментарий End foreach
совершенно излишен. У нас есть запросы к базе данных, обработка результатов запросов, дополнительная обработка, встроенная в HTML (если вы не заметили, там есть вложенные if
/ else
), экранирование вывода и HTML-шаблоны, все вместе. Другая проблема заключается в том, что параметр $id
поступает прямо из глобального $_REQUEST
, а не передается фактическому параметру в функцию.
Глядя на это, становится совершенно понятно, почему ядро WordPress оставалось в основном неизменным в течение многих лет. Рефакторинг такого кода — особенно при сохранении существующего поведения — поистине грандиозная задача, которую никто не захочет выполнять.
Итак, как мы делаем это правильно? Что ж, нужно помнить одну вещь: не существует единственно верного пути. Мы упомянули выше качества, к которым мы должны стремиться: Нам нужен пользовательский PHP-код WordPress, чтобы его можно было поддерживать и чтобы он был модульным. Давайте посмотрим, как мы можем разделить приведенный выше код на модули.
- Очевидно, SQL-запросы должны быть в отдельном модуле. В WordPress уже есть хорошо абстрагированный класс
WP_Query
, который следует использовать в качестве примера. - Весь HTML входит в шаблон. Ниже мы рассмотрим PHP-шаблоны.
- Оставшийся PHP-код должен быть заключен в функцию — несколько функций, если код слишком длинный или сложный для одной функции. Такие параметры, как
$id
, передаются через аргументы функции.
Это сильно упрощенная переписка приведенного выше примера:
function betterSiteSettings($args) { $data = WP_Settings_Query($args); // process $data here $context = array_merge([], $data_processed, $other_data); return Template::render('template.name', $context); }
Современная передовая практика разработки WordPress № 2: избегайте глобальных переменных
В WordPress слишком много глобальных переменных. Чем плохи глобальные переменные? Они затрудняют понимание вашего PHP-кода WordPress и делают состояние приложения ненадежным. Любой фрагмент PHP-кода — а это означает, что любой плагин, установленный в WordPress, — может читать и записывать глобальные переменные, поэтому нет гарантии, что они содержат достоверные данные. Попытка понять, какие глобальные переменные используются в чем-то вроде цикла, также не является тривиальной задачей.
Давайте посмотрим на это с практической точки зрения. Этот пример взят из WooCommerce. Наверное, каждый разработчик WordPress знает, что это такое — Цикл:
<?php while ( have_posts() ) : the_post(); ?> <?php wc_get_template_part( 'content', 'single-product' ); ?> <?php endwhile; // end of the loop. ?>
Фрагмент выше отображает шаблон продукта. Как он узнает, какой продукт показывать, если в wc_get_template_part
не переданы никакие параметры? Глядя на шаблон, мы видим, что он начинается с global $product;
, поэтому здесь хранится текущий объект продукта.
Теперь представьте, что у нас есть страница каталога, которая ищет и фильтрует продукты, и мы хотим показать всплывающее окно «сведения о продукте», оставаясь на той же странице. За кулисами интерфейсный скрипт выполняет AJAX-запрос, чтобы получить этот конкретный шаблон продукта. Мы не можем просто вызвать wc_get_template_part('content', 'single-product')
потому что он не использует параметры, поэтому нам нужно установить пару глобальных переменных, чтобы это работало.
Более сложные варианты использования будут включать более одного шаблона, хуки, запускаемые в этих шаблонах, и сторонние плагины, добавляющие свои обратные вызовы к этим хукам. Он может быстро обостриться. У нас нет возможности узнать, на какое глобальное состояние опираются эти обратные вызовы. Сторонние плагины могут свободно изменять любую глобальную переменную в своих обратных вызовах. Вместо того, чтобы использовать систему, мы начинаем бороться с системой, сталкиваясь со странными ошибками, которые возникают из-за ненадежного глобального состояния.
Не было бы более разумным передать этот идентификатор продукта в качестве параметра? Затем мы могли бы повторно использовать этот шаблон, не беспокоясь о том, что испортим глобальные переменные, используемые WordPress.
Современная передовая практика разработки WordPress № 3: используйте объектно-ориентированное программирование (ООП)
Модульность приводит к концепции объектов и объектно-ориентированному программированию. На самом базовом уровне ООП — это способ организации кода. Функции и переменные объединены в классы и соответственно называются методами и свойствами класса. Справочник по плагинам WordPress рекомендует использовать ООП для организации собственного PHP-кода WordPress.
Важным принципом ООП является ограничение доступа к методам и свойствам — или, в терминах PHP, обозначение их как private
или protected
— так, чтобы только другие методы класса могли получить к ним доступ и изменить их. Термин ООП для этого — инкапсуляция : данные инкапсулируются внутри класса, и единственный способ изменить эти данные — использовать предоставленные методы класса.
Это делает отладку и поддержку вашего кода намного проще, чем при использовании глобальных переменных, которые можно изменять в любом месте всей кодовой базы. Рассмотрим глобальную переменную записи post
. Вы можете получить к нему доступ в любом месте вашего кода, и от его использования зависит множество функций. Что, если бы вы могли ограничить модификации только основными функциями WordPress, но чтение было бы разрешено всем? Скрытие или инкапсуляция глобальной переменной post
в классе и построение вокруг нее интерфейса сделали бы это возможным.
Это лишь очень базовое описание ООП и того, как его можно использовать в современной разработке WordPress. Для дальнейшего изучения я настоятельно рекомендую электронную книгу Карла Александера «Откройте для себя объектно-ориентированное программирование с использованием WordPress», которая содержит наиболее полный и полезный контент по теме ООП в WordPress.
Важно помнить, что ООП — это не панацея: плохой код можно написать с помощью ООП так же легко, как и с любой другой парадигмой программирования.
Давайте углубимся в некоторые конкретные советы по использованию PHP для разработки WordPress.
Современная передовая практика PHP № 1: ориентируйтесь на PHP 7.0+
Для использования современных функций PHP требуется современная версия PHP. Просто нет смысла поддерживать версии PHP ниже 7.0. Даже ядро WordPress потребует PHP 7.0 уже в конце 2019 года.
Тем не менее, рекомендуется проверить минимальную версию, чтобы избежать «белого экрана смерти» в несовместимых средах. Фрагмент ниже показывает использование заголовка плагина для объявления минимальной версии PHP с защитным условием в коде.
<?php /** * Plugin Name: My Awesome Plugin * Requires PHP: 7.0 */ // bails if PHP version is lower than required if (version_compare(PHP_VERSION, '7.0.0', '<')) { // add admin notice here return; } // the rest of the actual plugin here
Современная передовая практика PHP № 2: Принятие отраслевых стандартов PHP (Руководство по стилю кодирования PSR-2)
PSR — это рекомендации, опубликованные PHP Framework Interop Group. Они являются отраслевыми стандартами де-факто в любом современном рабочем процессе PHP, и можно с уверенностью сказать, что PHP-сообщество в целом следует этим стандартам. PSR-2 — это рекомендация, описывающая стиль кодирования. Популярные фреймворки PHP, такие как Symfony и Laravel, следуют PSR-2.
Почему вы должны использовать PSR-2 вместо стандарта кодирования WordPress? Главным образом потому, что стандарты WordPress устарели и не используют никаких новых языковых функций. Это понятно, потому что ядро WordPress должно следовать собственным стандартам. До недавнего времени он должен был поддерживать PHP 5.2, а PSR-2 не совместим с PHP 5.2.
Это может быть неочевидно, но нет необходимости использовать стандарты кодирования WordPress, если вы не привержены ядру. Не будет проблем с отправкой плагина, соответствующего стандарту PSR-2, в каталог плагинов WordPress. На самом деле, есть несколько очень веских аргументов в пользу этого.

Современная передовая практика PHP № 3: использование механизма шаблонов PHP
PHP не является шаблонизатором. Он начинался как один, но затем превратился в полнофункциональный язык программирования, и нет причин продолжать использовать его для создания шаблонов. Двумя самыми популярными механизмами шаблонов для PHP являются Twig и Blade, которые используются Symfony и Laravel соответственно. В этой статье мы будем использовать Twig в качестве примера шаблонизатора. однако Blade имеет сопоставимые характеристики и функциональность. Я умоляю вас изучить оба и решить для себя, что подходит вам лучше всего.
В приведенном ниже примере сравнивается шаблон PHP и соответствующий ему шаблон Twig. Отображение и экранирование вывода особенно многословны в примере с PHP:
foreach ( $options as $option ) { ?> <tr class="form-field"> <th scope="row"> <label for="<?php echo esc_attr( $option->option_name ); ?>"> <?php echo esc_html( strtolower( $option->option_name ) ); ?> </label> </th> </tr> <?php } // End foreach
В Twig это более лаконично и читабельно:
{% for option in options %} <tr class="form-field"> <th scope="row"> <label for="{{ option.option_name }}"> {{ option.option_name }} </label> </th> </tr> {% endfor %}
Основными преимуществами Twig являются:
- Читаемый и лаконичный синтаксис
- Автоматическое экранирование вывода
- Расширение шаблона через наследование и блоки
Что касается производительности, Twig компилируется в шаблоны PHP и практически не требует дополнительных затрат. Twig имеет только подмножество языковых конструкций PHP, ограниченное только шаблонами. Это вынуждает разработчиков удалять бизнес-логику из шаблонов, тем самым обеспечивая разделение задач.
Существует даже Twig для WordPress. Он называется Timber, и это отличный способ начать создавать лучшие шаблоны. Стартовая тема Timber — прекрасный пример организации тем ООП.
Современная лучшая практика PHP № 4: используйте Composer
Composer — это менеджер зависимостей для PHP. Это инструмент, который позволяет объявлять библиотеки, которые использует проект, а затем автоматизировать их загрузку, установку и обновление. Затем вам просто нужно включить файл автозагрузки Composer vendor/autoload.php
вместо того, чтобы вручную запрашивать каждую библиотеку.
Плагины и темы WordPress не часто используют сторонние библиотеки. Отчасти это связано с тем, что WordPress имеет обширный API, который удовлетворяет практически любые потребности, а отчасти из-за возможных конфликтов версий. Рассмотрим два плагина, требующие одной и той же библиотеки PHP, но разных ее версий. Плагин, который запускается первым, получает правильную версию, и второй плагин также получает эту версию. Вполне возможно, что это еще одна ситуация белого экрана смерти.
Во избежание конфликтов управление зависимостями следует использовать на уровне приложения, т. е. сайта WordPress в целом. Это то, что делает Roots (точнее, Bedrock). При использовании на уровне пакета (плагина или темы) Composer может вызывать конфликты при использовании сторонних библиотек. Это известная проблема. Единственное решение, которое существует до сих пор, — это переименование пространств имен этой внешней библиотеки во что-то уникальное, и это нетривиальная задача.
Однако у Composer все еще есть применение: автозагрузка ваших собственных классов. Но прежде чем мы пойдем дальше с автозагрузкой, нам нужно освоиться с пространствами имен PHP.
Современная передовая практика PHP № 5: использование пространств имен
Ядро WordPress, являющееся устаревшим проектом, использует глобальное пространство имен или, другими словами, вообще не использует пространство имен. Любые классы или функции, объявленные глобально (то есть не внутри другого класса или функции), видны в любом месте всей кодовой базы. Их имена должны быть уникальными не только в пределах вашей кодовой базы, но и для всех плагинов и тем, которые используются сейчас или могут быть использованы в будущем.
Коллизия имен (например, объявление функции с уже существующим именем) обычно приводит к белому экрану смерти, а мы этого не хотим. Кодекс WordPress рекомендует ставить перед всеми вашими функциями и классами что-то уникальное. Таким образом, вместо простых имен классов, таких как Order
, мы получаем что-то вроде Akrte_Awesome_Plugin_Order
где «Akrte» — уникальный префикс, который я только что придумал.
Пространства имен можно рассматривать как группы — или папки, если использовать аналогию с файловой системой, — что помогает организовать код и избежать конфликта имен. У вас могут быть сложные пространства имен, разделенные косой чертой, как вложенные папки. (В частности, в пространствах имен PHP используется обратная косая черта.)
Эти части пространства имен называются подпространствами имен. В нашем примере класс Akrte_Awesome_Plugin_Order
будет называться Akrte\Awesome_Plugin\Order
, если использовать пространства имен. Здесь Akrte
и Awesome_Plugin
являются частями пространства имен (или подпространствами имен), а Order
— именем класса. Затем вы можете добавить оператор use
и впоследствии использовать только имя класса. Однозначно лучше выглядит:
use Akrte\Awesome_Plugin\Order; $a = new Order;
Очевидно, что пространства имен должны быть уникальными; таким образом, мы должны дать первому «корневому» подпространству имен уникальное имя, и это обычно имя поставщика. Например, класс WooCommerce WC_REST_Order_Notes_V2_Controller
можно переделать с такими пространствами имен:
namespace WooCommerce\RestApi\V2\Controllers; class OrderNotes {}
Кодовая база WooCommerce в настоящее время использует пространства имен; например, в WooCommerce REST API версии 4.
Современная лучшая практика PHP № 6: используйте автозагрузчик
В большинстве рабочих процессов PHP обычным способом связывания файлов PHP является использование операторов require
или include
. По мере роста проекта вы получаете десятки операторов require
в своем основном файле плагина. Автозагрузчик автоматизирует включение файлов и делает это только по мере необходимости. Технически это функция, для которой require
SA-файл, содержащий класс или функцию, когда он впервые встречается в коде. Больше нет необходимости добавлять какие-либо операторы require
вручную.
Часто также наблюдается значительный прирост производительности, поскольку автозагрузчик загружает только те модули, которые используются в конкретном запросе. Без автозагрузчика включается вся ваша кодовая база, даже если запрос использует, скажем, только 10 процентов вашего кода.
Функция автозагрузчика должна знать, в каких файлах живут ваши классы и функции. И для этого существует стандарт PHP-FIG, PSR-4.
В нем говорится, что часть пространства имен, префикс, предназначена для соответствия базовой папке. Подпространства имен, которые следуют за ним, соответствуют папкам внутри базовой папки. Наконец, имя класса соответствует имени файла. Примеру класса Akrte\AwesomePlugin\Models\Order
потребуется следующая структура папок:
/awesome-plugin awesome-plugin.php /includes /Models Order.php
Префикс пространства имен includes
Akrte\AwesomePlugin\
соответствует папке include, как указано в конфигурации автозагрузчика, описанной ниже. Подпространство имен Models
имеет соответствующую папку Models
, а класс Order
содержится в Order.php
.
К счастью, нет необходимости самостоятельно реализовывать функцию автозагрузчика. Composer может создать для вас автозагрузчик:
- Установить Композитор
- Создайте файл
composer.json
в корневой папке вашего проекта. Он должен содержать такие строки:
{ "name": "vendor-name/plugin-name", "require": {}, "autoload": { "psr-4": { "Akrte\\AwesomePlugin\\": "includes/" } } }
- Запустите
composer install
. - Включите
vendor/autoload.php
в начало вашего основного PHP-файла плагина следующим образом:
<?php /** * Plugin Name: My Awesome Plugin */ defined('ABSPATH') || exit; require __DIR__ . '/vendor/autoload.php'; // do stuff
Помимо пространств имен, последняя кодовая база WooCommerce также использует автозагрузчик Composer.
Ознакомившись с этими принципами проектирования PHP, пришло время связать все наши уроки PHP с настройкой серверной части WordPress с одной последней рекомендацией.
Современная передовая практика разработки WordPress № 4: рассмотрите возможность использования корневого стека
Roots — это наиболее полный современный рабочий процесс разработки WordPress. Однако я бы сказал, что его не обязательно использовать в каждом проекте WordPress, потому что:
- Корни должны быть использованы с самого начала. Самая распространенная причина, на самом деле. Рефакторинг существующего проекта был бы слишком дорогостоящим.
- Это самоуверенно. Хорошо, когда ты соглашаешься, плохо, когда нет. Например, вы можете предпочесть другие способы организации темы. Проекты с собственным мнением также требуют времени, чтобы изучить «свои пути».
- Не все это знают. Разработчик, который придет после вас, чтобы поддерживать сайт, который вы создали с помощью стека Roots, может понятия не иметь, что это такое, и задаваться вопросом, что случилось с папками WordPress. Мы должны думать о наших коллегах-разработчиках WordPress.
В общем, вы хотели бы полностью понять все преимущества и недостатки любого сильно самоуверенного проекта, прежде чем браться за его использование.
Современные принципы PHP и программного обеспечения: обеспечение надежной серверной разработки WordPress
Совершенно очевидно, что не существует единственно правильного способа написания программного обеспечения. Такие концепции, как разделение интересов, существуют десятилетиями; однако то, что это означает практически, всегда оспаривалось. Возьмем, к примеру, CSS. Вначале мы встроили его как style
в HTML, затем решили, что отдельные листы CSS — это то, что касается разделения задач.
Перенесемся на десятилетие вперед: современные приложения JavaScript используют компоненты в качестве реализации разделения интересов. Фронтенд-разработчики тяготеют к CSS-in-JS, а это, по сути, означает повторное встраивание CSS в HTML (ну, это не так просто, но вы поняли идею). Круг завершен!
Лучшие практики всегда были направлены на улучшение опыта разработчиков:
Программы должны быть написаны для того, чтобы их могли читать люди, и лишь случайно для того, чтобы машины выполняли их.
Абельсон и Сассман, Структура и интерпретация компьютерных программ
Некоторые приемы из этого руководства по PHP WordPress можно быстро и легко реализовать в вашем проекте. Например, автозагрузчик: сделайте это один раз для каждого проекта и просто наслаждайтесь. С другой стороны, новые идеи архитектуры программного обеспечения требуют времени, практики и многочисленных итераций, чтобы освоиться и освоиться. Однако награды намного больше. Вы будете не только более эффективны в том, что делаете, но и получите больше удовольствия от того, что делаете. И регулярное удовольствие от работы, которую вы выполняете для клиентов, — это, пожалуй, единственный способ, которым она может быть устойчивой.