company_banner

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

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


    Его новые фичи уже были многократно описаны, в том числе и на Хабре. Это стрелочные функции, типизированные свойства классов и ещё много всякого синтаксического сахара. Но больше всего мы ждали новый релиз из-за производительности: в версии 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? Если да, то каким был ваш опыт? Собираетесь ли переходить?
    Badoo
    Big Dating

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

    Комментарии 27

      +9
      Спасибо. А замеры производительности были/будут — версия ниже и 7.4?
        +7

        В этой статье я рассказывал про наши эксперименты. Если коротко, то на тестовом эндпоинте PHP 7.4 давал +10% в сравнении с PHP 7.2, включение preload давало ещё +10% сверху.
        Сколько будет в реальности — узнаем, когда перейдём, и, возможно, поделимся результатами, если они будут интересные. :)

          –1
          мне интересно, а делали какие нибудь замеры что было бы, если бы вы перешли на другую платформу, вроде java, asp.net core и тд в плане производительности?
            +7

            Основная проблема — как это проверить не переписывая всё на "java, asp.net core и тд".

              –2
              Например, взять пару каких-нибудь прожорливый методов API, переписать на другие технологии и посмотреть стало лучше или нет
                +2
                А для чего им это делать? Переписывать все на Java они явно не будут, даже если окажется что Java быстрее.
        +6
        Этой краткой заметкой хочу пригласить к обсуждению: пробовали ли вы уже PHP 7.4? Если да, то каким был ваш опыт? Собираетесь ли переходить?

        Да, будем переходить. Нет, еще не пробовали, так как пока что в докере даже нет образа под данную версию языка для наших нужд.

        А вообще, учитывая, что мы живем со строгим режимом типизации, очень ожидаемой фичей будет типизация свойств классов. Давно пора. Ну и еще поддержка вариантов типизации вроде ?false. Раньше такое можно было только в аннотациях указать, теперь будет поддерживать сам язык.
          +6
          пробовали ли вы уже PHP 7.4? Если да, то каким был ваш опыт?

          Перевезли prod, когда вышел 7.4RC6.
          Столкнулись с проблемой утечки памяти в расширении parallel (issue) — ждём реакции krakjoe, автора расширения и разработчика php.

          Также есть проблемы с типизированными свойствами при развороте тестовой БД из Doctrine-фикстур. При получении неинициализированных lazy-объектов сущности из reference-репозитория фикстуры выходит Entity::$property must not be accessed before initialization, при $em->flush().
            +4
            Кто-нибудь пробовал в связке с недавно вышедшим Symfony 4.4lts?
              +1
              Soft Mocks-то свою задачу выполнил, позволил вам обновиться без особых проблем?
                +1

                Если только не считать то, что Soft Mocks сами(и частично работу с ними) пришлось править под 7.4 :)

                  0
                  Да, это в целом понятно :). Вопрос в том, сколько усилий для этого пришлось приложить? Легче ли это чем править расширение runkit/uopz? В github.com/badoo/soft-mocks, к сожалению, не вижу правок под PHP 7.4 пока что (возможно, не туда глядел).
                    +1

                    Мы пока в процессе, ещё не публиковали, но уже скоро.
                    Хотя зачем они тебе? :-)

                      +1
                      Мне они не нужны :). Мне просто любопытно.
                +1
                PHP 7.3 был итак уже очень быстрым и удобным. С Laravel — просто конфетка, но 7.4 — ТОРТ. Я уже пробовал эту версию языка на практике и показатели удивляют… Всем советую перейти на новую версию, не пожалеете!
                  0
                  Такой заметный прирост скорости?
                    0
                    обновился в dev пока окружении, ускорение в среднем миллисекунд на 20. preload пока не использовался
                      –2
                      Вот скажем, если в ванильном PHP разница скорости запросов не велика, но в Laravel, когда я с базы экзекютю все данные, разница в скорости — где то 60-76 мс. По моему, в больших проектах каждая миллисекунда на счету. И ещё, форуме medium официально заявили, что до версии PHP-v8.x они будут апгрейдить исключительно для framework-ов. Посмотрим что из этого выйдет…
                    0
                    А как у вас происходил именно переход? Вы часть серверов «отключали», обновляли и снова запускали в строй?

                    Как обновление происходит у маленьких компаний, кто не располагает кластером серверов, а, допустим, лишь одним php-сервером? Процесс, должно быть, быстрый. Если кто обновлялся «на живую», расскажите.
                      0
                      Смотря на специфику проекта. Если 100%ый аптайм не требуется — можно ночью на выходных, например, «по-живому» мигрировать. Полежит минуток 20-30 сайт, да и всё. А вообще, начиная с 7-ой версии, PHP стал адекватнее располагаться по директориям, и не перезаписывать себя. То есть сейчас, если не ошибаюсь, можно при работающем 7.2 — поставить 7.4, поставить все екстеншны, а потом просто a2dismode php7.2 && a2enmod php7.4 && service apache2 restart (ну, это всё в случае с апачем). То есть даунтайм будет вообще пару секунд всего.
                        +3
                        nginx + php-fpm и того проще. Запускаешь отдельный инстанс php-fpm, всё настраиваешь, правишь сокет в конфиге nginx, делаешь systemctl reload nginx. Без простоя совсем.
                      +2
                      Как вы поставляете php на сервера? Компилируете? Самосборные пакеты из спеков?
                        +2

                        Компилируем сами, потому как есть в том числе свои расширения.
                        Способ доставки даже проще — просто rsync завёрнутый в небольшую обёртку.

                          0
                          Я бы не сказал, что эта обертка такая уж небольшая :). Особенно учитывая количество времени, потраченного на его отладку.
                            0

                            Ну учитывая когда была написана большая её часть)

                        +1
                        А вы быстро рветесь в бой, предпочитаю подождать версси х.1 :)
                        По поводу возвращаемых значений false|null|"" хорошо спасает phpstan, проще один раз отрефакторить но потом легче обновляться будет.
                          +1
                          А я только перешел с 7.1 на 7.2, прирост заметил только в консольных командах — отчеты начали генерироваться заметно быстрее. Код покрыт тестами на 10% и в плане увеличения движется очень медленно…

                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                          Самое читаемое