- PVSM.RU - https://www.pvsm.ru -

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

Сегодня, наконец, опубликован релиз PHP 7.4 [1]!Вышел PHP 7.4! Как Badoo переходит на новую версию - 1

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

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

Поздравляю всех с долгожданным релизом! А ниже расскажу немного о том, как мы переходим на новую версию.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

$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: оно позволяет найти много потенциальных ошибок в коде.

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


class A1 { 
    private function priv() {} 
} 

class B1 extends A1 {} 

var_dump(method_exists(B1::class, 'priv'));

// PHP 7.3: bool(true)
// PHP 7.4: bool(false)

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

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

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

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

Автор: Pavel Murzakov

Источник [18]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/programmirovanie/338104

Ссылки в тексте:

[1] релиз PHP 7.4: https://www.php.net/archive/2019.php#2019-11-28-1

[2] описаны: https://www.php.net/manual/en/migration74.new-features.php

[3] на Хабре: https://habr.com/ru/company/funcorp/blog/454410/

[4] производительности: https://habr.com/ru/company/badoo/blog/472528/

[5] прекращается: https://www.php.net/supported-versions.php

[6] запланирован: https://externals.io/message/107849

[7] фикс: https://bugs.php.net/bug.php?id=78376

[8] фикс: https://bugs.php.net/bug.php?id=78868

[9] EG(fake_scope): https://github.com/php/php-src/blob/1a9a5f995c273b57ef1b42a8c68bcd736110b4a7/Zend/zend_globals.h#L163

[10] этого: https://github.com/php/php-src/commit/7ac06d66d4e19dc68b0440aff27f2a405354e5d6

[11] соответствующий RFC: https://wiki.php.net/rfc/notice-for-non-valid-array-container

[12] изменение: https://github.com/php/php-src/commit/6255308624a2a6bdbfa8581b9b920a8d180e458f#diff-f67c638590f0e9a686c5657200bcd82f

[13] пример один: https://github.com/php/php-src/commit/fc18f44213d59190a3e524e5c2f1a33f73be2cda

[14] пример два: https://github.com/php/php-src/commit/b964298c19c685f61542bbf30c49bd164b67c653

[15] изменениями: https://github.com/php/php-src/commit/d90cdbd9df42c3d012af23a64fb3062f0af60efa

[16] запретом: https://github.com/php/php-src/commit/0ba7c3eadf2683e4581079ea04b5b3019a5b6cce

[17] обязательным: https://github.com/php/php-src/commit/a986e70991057785cd3e5f4235215cb933351b4d

[18] Источник: https://habr.com/ru/post/477822/?utm_source=habrahabr&utm_medium=rss&utm_campaign=477822