Что нового

Fatal error в phpMyAdmin после обновления PHP — и причём тут вообще не phpMyAdmin

Симптом. После апгрейда PHP мой phpMyAdmin на /line3/ начал отдавать 502. В логе nginx — фатал на ровном месте:
1782490703128.png

Код:
PHP Fatal error: Declaration of Slim\Psr7\Uri::withScheme($scheme) must be compatible with Psr\Http\Message\UriInterface::withScheme(string $scheme): Psr\Http\Message\UriInterface in .../vendor/slim/psr7/src/Uri.php

Первая мысль — «битая сборка, переустановлю phpMyAdmin». Не угадал.

Почему это не баг phpMyAdmin. phpMyAdmin 5.2.3 (последний на сегодня) штатно везёт slim/psr7 1.4.2, а тот тянет psr/http-message 1.0 с нетипизированным withScheme($scheme). Внутри себя он консистентен и на голом PHP 8.5 работает нормально. Переустановка принесёт ровно тот же 1.4.2 и ровно тот же фатал. Лечить тут нечего — он жертва.

Кто настоящий виновник — opcache.preload. На том же сервере крутится XenForo, и под него у меня сделан preload, который прогревает весь его vendor — в том числе psr/http-message 2.0 с типизированным withScheme(string $scheme): UriInterface.

Ключевой момент: preload исполняется в мастере php-fpm при старте, и преложенные классы ложатся в общий OPcache, который наследует каждый воркер каждого пула этого мастера. Класс Psr\Http\Message\UriInterface уже определён по имени — поэтому когда phpMyAdmin компилирует свой старый Uri, его автолоадер для интерфейса даже не срабатывает: класс «уже есть». Только это чужой, типизированный 2.0. Старая нетипизированная реализация против него не проходит проверку сигнатур → Fatal.

PMA сам по себе цел, XenForo сам по себе цел — ломается их соседство в одном FPM-мастере.

Грабли, на которые тянет наступить. Очевидный фикс — «вынесу phpMyAdmin в отдельный FPM-пул». Не работает. preload — настройка уровня мастера (PHP_INI_SYSTEM), а не пула. Сколько пулов ни заводи под одним мастером, все они наследуют те же преложенные классы. Проверил руками: отдельный пул поднялся, запрос дошёл — и упал тем же самым фаталом.

Что реально решает. Развести их по разным мастерам. У меня уже был второй php-fpm (другой минорной версии) без этого preload — туда и увёл phpMyAdmin, поправив fastcgi_pass на его сокет:

NGINX:
location ^~ /line3/ {
    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.4-fpm.sock;   # мастер без preload XF
    }
}

Чужого 2.0-интерфейса в этом мастере нет → старый Uri компилируется против своего 1.0 → фатал уходит.

Чище — выкинуть phpMyAdmin вообще. Поставил Adminer: один PHP-файл, без Composer и без vendor, своего psr/http-message у него нет в принципе. Конфликтовать нечему — спокойно живёт рядом с XenForo на основном мастере, отдельный сокет под панель больше не нужен, а поверхность атаки в разы меньше. Доступ всё равно закрыл по IP в nginx — база смотрит в localhost, паролем тут не отделаешься.

Мораль. preload — мощная штука, но он протаскивает классы во все процессы своего мастера. Не прелоадь общие пакеты (psr/*, guzzle, symfony/*), если на том же мастере живёт что-то ещё: рано или поздно прилетит чужая мажорная версия того же интерфейса — и ты словишь этот фатал на пустом месте. Под мастером с preload держи только то приложение, ради которого preload и затевался. Всё остальное — на отдельный сокет или мастер.
Об авторе
Guru
Василий, cистемный админ /gnu/linux/windows/macos/mikrotik/troubleshooter, создатель сайта
Интересуюсь всем что делает инфраструктуру быстрой и надёжной
Открыт к общению и проектам, написать мне можно через форму или в личном сообщении

❗ Если есть пожелания по обзору какого-либо вопроса не представленного на сайте - пиши в комментариях

Комментарии

Нет комментариев для отображения.

Информация о статье

Автор
Guru
Время чтения статьи
2 мин чтения
Просмотры
2
Посл. обновление

Ещё в Работа над ошибками

Ещё от Guru

Поделиться этой статьёй

Назад
Верх