Legacy PHP · 3 мин чтения
С PHP 5 на PHP 8, не останавливая магазин
Две версии PHP рядом на одном сервере, пока код переезжает часть за частью.
На этой странице
Письмо от хостинговой компании вежливое и окончательное: PHP 5.6 отключат в конце квартала. Магазин работает на коде 2012 года, никого из авторов рядом нет, а заказы приходят каждый день. Переключить версию и надеяться — не план. Переписать всё с нуля — план, но обычно плохой.
Вот как это делаем мы: обе версии работают рядом, а код переезжает небольшими частями, каждая проверена отдельно.
Почему бы просто не переключить?
Потому что многое из того, что PHP 5 принимал, PHP 8 отвергает, и обычно фатальной ошибкой, а не предупреждением. Обычные подозреваемые:
| Что делает старый код | Что с этим стало |
|---|---|
mysql_query() и остальные mysql_* |
удалены в PHP 7.0; используйте PDO или mysqli |
ereg(), split() |
удалены в PHP 7.0; используйте preg_* |
each(), create_function() |
удалены в PHP 8.0 |
| конструктор с именем класса | игнорируется с PHP 8.0; объект создаётся без него |
0 == "abc" равно true |
с PHP 8.0 это false, что незаметно меняет некоторые if |
| свойства, добавленные объектам на лету | устарело с PHP 8.2 |
null, переданный в strlen(), trim() и подобные |
устарело с PHP 8.1 |
Первые три останавливают сайт. Последние четыре хуже: сайт продолжает работать, но делает немного не то.
Шаг 1: копия и карта
На рабочем сервере ничего не происходит. Мы берём копию кода и анонимизированную копию базы данных, разворачиваем их на тестовом сервере и читаем код. Здесь два инструмента экономят дни:
- PHPCompatibility, набор правил для PHP_CodeSniffer, показывает каждую строку, которая сломается на определённой версии PHP.
- Rector автоматически переписывает многие из этих строк, например старый синтаксис массивов или удалённые функции.
Ни один из них не заменяет чтения. Они дают карту; маршрут выбирает человек. Результат этого шага — письменный список: что ломается, где, насколько это рискованно и с чего начать.
Шаг 2: страховка
Прежде чем что-то менять, мы убеждаемся, что заметим, если что-то сломается:
- резервные копии, которые мы действительно один раз восстановили на тестовом сервере;
- короткий список сценариев, которые приносят деньги: поиск, корзина, оформление заказа, письмо со счётом, выгрузка в админке;
- проверка для каждого сценария: автоматическая там, где это дёшево, и записанная там, где нет.
Шаг 3: две версии на одном сервере
На сервере семейства Red Hat (AlmaLinux, Rocky, RHEL) репозиторий Remi устанавливает несколько версий PHP рядом, каждую со своей службой php-fpm. Дальше nginx направляет каждую часть сайта на ту версию, к которой она готова:
# новая, проверенная часть работает на PHP 8
location ^~ /checkout/ {
fastcgi_pass unix:/var/opt/remi/php85/run/php-fpm/www.sock;
include fastcgi.conf;
}
# всё остальное пока работает на PHP 5.6
location ~ \.php$ {
fastcgi_pass unix:/opt/remi/php56/root/var/run/php-fpm/www.sock;
include fastcgi.conf;
}
Пути к сокетам зависят от настроек каждого пула; главное — сама идея. Сайт всё время работает, а каждая часть переезжает, когда готова, а не когда так говорит календарь.
Шаг 4: небольшие шаги
По одной части: преобразовать, проверить на тестовом сервере, выпустить в тихий час, сутки следить за журналами. Потом следующая часть. Обычно первым идёт слой работы с базой данных, потому что от него зависит почти всё и потому что именно в mysql_* живут дыры в безопасности:
// до
$res = mysql_query("SELECT * FROM orders WHERE id=" . $_GET['id']);
// после
$stmt = $db->prepare('SELECT * FROM orders WHERE id = ?');
$stmt->execute([(int) $_GET['id']]);
Когда переехала последняя часть, PHP 5.6 отключают, и никто этого не замечает. Это и есть цель.
Может, лучше переписать?
Редко. Старый код некрасивый, но в нём годы исправленных ошибок и бизнес-правил, которые никто не записал. Переписывание выбрасывает всё это, и его приходится находить заново, обычно уже на рабочем сайте. Мы предлагаем переписать, только когда карта из шага 1 показывает, что так дешевле, и тогда говорим об этом письменно.
Сколько это займёт?
Это зависит от объёма кода и от того, сколько сюрпризов он прячет, а именно это и выясняет шаг 1. Поэтому мы начинаем с него: небольшой аудит по фиксированной цене, который заканчивается планом и ценой за остальное.
Если письмо от хостинговой компании уже пришло, отправьте нам запрос.
Хотите так же у себя?
Два вопроса и адрес эл. почты. Отвечаем в течение двух рабочих дней, обычно быстрее.