Отправить запрос

Legacy PHP · 3 мин чтения

С PHP 5 на PHP 8, не останавливая магазин

Две версии PHP рядом на одном сервере, пока код переезжает часть за частью.

На этой странице
  1. Почему бы просто не переключить?
  2. Шаг 1: копия и карта
  3. Шаг 2: страховка
  4. Шаг 3: две версии на одном сервере
  5. Шаг 4: небольшие шаги
  6. Может, лучше переписать?
  7. Сколько это займёт?

Письмо от хостинговой компании вежливое и окончательное: 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. Поэтому мы начинаем с него: небольшой аудит по фиксированной цене, который заканчивается планом и ценой за остальное.

Если письмо от хостинговой компании уже пришло, отправьте нам запрос.

Хотите так же у себя?

Два вопроса и адрес эл. почты. Отвечаем в течение двух рабочих дней, обычно быстрее.

Отправить запрос →