Безопасное использование PHP-зависимостей в экосистеме WordPress

Click here to view original web page at oddstyle.ru

Автор:

Дата публикации:

Плагины и темы из репозитория WordPress.org устанавливаются в виде автономных пакетов; эти пакеты должны содержать в себе весь код, файлы и зависимости, необходимые для их правильного функционирования. Централизованной системы с обзором всех зависимостей, используемых в темах и плагинах WP, не существует, а потому приходится создавать свою собственную безопасную сеть зависимостей.

Зависимости в WordPress

Разработчики не должны заново изобретать колесо при создании своих решений. Чтобы отойти от повторения рутинных действий в программировании, разработчики создали библиотеки инструментов на разных языках. Эти инструменты носят название «зависимостей». Все они поставляются в формате общего решения – менеджера зависимостей. Для PHP таким менеджером зависимостей является, к примеру, Composer.

Как уже упоминалось ранее, в WordPress нет централизованного способа управления всеми зависимостями, что может вести к определенным проблемам. Зависимости имеют свои версии, что позволяет эффективно управлять ими и осуществлять полный контроль над функционалом. Composer позволяет вам определять, с какой версией (или с каким диапазоном версий) зависимости совместим ваш код. Менеджер зависимостей будет устанавливать и использовать именно ту версию, которая полностью соответствует выставленному ограничению.

Проблема

Представьте, что на сайте с WP установлено два плагина, плагин А и плагин B. Они оба используют зависимость «great/dependency». Плагин А требует для работы версию 1.0 этой зависимости, а плагин B требует версию 3.0, более свежую. В результате мы имеем следующую проблему: функции, классы или константы заданы в обеих версиях, но имеют разный функционал. В PHP вы можете определить только класс и таким образом загрузить лишь одну версию. Ваш код должен получить класс от версии 1.0, но из-за последовательности загрузки в качестве первого активного класса может использоваться тот, что задан в версии 3.0.

Решение

Есть два способа решения этой проблемы.

  1. Использовать Composer как инструмент централизованного управления зависимостями. Это позволит вам управлять всеми зависимостями во всей установке WordPress.
  2. Убедиться в том, что каждый плагин имеет ряд зависимостей с уникальными именами для всех используемых классов, функций или констант (т.е. Yoast_Data_Search вместо Data_Search).

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

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

Второе решение – преобразование зависимостей, которые вы используете, в варианты, которые имеются у вас в коде. Эту часть я рассмотрю подробнее далее.

Реализация, существующая в Yoast SEO, полностью совместима с обоими путями решения проблемы и позволит вам минимизировать затраты сил, времени и средств.

Почему мы решили сделать это?

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

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

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

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

Как мы решили эту проблему?

Реализация затрагивает три разных среды:

  • Выпуск плагина для Composer
  • Выпуск плагина на WordPress.org
  • Разработка

Обратите внимание, что вам потребуется PHP 5.6, чтобы эта функциональность работала. В WordPress минимальная версия недавно была повышена до 5.6, потому мы настоятельно рекомендуем обернуть код, использующий эти зависимости, в условие с проверкой версии PHP.

Выпуск в виде пакета для Composer

Мы добавили требуемые зависимости в нашу Composer-конфигурацию:

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

Релиз на WordPress.org

Чтобы подготовить пакет, который будет выпущен на WordPress.org, мы создаем артефакт плагина, развертываемый в репозитории WordPress.org.

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

Для JS это означает минимизированные версии файлов, для CSS – скомпилированные версии вместо исходных (Sass) файлов. Для зависимостей это означает уникальные вариации классов, функций и констант внутри них.

Используемый инструментарий:

  • Webpack – создает JS-файлы и минимизирует их.
  • SASS – создает CSS-файлы.
  • Humbug PHP Scoper – модифицирует зависимости так, чтобы они были уникальными.

Задание префиксов для кода: PHP Scoper

PHP Scoper позволяет выполнять модификации кода. Он создан для того, чтобы «задавать префиксы для всех пространств имен PHP в файле/каталоге, чтобы изолировать код, связанный в PHAR».

