Добавить в корзинуПозвонить
Найти в Дзене
Topsite Web

Обновление Drupal до PHP 8

PHP 8 добавляет много интересных новых функций, но в то же время, будучи основной версией, ломает многие предыдущие модели поведения и функциональные возможности. Заставить Drupal работать с PHP 8 не так просто, как заставить его работать с новым второстепенным выпуском, таким как PHP 7.4. Сообщество Drupal начало планировать исправление проблем совместимости на раннем этапе. И когда выпуски начали выпускаться, возникали отдельные проблемы, связанные с каждым устареванием, измененными сигнатурами методов и другими критическими изменениями. Эти исправления вошли в одну проблему, чтобы мы могли запустить один тест для PHP 8. Это патч, с которого я начал, когда хотел протестировать Drupal 9 с PHP 8. Чтобы сделать его еще более увлекательным, я также использовал Composer 2 для все эти шаги. Зачем эта информация? Проблемы, которые я описываю здесь, более актуальны для приложений, которым необходимо поддерживать спектр версий PHP, а не одну или две. Многие проблемы могут не иметь отноше
Оглавление

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

Заставить Drupal работать с PHP 8 не так просто, как заставить его работать с новым второстепенным выпуском, таким как PHP 7.4.

Сообщество Drupal начало планировать исправление проблем совместимости на раннем этапе. И когда выпуски начали выпускаться, возникали отдельные проблемы, связанные с каждым устареванием, измененными сигнатурами методов и другими критическими изменениями. Эти исправления вошли в одну проблему, чтобы мы могли запустить один тест для PHP 8. Это патч, с которого я начал, когда хотел протестировать Drupal 9 с PHP 8. Чтобы сделать его еще более увлекательным, я также использовал Composer 2 для все эти шаги.

Зачем эта информация?

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

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

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

Окружающая среда и начальная настройка

Docker отлично подходит для создания быстрой среды для тестирования и разработки. В Topsite Web мы обычно используем Ландо для настройки проекта (на самом деле, наш инструмент шаблона проекта поддерживает создание каркаса по умолчанию для Ландо). Но здесь это не соответствовало бы моим потребностям, потому что Ландо еще не поддерживает PHP 8. В любом случае docker-compose для чего-то вроде этого намного проще. Для начала мне нужны только две службы - контейнер веб-сервера (с PHP) и база данных.

Сообщество Docker и PHP поддерживает отличную отправную точку в виде официальных образов PHP Docker в различных вариантах: CLI, FPM и с Apache on Buster. Здесь мы используем последний и добавляем различные расширения и настройки PHP, оптимизированные для Drupal. Я уже поддерживаю коллекцию оптимизированных для Drupal изображений PHP, и я адаптировал ее только для работы с образом PHP 8. Единственная разница в том, что поскольку pecl больше не включен в PHP (начиная с PHP 8), я просто удалил эти строки. Это означало, что распространенные расширения PHP, такие как APCu и YAML, не будут доступны, но это нормально для первой попытки.

Другая служба в файле docker-compose предназначена для MariaDB, и здесь я использую версию Docker от Bitnami. Есть официальный, но я больше привык к Bitnami, поскольку Ландо его использует. Я не занимаюсь сложной настройкой MariaDB, это просто для экспериментов. Помимо среды Docker, нам также потребуется настройка сайта Drupal. К счастью, это было очень просто с инструментом создания шаблонов, о котором я упоминал ранее. После установки инструмента axl-template я просто запускаю эту команду:

init-drupal topsiteweb / test-d9p8c2 --core-version "^9.2@dev"

Обычно этого было бы достаточно для хорошей отправной точки, но шаблон оптимизирован для композитора 1. Он включает в себя определенные пакеты, которые улучшают производительность композитора с Drupal. Однако я хочу использовать композитор 2, и эти пакеты не требуются. Фактически, они даже не работают. Итак, после приведенной выше команды init-drupal я удаляю этот пакет перед обновлением composer до версии 2. Я также обновляю composer-patches до последней версии dev, в которой есть обновленные операторы require для работы с composer 2.

composer remove zaporylie/composer-drupal-optimizations
composer require cweagans/composer-patches:"^1.0@dev"

Теперь я могу обновить композитор до последней версии командой

composer self-update --2

Проблема с зависимостями

Drupal построен на основе многих пакетов и компонентов в мире PHP, и они также должны поддерживать PHP 8. На данный момент большинство этих компонентов нелегко установить на PHP 8 из-за требований в их composer.json. Однако я запустил composer на моем локальном компьютере, на котором все еще работает PHP 7.4, и, соответственно, он не жаловался на PHP 8. Это, очевидно, было риском, и мы никогда не должны делать это на производственном сайте. Но для этого эксперимента я хотел попробовать запустить эти компоненты на PHP 8, несмотря на их ограничения composer.json. Хорошая новость в том, что ни один из этих компонентов пока не вызывал проблем в моих тестах.

