Хабрахабр

Вышел PHP 7.4! Как Badoo переходит на новую версию

Сегодня, наконец, опубликован релиз PHP 7.4!

Это стрелочные функции, типизированные свойства классов и ещё много всякого синтаксического сахара. Его новые фичи уже были многократно описаны, в том числе и на Хабре. 4 не только появился preload, но и сам PHP стал значительно быстрее. Но больше всего мы ждали новый релиз из-за производительности: в версии 7.

4 прекращается активная поддержка PHP 7. Плохая (или хорошая?) новость — с выходом PHP 7. Его последний релиз запланирован на середину декабря. 2. 4, а недавно активно занялись переходом на него, так как сейчас мы на уже почти не поддерживаемой версии 7. Мы давно проводим эксперименты с PHP 7. 2.

А ниже расскажу немного о том, как мы переходим на новую версию.
Несмотря на то, что у нас огромная кодовая база, мы живём на PHP уже 13 лет. Поздравляю всех с долгожданным релизом! Нам неоднократно приходилось переходить на новые версии, и процесс перехода хорошо отработан. 

Если сильно упростить, то можно выделить несколько шагов:

  • Добиваемся того, что unit-тесты начинают успешно проходить на новой версии.
  • Делаем обязательными прогоны тестов на новой версии для всех изменений кода (чтобы не пришлось повторять пункт 1, так как новый код пишется постоянно, и он может быть снова несовместимым).
  • Переключаем на новую версию девел-площадку, чиним проблемы и живём какое-то время в таком состоянии.
  • Повторяем это для стейджинга.
  • Плавно выкладываем на разные кластеры продакшена.

Наши правки в репозитории PHP

Проблема с preload (фикс)

В этот раз в процессе кое-что изменилось: так как мы очень ждали preload, начали проводить часть работы еще в июле, во время версии 7.4.0beta1. В итоге это вылилось для нас в достаточно большое количество времени, потраченного на отладку, ведь PHP 7.4 тогда был совсем сырым. Но с другой стороны, в результате мы нашли неприятный баг, починили его и отправили фикс в апстрим, чем помогли всему сообществу.

Дальше настало время заняться тестами.

Проблема с доступом к приватным свойствам (фикс)

Для того, чтобы вообще прогнать тесты, нужно обновить PHPUnit, SoftMocks и PHP-Parser как их часть. У нас большая кодовая база, и даже для обновления PHPUnit потребовалось переписать огромное количество тестов.

Было много падений со следующей ошибкой: После того, как нам удалось прогнать тесты, мы увидели очень странное.

PHP Fatal error: Cannot access private property ClassLoader::$classMap in vendor/composer/ClassLoader.php

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

Долгий дебаг при помощи gdb показал, что действительно в EG(fake_scope) по каким-то причинам оказывается не тот класс, в рамках которого происходит обращение к свойству, а другой, не связанный с ним. Проблема осложнялась тем, что воспроизводилась нестабильно.

3 (скорее всего, после вот этого изменения), весь мир жил с ней год и никому до нас она не мешала. После того, как мы нашли минимальный репродьюс-кейс (который, на минуточку, требует наличия трёх классов, автолоадера и Reflection), починили причину проблемы и запушили исправление в апстрим, оказалось, что эта проблема существовала ещё со времён PHP 7.

Правим несовместимости кода Badoo

Сейчас мы правим все несовместимости нашего кода с PHP 7.4. Подавляющее большинство несовместимостей для нас (больше сотни мест, >80% от всех несовместимостей) было вызвано добавлением ошибки «Trying to access array offset on value of type null/bool/int» (соответствующий RFC). Она возникает при использовании синтаксиса обращения к элементу массива на других типах данных. 

Проблему хорошо показывает вот такой пример:

$a = false;
var_dump($a[‘somekey’]); // PHP 7.3: // NULL
// // PHP 7.4:
// Notice: Trying to access array offset on value of type bool in Command line code on line 1
// NULL

Навскидку кажется, что такого в реальном коде не должно встречаться, но, как показала практика, это достаточно частый случай: например, функция может возвращать массив в нормальном случае и false/null в случае ошибки, а дальше вверх по стеку информация о false/null теряется, и этот случай не обрабатывается отдельно. 

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

Кстати, в настоящий момент информации о нём нет в release notes или upgrading guide. Второе по количеству доставленных проблем обновление — это изменение в работе method_exists(). Суть его заключается в следующем:


class A1 } class B1 extends A1 {} var_dump(method_exists(B1::class, 'priv')); // PHP 7.3: bool(true)
// PHP 7.4: bool(false)

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

Но в сумме затраты на адаптацию ко всем этим изменениям не сравнятся со случаем использования синтаксиса массивов на не-массивах. Конечно, мы в разной степени сталкиваемся и с другими несовместимостями, в том числе с многочисленными изменениями, связанными с рефлексией (пример один, пример два), с изменениями в hexdec() и подобных, запретом array_key_exists() даже для ArrayAccess-объектов, с несовместимостями в различных бибилотеках зависимостей, подключаемых через Composer, и даже со всякими экзотическими штуками вроде ставшим обязательным stream_set_option() для stream wrapper’ов при include’ах.

4. В настоящий момент мы закончили работу с юнит-тестами: они полностью проходят на PHP 7. Ведём работу над API-тестами и до конца года планируем начать переключение различных кластеров и окружений.

4? Этой краткой заметкой хочу пригласить к обсуждению: пробовали ли вы уже PHP 7. Собираетесь ли переходить? Если да, то каким был ваш опыт?

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»