Руководство по обновлению для разработчиков Grav 2.0¶
Эта страница — справочник для разработчиков о том, что изменилось под капотом между Grav 1.7 и Grav 2.0. Если вы поддерживаете плагин, тему или собственную интеграцию, здесь описано, что нужно обновить.
Сам процесс миграции для конечных пользователей (перенос существующего сайта с 1.7 или 1.8 на свежую установку 2.0) описан в разделах Автоматизированная миграция и Ручная миграция. О портировании с помощью агента см. Разработка с помощью ИИ.
Важно
Grav 2.0 не поставляется как обновление на месте. Минимальная версия PHP, стек vendor, админка и публичный API меняются одновременно. Поддерживаемый путь — свежая установка плюс импорт контента. На этой странице описаны технические причины этого и то, что каждое изменение означает для авторов плагинов и тем.
Ключевые изменения¶
- Минимум PHP 8.3 с полной поддержкой PHP 8.4 и 8.5
- Компоненты Symfony 7.x (было 4.4)
- Twig 3.x (форк для совместимости с Defer)
- Monolog 3.x (было 1.25)
- Symfony Cache заменяет Doctrine Cache
- Классическая админка удалена, её заменил Admin 2.0 (SPA на SvelteKit, работающий с новым плагином API)
- Собственный REST API как компонент ядра
- Флаг
compatibility:в схемах для плагинов и тем - Quark 2 — новая тема по умолчанию
- Песочница Twig в контенте для написанного редакторами Twig в контенте страниц, с событием
onBuildTwigSandboxPolicyдля плагинов - Нативная поддержка
.envв ядре, поэтому плагин DotEnv больше не нужен (см. Конфигурация)
Важные несовместимые изменения¶
Требование к версии PHP¶
- Минимальный PHP: 8.3+ (в Grav 1.7 было 7.3.6+)
- Убедитесь, что ваше хостинговое окружение поддерживает PHP 8.3 или выше
- Обновите устаревший код PHP в собственных плагинах или темах
- Проверьте объявления nullable-параметров (
?TypeвместоType = null) - Обновите использование динамических свойств (в PHP 8.2+ необъявленные динамические свойства устарели)
- Замените устаревшие функции и сигнатуры
Классическая админка удалена¶
Классическая админка, рендерившаяся через Twig (плагин admin в том виде, в каком он существовал в 1.x), полностью удалена из Grav 2.0. Её замена — Admin 2.0, SPA на SvelteKit, работающая с новым плагином Grav API.
Для авторов плагинов это самое большое изменение:
- Собственные страницы админки, рендерившиеся через шаблоны Twig классической админки, больше негде выполнять. Их нужно перенести в систему компонентов Admin 2.0. Шаблоны см. в разделе Интеграция плагинов с Admin Next.
- Элементы боковой панели, кнопки меню, плавающие виджеты, настраиваемые типы полей и настраиваемые отчёты в Admin 2.0 регистрируются через события
onApi*. Старые событияonAdmin*удалены. - Схемы переносятся без изменений. Все существующие поля форм и схемы работают в Admin 2.0 как есть; изменилась только оболочка интерфейса админки. Единственное исключение — собственные поставщики
data-*@, для которых теперь нужна регистрация в одну строку (см. ниже).
Если у вашего плагина нет собственного интерфейса админки (только схемы), в этой части для вас ничего не меняется. Страницы конфигурации будут корректно отображаться в Admin 2.0.
Собственные поставщики data-*@ должны быть зарегистрированы¶
Начиная с Grav 2.0.11 вызываемые Class::method, на которые ссылаются директивы схем data-*@ (data-options@, data-default@, data-fields@ и т. д.), проверяются по списку разрешённых поставщиков данных. Собственные поставщики ядра Grav разрешены заранее, а поставщик, который вы поставляете в плагине или теме, — нет. Admin 2.0 разрешает эти вызываемые объекты через API, и незарегистрированный отклоняется с ошибкой Callable '...' is not an approved data provider., из-за чего параметры поля остаются пустыми.
Зарегистрируйте каждого поставщика один раз при запуске с помощью Blueprint::addAllowedDynamicCallable():
use Grav\Common\Data\Blueprint;
public function onPluginsInitialized(): void
{
Blueprint::addAllowedDynamicCallable('Grav\Plugin\MyPlugin\Utils::myOptions');
}
Темы могут сделать то же самое из обработчика onThemeInitialized. Подробности см. в разделе Расширенные фичи схем.
Удалённые драйверы кэша¶
Эти неподдерживаемые драйверы удалены. Поддерживаемые адаптеры (APCu, Memcached, Redis, Array, Filesystem и т. д.) обрабатываются Symfony Cache под теми же именами. Если Grav не может определить собственный адаптер, он возвращается к filesystem:
APC→ используйтеapcuWinCache→ удалёнXCache→ удалёнMemcache→ используйтеmemcached
Удалённые системные параметры¶
system.umask_fixудалён по соображениям безопасности- Различные устаревшие ключи, которые инструмент миграции помечает с предложенными заменами
Обновления зависимостей¶
Основные зависимости¶
| Зависимость | Grav 1.7 | Grav 2.0 |
|---|---|---|
| PHP | 7.3.6+ | 8.3+ |
| Twig | 1.x | 3.x (форк для Defer) |
| Symfony | 4.4 | 7.x |
| Monolog | 1.25 | 3.x |
| RocketTheme/Toolbox | 1.0 | 2.0 |
Удалено¶
- Doctrine Cache (заменён на Symfony Cache)
- Устаревшие драйверы кэша (APC, WinCache, XCache, Memcache)
Миграция на Twig 3¶
Синтаксис Twig 1 и Twig 2, который больше не действителен в Twig 3, нужно обновить. Grav 2.0 включает переключатели совместимости во время выполнения, но рекомендуется обновить шаблоны навсегда, а не полагаться на преобразования.
Включите слой совместимости на время миграции:
system:
strict_mode:
twig2_compat: true # Совместимость с Twig 2
twig3_compat: true # Совместимость с Twig 3
После обновления шаблонов отключите эти переключатели и очистите кэш, чтобы убедиться, что всё рендерится без слоя совместимости.
Фильтры в циклах¶
Циклы for со встроенными условиями if должны сначала отфильтровать последовательность:
{# Устаревший вариант #}
{% for page in collection if page.published %}
{{ page.title }}
{% endfor %}
{# Twig 3 #}
{% for page in collection|filter(page => page.published) %}
{{ page.title }}
{% endfor %}
Блоки spaceless и filter¶
Twig 3 удалил блоки {% spaceless %} и {% filter %}. Используйте {% apply %}:
{# Устаревший вариант #}
{% spaceless %}
<div>{{ content|raw }}</div>
{% endspaceless %}
{# Twig 3 #}
{% apply spaceless %}
<div>{{ content|raw }}</div>
{% endapply %}
Проверка sameas¶
Используйте is same as вместо is sameas:
Сигнатура фильтра replace¶
Twig 3 ожидает карту вместо двух строковых аргументов:
Песочница Twig в контенте¶
Grav 2.0 пропускает написанный редакторами Twig в контенте страниц через песочницу безопасности. Это касается только контента (process.twig на странице и строковых шаблонов, рендерящихся через Twig::processString()). Файлы шаблонов тем и плагинов на диске считаются доверенными и никогда не проходят через песочницу, поэтому для обычной разработки .html.twig ничего не меняется.
Примечание
О стороне владельца сайта (включение Twig в контенте, список разрешённых по умолчанию и что происходит при блокировке) см. Twig в контенте.
Внутри песочницы разрешён только список разрешённых тегов, фильтров, функций, методов и свойств. Значения по умолчанию находятся в system/config/security.yaml в разделе twig_sandbox. Если ваш плагин регистрирует функцию, фильтр или тег Twig, которые авторы должны вызывать из контента страниц, песочница по умолчанию блокирует его, потому что не может знать, безопасно ли выполнять этот член против недоверенных входных данных.
Разрешение членов Twig вашего плагина¶
Подпишитесь на событие onBuildTwigSandboxPolicy и добавьте элементы в нужный список. Событие передаёт те же списки, что используются в security.yaml: плоские списки для tags, filters и functions и список строк class/methods для methods и properties.
use RocketTheme\Toolbox\Event\Event;
public static function getSubscribedEvents(): array
{
return [
'onBuildTwigSandboxPolicy' => ['onBuildTwigSandboxPolicy', 0],
];
}
public function onBuildTwigSandboxPolicy(Event $event): void
{
// Разрешить функцию Twig этого плагина в контенте страниц, выполняемом в песочнице.
$functions = $event['functions'];
$functions[] = 'unite_gallery';
$event['functions'] = $functions;
// Разрешить конкретные методы одного из собственных классов плагина
// (та же структура, что в security.yaml: список строк class/methods).
$methods = $event['methods'];
$methods[] = ['class' => \Grav\Plugin\MyGallery\Gallery::class, 'methods' => 'render, thumbnail'];
$event['methods'] = $methods;
}
Шаблон «прочитать — изменить — записать» обязателен, потому что аргументы события возвращаются по значению. Событие срабатывает только при построении политики (один раз за запрос и с кэшированием), поэтому регистрация членов здесь не добавляет затрат на каждый рендеринг.
Примечание
Разрешайте только члены, безопасные для выполнения против контента, который может написать любой пользователь с доступом к редактированию страниц. Регистрация члена здесь — то же решение о доверии, что и его изначальное открытие. Если функция читает файлы, вычисляет строки или обращается к контейнеру, не добавляйте её в список.
Администратор сайта также может разрешить функцию, не трогая код плагина, добавив её в user/config/security.yaml. Учтите, что списки разрешённых песочницы не дополняются при ручном редактировании: плоские списки (allowed_functions, allowed_filters, allowed_tags) заменяются целиком, поэтому фрагмент вроде этого:
удалит все встроенные безопасные функции и оставит только unite_gallery. Добавьте запись через Админка → Конфигурация → Безопасность → Песочница Twig (там список сохраняется целиком) или, если редактируете файл вручную, скопируйте полный список по умолчанию из system/config/security.yaml и дополните его. Для функций из плагинов лучше регистрировать их через onBuildTwigSandboxPolicy, как показано выше: это не требует конфигурации для каждого сайта и переживает изменения списка по умолчанию.
Вызов функций PHP из шаблонов: undefined_functions удалён¶
Grav 1.7 позволял шаблону вызывать почти любую функцию PHP по имени, потому что system.twig.undefined_functions (и undefined_filters) по умолчанию были включены. Grav 2.0 убирает это автоматическое разрешение. Вызов функции PHP, которую Grav или плагин не зарегистрировали, теперь является жёсткой ошибкой — как в доверенных шаблонах, так и в контенте страниц, выполняемом в песочнице.
Списки разрешённых safe_functions / safe_filters остаются явным согласием, по умолчанию пустые. Перечислите конкретные функции, нужные доверенному шаблону:
Команды и функции выполнения кода (system, exec, assert и подобные) всегда отклоняются, и их нельзя добавить в эти списки. Списки разрешённых применяются только к доверенным шаблонам; контент страниц, написанный редакторами, регулируется песочницей Twig в контенте.
Для большинства типовых случаев уже есть эквивалент в Twig или Grav, например фильтр upper вместо strtoupper. Вы также можете зарегистрировать функцию в PHP через onTwigExtensions:
public function onTwigExtensions(): void
{
$this->grav['twig']->twig()->addFunction(
new \Twig\TwigFunction('basename', 'basename')
);
}
Миграция на Monolog 3¶
Методы addInfo(), addError() и т. д. в логгере удалены в Monolog 3. Используйте вместо них имена методов PSR-3. Они также работают в Monolog 2.3+, если ваш код рассчитан на обе версии:
// Старый вариант (Monolog 1.x)
$this->grav['log']->addInfo($message);
$this->grav['log']->addError($message);
// Новый вариант (совместим с Monolog 2.3+ и 3.x)
$this->grav['log']->info($message);
$this->grav['log']->error($message);
Конфликты журналирования PSR-3¶
Если ваш плагин поставляет собственную папку vendor/ или composer.json и фиксирует psr/log на старой версии 1.x, Composer установит несовместимую версию, которая нарушит ожидания обработчика Monolog 3 в Grav. Вы увидите ошибки вроде:
Чтобы решить проблему:
- Удалите явное требование
psr/logиз своего расширения, или - Отдайте предпочтение версии из Grav, добавив блок
replaceвcomposer.json:
- Удалите папку
vendor/плагина после обновленияcomposer.jsonи пересоберите её командойcomposer install.
Команда bin/gpm preflight помечает эту проблему предупреждениями PSR/log compatibility; устраните их до миграции.
Symfony 7¶
Компоненты Symfony теперь на линии 7.x (было 4.4). Для большинства кода плагинов это незаметно, но обратите внимание на:
- Сигнатуры подписчиков событий (возвращаемые типы
getSubscribedEvents()) - Объявления аргументов и параметров консольных команд
- Определения сервисов контейнера в собственных плагинах
- Удалённые устаревшие возможности из диапазона 4.x → 7.x
Если вы напрямую используете DI-контейнер, консоль или диспетчер событий Symfony, изучите руководства по обновлению Symfony для компонентов, от которых зависите.
Интеграция с API и Admin 2.0¶
Grav 2.0 представляет собственный REST API как компонент ядра, поставляемый в виде плагина Grav API. Admin 2.0 — первый клиент этого API, но он полностью универсален: всё, что умеет Admin 2.0, внешний клиент может делать через те же эндпоинты.
Для авторов плагинов есть две точки интеграции:
Добавление эндпоинтов API¶
Если ваш плагин предоставляет данные или операции, которые должны быть доступны Admin 2.0 или внешним клиентам, зарегистрируйте эндпоинты через событие onApiRegisterRoutes. Контроллеры наследуют AbstractApiController и бесплатно получают аутентификацию, разрешения, разбор запросов и обработку ошибок.
Полный шаблон см. в разделе Интеграция плагинов с API.
Интерфейс Admin 2.0¶
Если у вашего плагина раньше был интерфейс классической админки, описание замены находится в общем руководстве по интеграции с Admin Next. Соответствующие хуки событий onApi* регистрируют элементы боковой панели, кнопки меню, плавающие виджеты, страницы плагина, настраиваемые типы полей, настраиваемые отчёты и изменения схем.
Навык Claude Code grav-api-admin-next-integration (Разработка с помощью ИИ) описывает весь набор.
Не привязывайте подписку на события к isAdmin()¶
Распространённый шаблон 1.x — решать внутри onPluginsInitialized(), подписываться ли на событие контекста админки, с проверкой $this->isAdmin():
public function onPluginsInitialized(): void
{
if ($this->isAdmin()) {
// Регистрируем обработчики для админки, только когда мы в админке.
$this->enable([
'onGetPageTemplates' => ['onGetPageTemplates', 0],
]);
return;
}
// ...обработчики фронтенда...
}
Это работало при классической админке, потому что плагин Admin устанавливал $grav['admin'] очень рано, в своём собственном onPluginsInitialized(). К моменту инициализации любого другого плагина isAdmin() уже был true.
В запросе Admin 2.0 нет классической админки. Admin 2.0 — это SPA, которая общается с плагином Grav API, и запросы API проходят через этот плагин. Плагин API устанавливает контекст админки (регистрирует лёгкий прокси как $grav['admin'], чтобы isAdmin() возвращал true) только при отправке совпавшего маршрута, что происходит намного позже onPluginsInitialized(). Поэтому во время инициализации плагинов при запросе API $grav['admin'] ещё не установлен, и isAdmin() возвращает false. Закрытый условием блок enable() никогда не выполняется, обработчик не подписывается, и когда событие наконец срабатывает при отправке, слушать его некому.
Самый заметный пострадавший — список шаблонов страниц. Pages::getTypes() вызывает onGetPageTemplates (и onGetPageBlueprints), чтобы плагины зарегистрировали типы страниц. У плагина, который регистрирует свой шаблон в блоке с условием isAdmin(), шаблон молча исчезнет из выпадающего списка «Добавить страницу» в Admin 2.0, хотя тот же плагин нормально работал с классической админкой. (Если активная тема поставляется с файлом шаблона того же имени, копия из темы маскирует ошибку, из-за чего её легко не заметить.)
Решение — подписываться на эти события безусловно, в getSubscribedEvents():
public static function getSubscribedEvents()
{
return [
'onPluginsInitialized' => ['onPluginsInitialized', 0],
'onTwigTemplatePaths' => ['onTwigTemplatePaths', 0],
// Регистрируем шаблон страницы в любом контексте. Обработчик
// не зависит от контекста, а событие срабатывает только когда Grav
// строит список типов, поэтому безусловная подписка ничего не стоит.
'onGetPageTemplates' => ['onGetPageTemplates', 0],
];
}
public function onGetPageTemplates(Event $event): void
{
$event->types->register('my-template');
}
Это работает независимо от того, как устанавливается контекст админки, потому что слушатель подключается до отправки любого запроса.
Примечание
Правило шире этих двух событий: никогда не решайте, подписываться ли на событие, на основе isAdmin(), вычисленного внутри onPluginsInitialized(). Если обработчик дешёвый и не зависит от контекста, подписывайтесь на него статически. Если он действительно должен выполняться только в контексте админки или API, выполняйте проверку isAdmin() внутри обработчика, где она работает во время отправки и контекст админки уже установлен, а не во время подписки.
Флаг compatibility: в схемах¶
Плагины и темы теперь объявляют, на каких мажорных версиях Grav они протестированы:
Его читают:
- Мастер миграции при миграции 1.x → 2.0 (чтобы решить, что устанавливать на подготовленный сайт 2.0)
- GPM на сайте 2.0 (чтобы отказываться устанавливать пакеты без флага для запущенной версии Grav)
Без явного блока compatibility: Grav делает вывод по массиву dependencies:. Плагин, требующий grav >= 1.7, считается совместимым только с 1.7; требующий grav >= 2.0 — только с 2.0.
Что делать авторам плагинов: после тестирования плагина на Grav 2.0 добавьте '2.0' в список compatibility.grav и выпустите новую версию.
Полные правила и логику вывода см. в разделе Совместимость плагинов.
Переводы админки и пространство имён ICU.¶
Admin 2.0 расширяет существующий конвейер языков Grav необязательным слоем ICU MessageFormat для подстановок, множественных форм и вариантов выбора (select). Ключи с префиксом ICU. проходят через форматтер; ключи без префикса возвращаются как есть, точно как в классической админке.
Это значит, что один языковой YAML может обслуживать и 1.7, и 2.0:
PLUGIN_MY_PLUGIN:
ITEMS_COUNT: "{count} items" # плоский ключ: его читают обе версии
ICU:
PLUGIN_MY_PLUGIN:
ITEMS_COUNT: "{count, plural, one {# item} other {# items}}"
Grav 1.7 полностью игнорирует блок ICU: верхнего уровня. Admin 2.0 отдаёт ему предпочтение.
Полные подробности, включая фильтр отключённых плагинов и инструменты аудита, см. в разделе Переводы админки.
Улучшения качества кода¶
Кодовая база Grav 2.0 поднята до:
- Поддержки PHPStan level 6 в классах фреймворка
- Совместимости с PHP 8.4 по всему коду
- Более строгих объявлений типов
- Улучшенной обработки ошибок
Плагины, которые полагаются на нестрогую типизацию или необъявленные динамические свойства, могут выдавать предупреждения или фатальные ошибки там, где раньше их не было.
Краткое повторение пути миграции¶
Процесс миграции для конечных пользователей подробно описан в других разделах. Кратко:
- Создайте резервную копию существующего сайта Grav 1.7 или 1.8
- Установите плагин Migrate to Grav 2.0
- Запустите мастер (или выполните процедуру ручной миграции)
- Проверьте подготовленную установку 2.0
- Переключитесь, когда будете готовы
Мастер выполняет проверку флага compatibility:, установки через GPM, копирование контента и замену. Сайт 1.x не затрагивается до переключения.
Если ваш сайт раньше работал на бета-версии Grav 1.8, путь миграции тот же: 1.8 beta → свежая установка 2.0 + импорт. Прямого обновления с бета-версии 1.8 до 2.0 нет; один и тот же инструмент миграции обрабатывает обе исходные версии.
Контрольный список для авторов плагинов¶
Прежде чем объявить совместимость с 2.0:
- Плагин устанавливается и включается без ошибок на свежей установке Grav 2.0
- Нет предупреждений об устаревших возможностях PHP 8.3+ и фатальных ошибок (
error_reporting=E_ALL) - Нет предупреждений слоя совместимости Twig 3 (отключите
twig2_compatиtwig3_compatи проверьтеlogs/grav.log) - Нет предупреждений об устаревших возможностях Monolog (не осталось вызовов
addInfo()/addError()) - Нет конфликтов
psr/log(bin/gpm preflightне выдаёт замечаний) - Зависимости Composer чисто устанавливаются с PHP 8.3+
- Схемы корректно отображаются в Admin 2.0
- Все вызываемые объекты собственных поставщиков
data-*@зарегистрированы черезBlueprint::addAllowedDynamicCallable()(поля выбора, использующие их, показывают параметры в Admin 2.0) - Если у плагина раньше был интерфейс классической админки: эквивалентная функциональность зарегистрирована через события
onApi*для Admin 2.0 - Ни одна подписка на события не привязана к
isAdmin()внутриonPluginsInitialized()(зарегистрированные так шаблоны и схемы страниц исчезают в Admin 2.0); подписывайтесь статически - Все собственные шаблоны страниц, которые предоставляет плагин, появляются в выпадающем списке «Добавить страницу» в Admin 2.0
- Языковые строки корректно отображаются в Admin 2.0 (нет «очеловеченных» запасных вариантов для существующих ключей)
- Флаг
compatibility:вblueprints.yamlсодержит'2.0' - Плагин работает с пробным запуском и строгим режимом мастера миграции
Решение проблем¶
О проблемах времени выполнения, возникающих во время самой миграции (определение версии PHP, циклы перенаправлений на подготовленной копии, восстановление после частичного переключения, устаревшие ключи конфигурации), см. страницу Решение проблем.
Об инструментах, автоматизирующих большую часть работы по портированию, см. Разработка с помощью ИИ.
Как получить помощь¶
При сообщении о проблеме укажите:
- Версию PHP
- Версию Grav (исходную и целевую)
- Вывод
bin/gpm preflight(если применимо) - Содержимое
.migrating(если миграция выполнялась) - Соответствующие записи из
logs/grav.log - Список установленных плагинов