Хуки событий⚓︎
В предыдущей главе, вы, возможно, заметили, что наша логика плагина была заключена в двух методах. Каждый из этих методов onPluginsInitialized и onPageInitialized соответствуют хукам событий, которые доступны на протяжении всего жизненного цикла Grav.
Чтобы полностью использовать мощь плагинов Grav, вам нужно знать, какие хуки событий доступны, в каком порядке вызываются и что доступно во время этих вызовов. Хуки событий имеют прямое отношение к общему жизненному циклу Grav.
Порядок событий⚓︎
Большинство событий в Grav происходят в определенном порядке, и важно понимать этот порядок, если вы создаете плагины:
- onFatalException (нет порядка, может произойти в любое время)
PluginsLoadedEventclass (1.7)PluginsLoadedEventclass (1.7)- onPluginsInitialized
FlexRegisterEventclass (1.7)- onThemeInitialized
- onRequestHandlerInit (1.6)
- onTask (1.6)
- onTask.{task}
- onAction (1.6)
- onAction.{action} (1.6)
- onBackupsInitialized
- onSchedulerInitialized (1.6)
- onAssetsInitialized
- onTwigTemplatePaths
- onTwigLoader
- onTwigInitialized
- onTwigExtensions
- onBuildPagesInitialized (один раз при повторной обработке страниц)
- onPageProcessed (каждая страница, ещё не кэшированная)
- onFormPageHeaderProcessed (1.6) (каждая страница, ещё не кэшированная)
- onFolderProcessed (для каждой найденной папки)
- onPagesInitialized
- onPageInitialized
- onPageContentRaw (каждая страница, ещё не кэшированная)
- onMarkdownInitialized
- onPageContentProcessed (каждая страница, ещё не кэшированная)
- onPageContent (вызывается при первом вызове Page::content() даже при кэшировании.)
- onPageNotFound
- onPageAction (1.6)
- onPageAction.{action} (1.6)
- onPageTask (1.6)
- onPageTask.{task} (1.6)
- onTwigPageVariables (каждая страница, ещё не кэшированная)
- onHttpPostFilter (1.5.2)
- onTwigSiteVariables
- onCollectionProcessed (по запросу коллекции)
- onOutputGenerated
- onPageHeaders
- onOutputRendered
- onShutdown
Разные события:
- onBlueprintCreated
- onTwigTemplateVariables
- onTwigStringVariables
- onBeforeDownload
- onPageFallBackUrl
- onMediaLocate
- onGetPageBlueprints
- onGetPageTemplates
- onFlexObjectRender (1.6)
- onFlexCollectionRender (1.6)
- onBeforeCacheClear
- onImageMediumSaved (ImageFile)
- onAfterCacheClear (1.7)
- onHttpPostFilter (1.7)
PermissionsRegisterEventclass (1.7)
Хуки событий ядра Grav⚓︎
Есть несколько основных обработчиков событий Grav, которые запускаются во время обработки страницы:
onFatalException⚓︎
Это событие может быть запущено в любое время, если PHP выдает фатальное исключение. В настоящее время это используется плагином problems для обработки списка возможных причин, по которым Grav выдает фатальное исключение.
onPluginsInitialized⚓︎
Это первое доступное событие плагина. На данный момент были инициированы следующие объекты:
- Uri
- Config
- Debugger
- Cache
- Plugins
Плагин не будет загружен вообще, если для этого конкретного плагина установлена опция конфигурации enabled: false.
onAssetsInitialized⚓︎
Событие означает, что менеджер ресурсов инициализирован и готов к добавлению ресурсов и управлению ими.
onPagesInitialized⚓︎
Это событие означает, что все страницы в папке Grav user/pages были загружены как объекты и доступны в объекте Pages.
onPageNotFound⚓︎
Это событие, которое может быть запущено, если ожидаемая страница не найдена. В настоящее время это используется плагином error для отображения красивой страницы с ошибкой 404.
onPageInitialized⚓︎
Текущая страница по запросу URL загружена в объект Page.
onOutputGenerated⚓︎
Вывод был обработан механизмом создания шаблонов Twig и теперь представляет собой всего лишь строку HTML.
onPageHeaders⚓︎
Позволяет манипулировать объектом заголовков страницы.
onOutputRendered⚓︎
Вывод был полностью обработан и отправлен на дисплей.
onShutdown⚓︎
Новое и очень мощное событие, которое позволяет выполнять действия после того, как Grav завершил обработку и соединение с клиентом было закрыто. Это особенно полезно для выполнения действий, которые не требуют взаимодействия с пользователем и потенциально могут повлиять на производительность. Возможные варианты использования включают отслеживание пользователей и обработку заданий.
onBeforeDownload⚓︎
Это новое событие передает объект события, содержащий file. Это событие можно использовать для ведения журнала или предоставления/запрета доступа для загрузки указанного файла.
onGetPageTemplates⚓︎
Это событие позволяет плагинам предоставлять свои собственные шаблоны в дополнение к шаблонам, собранным из структуры каталогов и ядра темы. Это особенно полезно, если вы хотите, чтобы плагин предоставлял собственный шаблон.
Пример
<?php
/**
* Добавляем типы шаблонов страниц.
*/
public function onGetPageTemplates(Event $event)
{
/** @var Types $types */
$types = $event->types;
$types->register('downloads');
}
Это позволяет плагину зарегистрировать шаблон (который он может предоставить), чтобы он отображался в раскрывающемся списке типов шаблонов страниц (например, при редактировании страницы). В приведенном выше примере добавлен тип шаблона downloads, поскольку в каталоге downloads есть файл downloads.html.twig.
onGetPageBlueprints⚓︎
Это событие, такое как onGetPageTemplates, позволяет плагину предоставлять свои собственные ресурсы в дополнение к основным и специфическим для темы. В данном случае это чертежи.
Пример
<?php
$scanBlueprintsAndTemplates = function () use ($grav) {
// Сканируем чертежи
$event = new Event();
$event->types = self::$types;
$grav->fireEvent('onGetPageBlueprints', $event);
self::$types->scanBlueprints('theme://blueprints/');
// Сканируем шаблоны
$event = new Event();
$event->types = self::$types;
$grav->fireEvent('onGetPageTemplates', $event);
self::$types->scanTemplates('theme://templates/');
};
В этом примере мы используем хуки onGetPageTemplates и onGetPageBlueprints, чтобы сделать эти предоставляемые плагином ресурсы (шаблоны и чертежи) доступными Grav для наследования и других целей.
Хуки событий Twig⚓︎
Twig имеет собственный набор обработчиков событий.
onTwigTemplatePaths⚓︎
Базовые местоположения для путей к шаблонам были установлены на объекте Twig. Если вам нужно добавить другие места, где Twig будет искать пути к шаблонам, используйте это событие.
Пример
<?php
/**
* Добавляем каталог шаблонов в пути поиска Twig.
*/
public function onTwigTemplatePaths()
{
$this->grav['twig']->twig_paths[] = __DIR__ . '/templates';
}
onTwigInitialized⚓︎
На этом этапе шаблонизатор Twig инициализирован.
onTwigExtensions⚓︎
Основные расширения Twig были загружены, но если вам нужно добавить собственное расширение Twig, вы можете сделать это с помощью этого обработчика событий.
onTwigPageVariables⚓︎
Где Twig обрабатывает страницу напрямую, то есть когда вы устанавливаете process: twig: true в заголовках YAML страницы. Здесь вы должны добавить в Twig любые переменные, которые должны быть доступны Twig во время этого процесса.
onTwigSiteVariables⚓︎
Где Twig обрабатывает полную иерархию шаблонов сайта. Здесь вы должны добавить в Twig любые переменные, которые должны быть доступны Twig во время этого процесса.
onBuildTwigSandboxPolicy (2.0)⚓︎
Вызывается во время формирования политики песочницы Twig для контента в Grav. Twig-код, добавленный редакторами непосредственно в содержимое страниц, выполняется внутри этой песочницы (шаблоны тем и плагинов, находящиеся на диске, считаются доверенными и никогда не выполняются в песочнице). Если ваш плагин предоставляет функцию, фильтр, тег, метод или свойство Twig, которые авторы должны иметь возможность использовать в содержимом страниц, разрешите их здесь, иначе песочница заблокирует их использование.
Событие получает те же списки разрешений, что используются в security.yaml: плоские списки для tags, filters и functions, а также список записей вида class/methods для methods и properties. Добавляйте элементы в нужный список по схеме «прочитать → изменить → вернуть», поскольку аргументы передаются по значению. Событие срабатывает только при построении политики, один раз за запрос, после чего результат кэшируется, поэтому дополнительных затрат на каждую отрисовку страницы нет.
Разрешайте только те элементы, которые безопасно выполнять в контенте, созданном любым пользователем с правом редактирования страниц. Регистрация элемента в этом списке означает тот же уровень доверия, что и его изначальное предоставление пользователям.
Пример
<?php
/**
* Разрешаем использование Twig-функции этого плагина в изолированном содержимом страницы, обрабатываемом в песочнице.
*/
public function onBuildTwigSandboxPolicy(Event $event)
{
$functions = $event['functions'];
$functions[] = 'unite_gallery';
$event['functions'] = $functions;
}
onXssTrustedMarkup (2.0)⚓︎
Вызывается во время проверки результирующего HTML на наличие XSS, которую Grav выполняет для Twig-кода, созданного редактором и размещённого в содержимом страницы (проверка, управляемая параметром security.twig_content.xss_scan_output, используемая, когда включён security.twig_content.process_enabled). Проверка анализирует итоговый HTML и, если обнаруживает потенциально опасную разметку (например, <iframe>, <script>, <object>, обработчик on*=, ...), полностью очищает содержимое страницы, чтобы предотвратить выполнение вредоносного кода, сформированного во время рендеринга.
Это создаёт проблему для плагинов, которые на законных основаниях генерируют такую разметку — например, <iframe> для встраивания видео, <script type="application/ld+json"> с JSON-LD, пользовательский элемент, <object> или <embed>. Используйте это событие, чтобы удалить собственную доверенную разметку из $event['html'] до начала проверки. Всё, что обработчик оставит в $event['html'], по-прежнему будет проверено, поэтому защита сохранится для всего остального.
$event['html'] — это содержимое, которое будет проверяться (именно его следует изменять и возвращать); $event['original'] содержит полный, неизменённый результат рендеринга и предоставляется только для справки.
Модель доверия
Это исключение распространяется только на код плагинов и тем, установленных администратором, — то есть действует тот же уровень доверия, который темы уже имеют при использовании Twig без песочницы. Оно не ослабляет проверку содержимого, созданного редакторами: сканер по-прежнему выполняется для всего, что вы явно не исключили. Поэтому вы можете брать на себя ответственность только за разметку, сгенерированную вашим плагином. Используйте максимально точное сопоставление (например, собственный класс или атрибут-маркер либо, для встроенного контента, проверку хоста) и никогда не исключайте из проверки тег целиком — иначе вы ослабите защиту для содержимого, которое не было создано вашим плагином. Если же исключение окажется слишком узким, это безопасно: разметка будет проверена и при необходимости страница будет очищена, но уязвимость не появится.
Пример — исключение из проверки блока JSON-LD, сгенерированного плагином
<?php
public function onXssTrustedMarkup(Event $event)
{
// Удаляем ТОЛЬКО JSON-LD, сгенерированный этим плагином, используя максимально точное сопоставление.
$html = preg_replace(
'#<script type="application/ld\+json" data-myplugin>.*?</script>#is',
' ',
$event['html']
);
$event['html'] = $html;
}
Iframe от доверенных провайдеров — декларативный способ
Для распространённого случая, когда требуется встроить <iframe> от доверенного провайдера (YouTube, Vimeo, карта и т. п.), вам не нужно самостоятельно писать логику сопоставления. Зарегистрируйте доверенные хосты, и Grav автоматически исключит соответствующие <iframe> из проверки (только если все значения src/data-src указывают на доверенный хост и отсутствуют встроенные обработчики событий; доверенный хост также распространяется на все его поддомены):
<?php
// Добавляем хосты во встроенный список разрешённых для iframe.
// (это также можно настроить через security.xss_allowed_iframe_hosts).
public function onXssAllowedIframeHosts(Event $event)
{
$hosts = $event['hosts'];
$hosts[] = 'youtube.com';
$hosts[] = 'youtube-nocookie.com';
$event['hosts'] = $hosts;
}
Вы также можете использовать ту же безопасную проверку хоста для собственного провайдера в обработчике onXssTrustedMarkup, воспользовавшись общедоступным вспомогательным методом \Grav\Common\Security::exciseTrustedIframes($html, ['vimeo.com']).
Стоит учитывать ещё один важный момент: эта проверка анализирует разметку после обработки Markdown, а фильтр tagfilter в реализации GFM, используемой Grav, экранирует необработанные теги <iframe> и <script>, вставленные в содержимое во время обработки Markdown. Поэтому, если ваш плагин добавляет разметку от провайдера, делайте это в обработчике onPageContentProcessed (после обработки Markdown), а не в onPageContentRaw, чтобы разметка сохранилась и могла быть распознана этим механизмом исключений.
Хуки событий коллекции⚓︎
onCollectionProcessed⚓︎
Если вам нужно манипулировать коллекцией после того, как она была обработана, самое время это сделать.
Хуки событий страницы⚓︎
onBuildPagesInitialized⚓︎
Это событие запускается один раз, когда страницы будут повторно обработаны. Обычно это происходит, если срок действия кэша истек или его необходимо обновить. Это полезное событие для плагинов, которым необходимо управлять контентом и кэшировать результаты.
onBlueprintCreated⚓︎
Это используется для обработки и обработки форм.
onPageContentRaw⚓︎
После того, как страница найдена, заголовок обрабатывается, но содержимое не обрабатывается. Это запускается для каждой страницы в системе Grav. Производительность не является проблемой, потому что это событие не будет запускаться на кэшированной странице, только когда кэш очищен или происходит событие очистки кэша.
onPageProcessed⚓︎
После того, как страница проанализирована и обработана. Это запускается для каждой страницы в системе Grav. Производительность не является проблемой, потому что это событие не будет запускаться на кэшированной странице, только когда кэш очищен или происходит событие очистки кэша.
onMarkdownInitialized⚓︎
Вызывается при инициализации Markdown. Позволяет переопределить реализацию обработки Parsedown по умолчанию. См. Пример использования в PR.
onPageContentProcessed⚓︎
Это событие запускается после того, как метод страницы content() обработал содержимое страницы. Это особенно полезно, если вы хотите выполнять действия с постобработанным содержимым, но при этом убедитесь, что результаты кэшируются. Производительность не является проблемой, потому что это событие не будет запускаться на кэшированной странице, только когда кэш очищен или происходит событие очистки кэша.
onFolderProcessed⚓︎
После того, как папка проанализирована и обработана. Он запускается для каждой папки в системе Grav. Производительность не является проблемой, потому что это событие не будет запускаться на кэшированной странице, только когда кэш очищен или происходит событие очистки кэша.
onPageFallBackUrl⚓︎
Если маршрут не распознается как страница, Grav пытается получить доступ к медиаресурсу страницы. Событие запускается, как только начинается процедура, поэтому плагины могут подключаться и предоставлять дополнительные функции.
onMediaLocate⚓︎
Добавляет поддержку пользовательских медиа-расположений для отрывков (excerpts).
onTwigLoader⚓︎
Добавляет поддержку использования пространств имен вместе с двумя новыми методами в классе Twig: Twig::addPath($path, $namespace) и Twig::prependPath($path, $namespace).