Конечно, это блокировщик добавления поддержки PHP 8 для Drupal, и эта проблема отслеживается.

Исправить ошибки Drupal

Это исправление, по крайней мере, загрузило страницы установки Drupal, и я дошел до страницы, на которой мы вводим данные о базе данных. (В конце концов я настроил файл .env и больше никогда не видел эту страницу, но это не относится к делу.) На этом этапе я увидел ошибку, связанную с недопустимой подписью метода для метода PDO. В PHP 8 изменились сигнатуры методов PDOStatement :: fetchAll и PDOStatement :: fetch. К сожалению, в Drupal 8 и 9 есть оболочка для этого метода со старой подписью. Теперь это нужно изменить для PHP 8. Но это изменение нарушит поддержку PHP 7, и это создает наши сложности.

Решение представляет собой довольно блестящую хакерскую разработку Алекса Потта, в которой мы вводим два интерфейса - один для PHP 7, а другой - для PHP 8. В зависимости от версии PHP мы назначаем соответствующий интерфейс, который используется фактическим классом. Затем, чтобы обрабатывать обе сигнатуры методов, у нас есть две разные черты - снова одна для PHP 7, а другая - для PHP 8. Мы снова назначаем псевдоним соответствующей особенности в зависимости от версии PHP, которая используется в классе.

Это выглядит примерно так:

if (PHP_VERSION_ID >= 80000) {
  class_alias('\Drupal\Core\Database\Php8StatementInterface',  '\Drupal\Core\Database\StatementInterfaceBase');
}
else {
  class_alias('\Drupal\Core\Database\Php7StatementInterface',   '\Drupal\Core\Database\StatementInterfaceBase');
}
interface StatementInterface extends StatementInterfaceBase, \Traversable {
  // ..
}

Точно такие же черты имеют псевдонимы, и каждая из черт вызывает новый вспомогательный метод в фактическом классе Statement (они просто переименованы из предыдущего метода). Например, прежний метод fetchAll теперь станет doFetchAll, и, поскольку это другое имя, не имеет значения, какая у него подпись. Метод fetchAll теперь будет находиться в соответствующей характеристике с соответствующей сигнатурой в зависимости от версии PHP и будет просто вызывать метод doFetchAll. Таким образом, у нас есть две разные сигнатуры методов в интерфейсах и трейтах в зависимости от версии PHP!

Прежде чем я перейду к следующей проблеме, я должен отметить, что проблема здесь с PDOStatement действительно из-за ошибки в дизайне.

Мы никогда не должны напрямую зависеть от API того, что мы не можем напрямую контролировать. Версия PHP - это то, что мы, как разработчики Drupal, не можем контролировать. Конечно, нам может потребоваться минимальная версия PHP, но мы не можем контролировать PHP для управления другими зависимостями.

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

Динамические маршруты

Я был в восторге от рабочей установки PHP 8 и хотел продолжить тестирование, при этом следя за журналом ошибок. Я обнаружил несколько нишевых ошибок в поведении Drupal на динамических маршрутах, но это оказалось проблемой с изменениями в PDOStatement :: fetchAll и его поведением с необязательными параметрами. Это было очень запутанно, и я рад, что PHP 8 изменил сигнатуру метода на использование вариативных чисел. Проблема заключалась в том, как черты обернули вызов фактического метода соответствующего класса Statement. Мне пришлось использовать уродливый блок switch..case для учета количества параметров, аналогичных тому, как это уже было обработано в ядре Drupal.

Предупреждения CKEditor

Я заметил, что CKEditor регистрирует несколько предупреждений, когда открывал форму добавления / редактирования узла. Предупреждения от метода под названием CKEditorPluginManager :: getEnabledButtons, и это было связано с тем, как определенный параметр был передан в обратный вызов для array_reduce. К счастью, это оказалось несложным решением, потому что не было необходимости передавать этот параметр по ссылке, и исправление было быстро зафиксировано.

Компиляция расширений

Так же, как у вас, вероятно, не было бы сайта Drupal с хотя бы одним модулем contrib, у вас, вероятно, не было бы установки PHP без нескольких распространенных расширений. Ядро Drupal использует некоторые из этих расширений, когда они доступны (например, APCu и YAML), что обеспечивает лучшую производительность. Это означает, что даже если расширения технически не требуются, они, скорее всего, у вас есть.

Я начал с расширений, которые почти всегда устанавливаю на сайты, на которых я работаю. Это APCu, YAML и Redis. Ядро Drupal не использует Redis, но я почти всегда устанавливаю модуль Redis для кеширования, для которого требуется этот модуль. Имеет смысл проверить, работает ли он на PHP 8 (как модуль, так и расширение). Что касается двух других расширений, ядро ​​Drupal использует расширения APCu и YAML для повышения производительности, если они доступны. Опять же, рекомендуется протестировать Drupal с установленными этими расширениями.