PHAR – исполняемые файлы PHP, которые существуют в экосистеме без глобальной системы управления зависимостями.

PHP Scoper позволяет вам решить следующие задачи:

  • Задание префикса для всех существующих пространств имен с использованием вашего собственного пространства.
  • Задание префикса для всех глобальных элементов пространства имен с использованием пространства имен.
  • Задание префикса для всех констант.
  • Выполнение модификаций кода (поиск/замена). Поскольку некоторые из имен классов используются в строках или создаются динамически, PHP Scoper позволяет добавить произвольную конфигурацию для обработки этих случаев. Вы можете рассмотреть нашу конфигурацию Idiorm как пример.

Создание артефакта состоит из следующих шагов:

Исключение: интерфейсы PSR

Мы будем использовать префиксы для любых классов, интерфейсов и констант, с которыми поставляются зависимости, за исключением только PSR-интерфейсов. Это позволит разработчикам сайтов, тем и плагинов легко интегрироваться со своими произвольными разработками на базе отраслевых стандартов. В настоящее время мы используем только интерфейс PSR-3 Logger Interface. В будущем мы можем использовать и другие интерфейсы.

Разработка плагинов

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

Чтобы код работал в двух ситуациях (с Composer и без него), мы динамически добавляем PHP Alias’ы (псевдонимы) для классов и интерфейсов, которые начинаются с определенного префикса. Это позволяет нам всегда ссылаться на префиксную версию класса в коде, даже если загружен вариант без префикса (через Composer).

Как реализовать префиксные зависимости в плагине WordPress

Требования к заданию префиксов в PHP-зависимостях в вашем плагине или вашей теме следующие:

  1. Установка Composer. Вам нужен будет Composer, чтобы управлять зависимостями в процессе разработки. Для этого надо установить Composer, создать конфигурационный файл composer.json, и установить необходимые зависимости.
  2. Конфигурирование PHP Scoper. Вы сообщаете, какие классы должны иметь префикс и как именно это будет осуществляться:
    01php ./vendor/humbug/php-scoper/bin/php-scoper add-prefix --prefix={your prefix} --output-dir=./vendor_prefixed/{dependency} --config={configuration file}
  3. Создание псевдонимов (алиасов) для префиксных классов и интерфейсов. Нужно это, чтобы ваш код работал с сайтом, управляемым Composer, а также с автономным zip-архивом. Посмотрите код, который мы используем в Yoast SEO, чтобы сделать это.
  4. Создание артефакта.
    • Используйте Composer для установки всех зависимостей.
    • Выполните скрипт для задания префикса к зависимостям.
    • Удалите prefix-dependencies из конфигурации Composer. Не трогайте composer.lock или composer.json.
    • Выполните composer install для установки тех зависимостей, которые будут поставляться вами.
    • Скопируйте папки vendor_prefixed и vendor в папку artifact
    • Создайте артефакт для публикации на WordPress.org или установки на сайте WordPress вручную.

В Yoast SEO мы используем Grunt для автоматизации этих задач. Мы можем просто выполнить grunt artifact, чтобы создать установщик плагина, который будет отражать состояние плагина так же, как и в момент его выпуска.

Оптимизация добавления префиксов к зависимостям

Поскольку выполнение скрипта prefix-dependencies требует времени, мы будем запускать его только тогда, когда зависимости отсутствуют и необходимы. Мы создадим файл dependencies-prefixed.txt в папке vendor_prefixed, чтобы проверять наличие зависимостей.

Необычное поведение конфигурации Composer

Модификация composer.json и composer.lock для удаления зависимостей с префиксами – шаг, который является рискованным и требует некоторой очистки. Мы рассмотрели альтернативный вариант с копированием composer.json и composer.lock в папку artifact, выполнив затем операции по подготовке папок vendor и vendor_prefixed в данной локации. Файлы Composer в среде разработки мы оставили нетронутыми. Поскольку artifact не содержит конфигураций Composer, эти файлы необходимо полностью удалить после завершения работы над зависимостями. Этот подход усложняет тестирование использования зависимостей с префиксом и без него во время разработки.

Источник: developer.yoast.com