Его новые фичи уже были многократно описаны, в том числе и на Хабре. Это стрелочные функции, типизированные свойства классов и ещё много всякого синтаксического сахара. Но больше всего мы ждали новый релиз из-за производительности: в версии 7.4 не только появился preload, но и сам PHP стал значительно быстрее.
Плохая (или хорошая?) новость — с выходом PHP 7.4 прекращается активная поддержка PHP 7.2. Его последний релиз запланирован на середину декабря. Мы давно проводим эксперименты с PHP 7.4, а недавно активно занялись переходом на него, так как сейчас мы на уже почти не поддерживаемой версии 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) по каким-то причинам оказывается не тот класс, в рамках которого происходит обращение к свойству, а другой, не связанный с ним.
После того, как мы нашли минимальный репродьюс-кейс (который, на минуточку, требует наличия трёх классов, автолоадера и Reflection), починили причину проблемы и запушили исправление в апстрим, оказалось, что эта проблема существовала ещё со времён PHP 7.3 (скорее всего, после вот этого изменения), весь мир жил с ней год и никому до нас она не мешала.
Правим несовместимости кода 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: оно позволяет найти много потенциальных ошибок в коде.
Второе по количеству доставленных проблем обновление — это изменение в работе 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)
Эту особенность, опять же, сложно встретить в реальном коде. Но, как оказалось, мы ненамеренно активно эксплуатировали это в тестах.
Конечно, мы в разной степени сталкиваемся и с другими несовместимостями, в том числе с многочисленными изменениями, связанными с рефлексией (пример один, пример два), с изменениями в hexdec() и подобных, запретом array_key_exists() даже для ArrayAccess-объектов, с несовместимостями в различных библиотеках зависимостей, подключаемых через Composer, и даже со всякими экзотическими штуками вроде ставшим обязательным stream_set_option() для stream wrapper’ов при include’ах. Но в сумме затраты на адаптацию ко всем этим изменениям не сравнятся со случаем использования синтаксиса массивов на не-массивах.
В настоящий момент мы закончили работу с юнит-тестами: они полностью проходят на PHP 7.4. Ведём работу над API-тестами и до конца года планируем начать переключение различных кластеров и окружений.
Этой краткой заметкой хочу пригласить к обсуждению: пробовали ли вы уже PHP 7.4? Если да, то каким был ваш опыт? Собираетесь ли переходить?
Комментарии (24)
stanislav-belichenko
28.11.2019 14:03+1Этой краткой заметкой хочу пригласить к обсуждению: пробовали ли вы уже PHP 7.4? Если да, то каким был ваш опыт? Собираетесь ли переходить?
Да, будем переходить. Нет, еще не пробовали, так как пока что в докере даже нет образа под данную версию языка для наших нужд.
А вообще, учитывая, что мы живем со строгим режимом типизации, очень ожидаемой фичей будет типизация свойств классов. Давно пора. Ну и еще поддержка вариантов типизации вроде ?false. Раньше такое можно было только в аннотациях указать, теперь будет поддерживать сам язык.
avolver
28.11.2019 14:46+2пробовали ли вы уже PHP 7.4? Если да, то каким был ваш опыт?
Перевезлиprod
, когда вышел 7.4RC6.
Столкнулись с проблемой утечки памяти в расширенииparallel
(issue) — ждём реакции krakjoe, автора расширения и разработчика php.
Также есть проблемы с типизированными свойствами при развороте тестовой БД из Doctrine-фикстур. При получении неинициализированных lazy-объектов сущности из reference-репозитория фикстуры выходитEntity::$property must not be accessed before initialization
, при$em->flush()
.
youROCK
28.11.2019 16:04Soft Mocks-то свою задачу выполнил, позволил вам обновиться без особых проблем?
pmurzakov Автор
28.11.2019 16:49Если только не считать то, что Soft Mocks сами(и частично работу с ними) пришлось править под 7.4 :)
youROCK
28.11.2019 16:51Да, это в целом понятно :). Вопрос в том, сколько усилий для этого пришлось приложить? Легче ли это чем править расширение runkit/uopz? В github.com/badoo/soft-mocks, к сожалению, не вижу правок под PHP 7.4 пока что (возможно, не туда глядел).
GitCodeDestroyer
28.11.2019 19:34+2PHP 7.3 был итак уже очень быстрым и удобным. С Laravel — просто конфетка, но 7.4 — ТОРТ. Я уже пробовал эту версию языка на практике и показатели удивляют… Всем советую перейти на новую версию, не пожалеете!
vlreshet
28.11.2019 19:41Такой заметный прирост скорости?
polyanin
28.11.2019 22:53обновился в dev пока окружении, ускорение в среднем миллисекунд на 20. preload пока не использовался
GitCodeDestroyer
29.11.2019 16:20Вот скажем, если в ванильном PHP разница скорости запросов не велика, но в Laravel, когда я с базы экзекютю все данные, разница в скорости — где то 60-76 мс. По моему, в больших проектах каждая миллисекунда на счету. И ещё, форуме medium официально заявили, что до версии PHP-v8.x они будут апгрейдить исключительно для framework-ов. Посмотрим что из этого выйдет…
AlexanderY
29.11.2019 11:33А как у вас происходил именно переход? Вы часть серверов «отключали», обновляли и снова запускали в строй?
Как обновление происходит у маленьких компаний, кто не располагает кластером серверов, а, допустим, лишь одним php-сервером? Процесс, должно быть, быстрый. Если кто обновлялся «на живую», расскажите.vlreshet
29.11.2019 11:45Смотря на специфику проекта. Если 100%ый аптайм не требуется — можно ночью на выходных, например, «по-живому» мигрировать. Полежит минуток 20-30 сайт, да и всё. А вообще, начиная с 7-ой версии, PHP стал адекватнее располагаться по директориям, и не перезаписывать себя. То есть сейчас, если не ошибаюсь, можно при работающем 7.2 — поставить 7.4, поставить все екстеншны, а потом просто a2dismode php7.2 && a2enmod php7.4 && service apache2 restart (ну, это всё в случае с апачем). То есть даунтайм будет вообще пару секунд всего.
Tatikoma
29.11.2019 12:31nginx + php-fpm и того проще. Запускаешь отдельный инстанс php-fpm, всё настраиваешь, правишь сокет в конфиге nginx, делаешь systemctl reload nginx. Без простоя совсем.
zviryatko
30.11.2019 20:55А вы быстро рветесь в бой, предпочитаю подождать версси х.1 :)
По поводу возвращаемых значений false|null|"" хорошо спасает phpstan, проще один раз отрефакторить но потом легче обновляться будет.
OtshelnikFm
Спасибо. А замеры производительности были/будут — версия ниже и 7.4?
pmurzakov Автор
В этой статье я рассказывал про наши эксперименты. Если коротко, то на тестовом эндпоинте PHP 7.4 давал +10% в сравнении с PHP 7.2, включение preload давало ещё +10% сверху.
Сколько будет в реальности — узнаем, когда перейдём, и, возможно, поделимся результатами, если они будут интересные. :)
mirypoko
мне интересно, а делали какие нибудь замеры что было бы, если бы вы перешли на другую платформу, вроде java, asp.net core и тд в плане производительности?
Sannis
Основная проблема — как это проверить не переписывая всё на "java, asp.net core и тд".
mirypoko
Например, взять пару каких-нибудь прожорливый методов API, переписать на другие технологии и посмотреть стало лучше или нет
fiftin
А для чего им это делать? Переписывать все на Java они явно не будут, даже если окажется что Java быстрее.