Время идет, прогресс приносит свои плоды, каждый месяц выходят новые версии того или иного программного обеспечения. То же происходит и с языком PHP. Наша команда проекта krisha.kz решила, что уже пора совершить переход на новую версию интерпретатора. Мы поделимся опытом перехода PHP с версии 5.6 на 7.1, который обслуживает наш основной монолит.
Существует видео об устройстве этого монолита. Его особенностью является, то что он основан на фреймворке Phalcon версии 2. В связи с этим, помимо обновления самого PHP, нам нужно было поработать и над переходом на 3-ю версию Phalcon.
Собственно, сам переезд был осуществлен еще 11 октября 2017 — руки не доходили написать про это. Но, думаю, тем кто использует флакон будет интересно.
Профит
Начнем сразу с пользы которую мы получили.
На графике показан промежуток в три месяца, стрелочка указывает на момент перехода на php7.1 (11 октября 2017 г.). Голубая линия по оси Y справа показывает количество запросов. Две остальных линии по оси Y слева показывают скорость рендеринга страницы на PHP. Данные сгруппированы по дням.
Как видно, профит составляет примерно 30% по скорости отдачи страницы.
Потребление процессора также снизилось, но к сожалению графика по этим данным не сохранилось.
Помимо материальной стороны произошли и, так сказать, культурные изменения при написании кода на PHP. Было принято решение всегда объявлять тип аргумента метода и тип возвращаемого значения. Понятно, что производительность от этого не возрастет, как утверждают сами разработчики PHP7, но это позволяет писать более предсказуемый код.
PHP7 привнес новую порцию синтаксического сахара такого как “оператор объединения с null”, “оператор spaceship” и многое другое. Все эти нововведения помогают писать код проще и понятнее.
Есть и негативная сторона — вследствии обновления библиотек до последней версии, чтобы они работали на PHP7.1, пришлось отказаться от некоторой функциональности зависимых библиотек. Например, при обновлении twig до версии ~2.0 пришлось отказаться от Traceable в debugbar.
Обновление Phalcon — это боль вдвойне. Требуется не только обновить кодовую базу, но и затронуть инфраструктуру обновлением extension. Тем не менее подробное руководство по обновлению от разработчиков смягчает этот момент
Как это было
Стояла задача обновить PHP с версии 5.6 до 7.1 и Phalcon c 2.0 до 3.2.
На момент середины 2017 года PHP 7.1 показал себя стабильно и все вендоры, что мы используем, обзавелись поддержкой.
Phalcon 3.2 уверенно работал с PHP 7.1 и имелась достаточно подробная документация с примерами кода по миграции на новую версию.
Обновление двух важных компонентов системы может показаться труднореализуемым решением. Однако, проанализировав changelog Phalcon мы пришли к выводу, что не требуется глобальных изменений в коде. Обратная совместимость была нарушена не сильно. В этой задаче убить двух зайцев все же получилось.
Важным было избегать ненужного рефакторинга и прочих соблазнов, в которых можно “утонуть” растянув срок выполнения и усложнив тестирование.
Обновление PHP
Анализатор php7cc показал, что у нас имеется несколько классов, которые следует переименовать. Также используется устаревшее расширение mcrypt.
Еще обработчик ошибок нужно было научить понимать Throwable.
Классы с зарезервированными именами переименовали с постфиксом директории, в которой они находятся. Было решено пройтись и по soft-reserved-words, чтобы уменьшить правки при переходе на последующие версии.
Расширение mcrypt сменили в пользу openssl.
Было:
$ivSize = mcrypt_get_iv_size(self::MCRYPT_CIPHER, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($ivSize, self::RANDOMNESS_SOURCE);
Стало:
$ivSize = openssl_cipher_iv_length(self::OPENSSL_CIPHER);
$iv = openssl_random_pseudo_bytes($ivSize);
На этом список основных изменений по переходу на php 7.1 можно считать оконченным.
Обновление Phalcon
Более сильно код затронули изменения, связанные с переходом на Phalcon. Хоть правки носили больше структурный характер. DI и поведение валидации форм предстояло переделать под новые требования.
Изменился внутренний контекст DI, благодаря чему код стал более логичным.
Было:
$this->di->set('api', function () {
// здесь $this это текущий класс
$api = new Api($this->di->config->api->toArray());
$userCookie = $this->di->config->get('cookieId');
if ($this->di->getCookies()->has($userCookie)) {
$api->setUserId($this->di->getCookies()->getValue($userCookie));
}
return $api;
}, true);
Стало:
$this->di->set('api', function () {
// здесь $this это \Phalcon\Di
$api = new Api($this->config->api->toArray());
$userCookie = $this->config->get('cookieId');
if ($this->getCookies()->has($userCookie)) {
$api->setUserId($this->getCookies()->getValue($userCookie));
}
return $api;
}, true);
Чтобы понять, что изменилось в классах Form и Validator, мы отправились в исходный код фреймворка. Огромным плюсом является то, что zephir синтаксически похож на PHP. Благодаря этому разобраться в коде было проще и быстрее.
Вот основное изменение, которое было сделано в коде нашего проекта для работы форм:
Было:
public function appendMessage($message, $field, $type = null)
{
if (is_string($message)) {
$message = new Message($message, $field, $type);
}
if (!is_null($this->_messages) && array_key_exists($field, $this->_messages)) {
$this->_messages[$field]->appendMessage($message);
} else {
$this->_messages[$field] = new Message\Group([$message]);
}
}
Стало:
public function appendMessage($message, $field, $type = null)
{
if (is_string($message)) {
$message = new Message($message, $field, $type);
}
// Изменился способ хранения сообщений формы
if (!$this->_messages) {
$this->_messages = new Message\Group([$message]);
} else {
$this->_messages->appendMessage($message);
}
}
У нас имеются методы по работе с формами, которые стали несовместимы с текущим интерфейсом Phalcon\Forms. Сообщения теперь накапливаются в объекте Phalcon\Validation\Message\Group вместо массива. Один элемент формы теперь содержит множество сообщений валидации.
Настройка cancelOnFail поменяла свою логику, в случае ошибки валидация отменяется для всей формы, пропуская остальные элементы. Раньше процесс закачивался только для проверяемого поля и переходил к последующим полям.
Настройка окружения
На продакшене были созданы новые виртуальные бэкенды с равнозначными параметрами, правда, с другой операционной системой и обновленным nginx.
Старые:
PHP 5.6.17
php5-phalcon 2.0.8-php56~jessie
nginx 1.10
Linux version 3.16.0-4-amd64 (debian-kernel@lists.debian.org) (gcc version 4.8.4 (Debian 4.8.4-1) )
Новые:
PHP 7.1.10
php7.1-phalcon 3.2.2-1+php7.1
nginx 1.11
Linux version 4.11.0-14-generic (buildd@lcy01-08) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) )
Особых изменений в конфигурации php делать не пришлось. Серверы с кэшем (Redis) также не претерпели никаких изменений, поменяли только префикс кэша.
Так как релиз производился на новые бэкенды, дополнительной страховкой было, то что в случае фиаско можно было быстро переключиться на старые бэкенды с версией кода для php 5.6.
Переход на PHP7 не обошел и нашего CI сервера. Так как автотесты запускаются в docker-контейнере с определенной версией PHP, то мы создали образы с новыми версиями php и phalcon. К слову, вот как у нас происходит запуск тестов:
set -eux
docker pull ${bamboo.docker.base.image.php.krisha}
docker run --rm --volume $(pwd):/code --volume /etc/passwd:/etc/passwd:ro --volume /etc/group:/etc/group:ro --user $(id -u):$(id -g) --workdir /code --interactive ${bamboo.docker.base.image.php.krisha} /code/vendor/bin/robo parallel mobile
Этот код находится в таске бамбу на стадии тестирования. Тема настройки CI заслуживает отдельного освещения, поэтому мы не будем углубляться в это.
Выводы
Благодаря большому количеству автотестов в совокупности с ручным тестированием удалось избежать серьезных проблем при обновлении. Своевременность постановки задачи позволила воспользоваться опытом других проектов и обойти грабли, на которые они наступили. Работы по переходу уложились в срок 1 месяц. После, в течение недели были поправлены несколько минорных багов.
Особая благодарность comment за помощь в написании статьи и реализации перехода на PHP7.
Комментарии (11)
oxidmod
14.03.2018 11:16+1А с 7.1 еще есть смысл использовать phalcon?
SadhooKlay
14.03.2018 11:49Еще какой :)
oxidmod
14.03.2018 12:00Какой? Я так понимаю основной профит в том, что при старте приложения не нужно читать из файлов классы фреймворка. Окей, можно понять под реакт пхп и приложение вообще умирать не будет. А уже ваш код все равно лежит в обычных пхп файлах
majesty
14.03.2018 12:21Новый проект на Phalcon заводить имеет смысл только из большой любви к этому фреймворку. Но когда есть продукт, который уже на нем написан, уже работает и приносит прибыль, смысла использовать Phalcon больше, чем мигрировать на что-то другое.
SadhooKlay
14.03.2018 16:41akimserg Автор
15.03.2018 07:07Интересный пост, но кроме как «потоком сознания» назвать не могу. Если по теме — то ответ на вопрос какие минусы у фалкона и почему он больше не нужен: сам по себе фалкон по функциональности и возможностям не выделяется среди конкурентов, но при этом имеет минус в виде того самого расширения для php, которое некогда дало ему славу благодоря, котороый о нем у знали. Почему минус? Да потому, что это дополнительные затраты на установку этого расширения и отладку если что-то пошло не так, в принципе минус не существенный, но даже в таком виде уже смотрится как пятое колесо.
SOLON7
14.03.2018 14:33-1Намного интереснее было бы если бы вы рассказали какая у Вас Архитектура Используется?
Сколько у Вас Баз 100-700 ???
CQRS наверное тоже используется. Все таки попробовали бы Go Lang. У него перфоманс лучше чем у php, но хуже чем с++, с экономили бы!
Faclon имеет смысл использовать, если вы не хотите покупать новые сервера.
HVVM,falcon все это тулзы которые помогают экономить на серверных мощностях.
А там где экономия Там и профит ))
HVVM,php hack,falcon, golang это тулзы которые придумали мастадонты(*) чтобы сэкономить на серверных мощностях. Так уж исторически сложилось!
Есть разница когда серверов используется 100 или 1000.
Если аренда каждого сервера в месяц обходится 100 вечнозеленых.
WallEnd
Не было ли проблем при обновлении зависимостей и библиотек под php 7.1? И хотелось бы подробнее про docker-контейнер для автотестов! Статья супер! Спасибо!
akimserg Автор
Проблем с зависимостями не было так как прошло уже достаточно времени с выхода php 7.1 и уже все успели поправить либы под новую версию.
Контейнер используется для изолированного запуска автотестов, тут в принципе нет ничего сверхестсественного — заранее подготавливается образ с необходимой конфигурацией и затем запускается через CI