Один рабочий день, два реальных сайта — копия и боевой. Обновление ядра до мажорной версии, починка старого компонента, приведение cookie-баннера в порядок и снижение веса страниц в десятки раз. Ниже — что именно делалось, какие ловушки встретились и что это дало.
Медицинский сайт на Joomla, общий хостинг, никакой оболочки: доступ только по FTP, командной строки нет. Обновлять ядро при этом нужно — версия 5.4.5 устарела, а переход на 6 требует подготовленной почвы.
Что усложняло работу:
exec, shell_exec, system
и passthru закрыты хостингом. Обходной путь, который спасает: proc_open
остаётся доступным — через него из PHP-скрипта запускается консоль Joomla..htaccess хостинг проигнорировал: поднять PHP
«из файлов» не получилось. План обновления нужно строить после замера версии у конкретного домена.configuration.php и понять, где ты находишься.
На мажорную версию можно перейти только с «мостового» выпуска — то есть с x.4.
С сайта ниже 5.4 обновление до 6 даже не предложат.
В 5.4 канал обновлений больше не меняет адрес сервера: адрес один, а канал передаётся меткой ветки при запросе (для «следующей» — номер мажорной версии плюс один).
# поставить канал «следующая ветка»
php cli/joomla.php core:update:channel next
# и обязательно ОПРОСИТЬ сервер обновлений
php cli/joomla.php core:update:check -v
Если переключить канал и сразу запустить обновление, команда отвечает
«You already have the latest Joomla! version. 5.4.9» и ничего не делает: очередь обновлений
в базе взята со старого канала. Лечится не повтором обновления, а одним опросом
core:update:check — после него в ответе появляется
«New Joomla Version 6.1.4 is available».
Перед мажорным обновлением Joomla проверяет условия и останавливается, если они не выполнены — при этом файлы не тронуты. Требуется, чтобы старый плагин совместимости был выключен, а новый включён. Сообщение выглядит так:
[ERROR] The 'Behaviour - Backward Compatibility' plugin is enabled.
[INFO] The pre-conditions for a major upgrade are not fulfilled.
Две детали, которые стоят времени, если о них не знать. Первая: в базе может лежать несколько записей одного плагина совместимости — следы прошлых обновлений; проверка смотрит, есть ли хоть одна включённая, поэтому выключать надо все. Вторая: вместе со старым плагином приходится выключать системный плагин старого компонента — без него не запускается даже консоль Joomla.
Пока старый плагин совместимости выключен, сайт на 5.4 отдаёт ошибку 500 на всех страницах — это не поломка правки, а ожидаемое состояние окна обновления. Предупреждать владельца нужно заранее; окно получилось коротким — около двух-трёх минут.
На копии шли консолью, на боевом владелец нажал кнопку в админке — оба пути рабочие, и это хорошая новость: вход в админку при выключенном плагине совместимости работает, и обновление из панели проходит целиком, включая правку схемы базы.
# консольный путь: запуск в фоне, журнал в файл
cd /корень/сайта
setsid nohup /opt/php/8.5/bin/php cli/joomla.php core:update -n \
> /tmp/update.log 2>&1 < /dev/null &
Из PHP это запускается через proc_open, а журнал читается тем же скриптом-пробником.
Ещё одна мелочь: консоль нужно запускать той же сборкой PHP, что и сайт — на
хостинге рядом со штатной сборкой лежат версионные, и «просто php» бывает старее веба.
Обновление файлов — половина дела. Дальше по списку:
| Шаг | Зачем |
|---|---|
У плагина-замены включить подстановку старых имёнclasses_aliases = 1 |
В поставке шестёрки она выключена. Пока не включить — старые расширения не работают. |
| Вернуть системный плагин старого компонента | Он выключался на время окна, иначе консоль не стартовала. |
cli/joomla.php maintenance:database --fix -n |
Дочистить структуру базы. Важно: без ключа --fix команда только перечисляет проблемы и ничего не меняет. |
cli/joomla.php cache:clean -n |
Иначе страницы ещё 15 минут отдают старую разметку — и кажется, что ничего не сработало. |
Бывает, что обновление «падает» на самом последнем шаге, при финализации: не совпадают старые и
новые классы. Пугаться не нужно — файлы шестёрки уже на месте, версию видно в
administrator/manifests/files/joomla.xml, а база дочищается командой выше.
Самое частое после мажорного обновления — не «Joomla сломала расширение», а
удалённый метод драйвера базы. В шестёрке у MySQL-драйвера больше нет
castAsChar() (у PostgreSQL и PDO он остался), и старый компонент на этом падает:
страница врачей отдавала 500.
// было
$c_id = $query->castAsChar('c.id');
// стало — обычный SQL, работает на обеих версиях
$c_id = 'CAST(c.id AS CHAR)';
Правильный путь, конечно, — обновить само расширение до линейки, знающей новую ветку. Правка одной строки — это способ починить быстро и обратимо, а не решение на годы.
Пока в его папке лежит setup/index.php, компонент пишет «is currently offline for
maintenance» и свой код не выполняет. Это признак незавершённой установки, а не поломка сайта.
Заодно выяснилось, что файл языкового пакета вводит в заблуждение: он показывал версию 4.4 при ядре 5.4. Надёжнее определять версию ядра по признакам в файлах (наличие папки плагина совместимости шестёрки) — иначе легко построить неверный план обновления.
Отдельная работа того же дня. Владелец сообщил: «нажимаю „Принять все“ — ничего не происходит». При проверке выяснилось три вещи.
Что сделано:
До нажатия «Принять все» — ноль обращений к серверам аналитики. После нажатия — восемь запросов, и согласие записано в двух местах.
Вторая часть дня — скорость. Диагностика показала, что тормозит не код сайта, а картинки: библиотека фотографий врачей лежала в формате PNG — формате без потерь, придуманном для чертежей и логотипов. Для фотографии он раздувает файл в десятки раз.
Качество проверялось не «на слово»: собранная картинка-сравнение «оригинал | JPEG | WebP» и просмотр в масштабе 1:1 — по лицу, волосам и тексту на баннере. Разницы для посетителя нет.
Фотографии врачей хранит сам компонент: путь к фото карточки лежит в JSON-поле
media таблицы материалов, а сами файлы — в папке кэша, по шесть размеров
на каждое фото. Важная деталь: файла без суффикса размера на диске нет, хотя путь в базе
выглядит именно так — имя с размером компонент подставляет сам.
media/tz_portfolio_plus/article/cache/
belkina-13_xs.webp _s _m _l _o _xl
# базы belkina-13.webp на диске нет — путь виртуальный
Замена расширения в базе коснулась 47 карточек, а заранее в новый формат были переведены только те 19 наборов, что были в PNG. Пятнадцать фотографий на несколько минут стали отдавать «не найдено», пока не перевёл остальные. Вывод: сначала файлы — потом пути, и проверять отдачу каждой ссылки, а не факт заливки.
Итог по библиотеке:
| Что | Было | Стало |
|---|---|---|
| 19 наборов фотографий в PNG (6 размеров каждый) | 102,6 МБ | 1,5 МБ |
| 313 файлов, лежавших в JPEG | 26,3 МБ | 4,5 МБ |
| Страница врачей целиком | 12,86 МБ | 0,83 МБ |
| Главная страница | 1,72 МБ | 0,51 МБ |
Оригиналы никуда не удалялись — откат сводится к замене расширения обратно в базе.
Отчёт PageSpeed выглядел как приговор, но после разбора оказалось: собственный код сайта почти не виноват. Основной вес давали чужие виджеты, подключённые к странице.
| Кто | Скрипты | Процессор |
|---|---|---|
| Встроенное видео Rutube | 660 КБ | 996 мс |
| Викторина Marquiz (всплывает через минуту) | 650 КБ | 424 мс |
| Три счётчика Яндекс.Метрики | 272 КБ | 467 мс |
| Ещё один контейнер Google Tag Manager | 118 КБ | 133 мс |
| Значки-рейтинги и чат | 62 КБ | — |
Викторина была свёрнута в невидимое окно и должна была всплыть через 60 секунд — но её
iframe со всем кодом загружался сразу при каждом заходе. Видео — та же история.
Решение: настоящий адрес переносится в data-атрибут, а в самом
iframe остаётся пустышка; подстановка происходит в момент показа.
<!-- было: грузится всегда -->
<iframe class="quiz-iframe" src="https://mrqz.me/…"></iframe>
<!-- стало: адрес подставляется при показе -->
<iframe class="quiz-iframe" src="about:blank" data-dn-src="https://mrqz.me/…"></iframe>
В коде показа условие должно сравнивать текущий адрес кадра с адресом из атрибута, а не с пустышкой: сравнение с «пустым» адресом в части случаев не срабатывает и окно открывается пустым. Проверка — браузером: дождаться показа и убедиться, что адрес кадра стал настоящим.
defer — шесть файлов больше не
блокируют отрисовку страницы. Перед этим проверено, что ни один встроенный скрипт не вызывает
библиотеку на этапе разбора (иначе получите «$ is not defined»): defer сохраняет
порядок и выполняется до DOMContentLoaded, поэтому обработчики не ломаются..htaccess.
Правило из .htaccess подействовало не на всех: у картинок
(/images/, /img/, /assets/img/, /media/)
заголовки кэширования появились, а .css, .js и .svg
веб-сервер отдаёт мимо Apache — на них правило не действует. Проверять нужно каждый путь
отдельным запросом, а для остального писать в поддержку хостинга.
В «полевых» данных время ответа сервера выглядело пугающе — 1,5 секунды. Прямой замер показал 0,13 секунды на ответ и 0,28–0,31 с на сборку страницы внутри. То есть сервер быстрый, а цифра в отчёте — это мобильная сеть посетителя плюс усреднение за 28 дней, в которое попал и период до оптимизаций.
Полевые показатели Google обновляются медленно: сутки-двое на сдвиг и до четырёх недель на полное «выезжание» старых значений. Об этом стоит сказать владельцу заранее — иначе он будет смотреть на старые цифры и считать, что ничего не изменилось.
Не вышло:
.htaccess — их отдаёт nginx напрямую;Осталось на решение владельца:
configuration.php —
прежде чем что-то менять.maintenance:database --fix, очистить кэш.Материал подготовлен по итогам работ 13.10.2026 на копии и боевом сайте.
Все числа — из фактических замеров: вес страниц по HTTP, ответы сервера по журналам и замерам, оценка скорости — по лабораторным данным Google PageSpeed Insights для мобильных устройств.