⌘K

Filters

Filters transform a value passing through a named pipeline. Unlike hooks (see Hooks & the Plugin Lifecycle), every filter callback must return the value, modified or unchanged — nothing fires for its side effects alone.

pl_add_filter(string $hook, callable $callback, int $priority = 10): void
pl_apply_filter(string $hook, mixed $value, mixed ...$args): mixed

pl_apply_filter() passes $value through every registered callback on $hook, in priority order, and returns the final result — extra $args are forwarded to each callback after $value. If nothing is registered, $value comes back unchanged.

Core filter points already available

As of 1.4.0, the core itself calls pl_apply_filter() at five real points — register on any of these directly, no core edit required:

FilterFires whereReceives
item_before_saveJust before an item is written to disk($item, $type, $fileSlug)
content_data_arrayAfter sl_build_data_array() assembles $data($data)
menu_treeAfter the main menu tree is built($tree)
head_meta_tagsAfter render_meta_tags() builds its output($html, $pageData)
render_content_htmlAfter the full shortcode/Markdown pipeline runs($html, $item)
pl_add_filter('render_content_html', function (string $html, ?array $item) {
    return $html . '<div class="my-plugin-badge">Verified</div>';
});

This is genuinely the most direct way for a plugin to affect front-end content today — it doesn't need the output-buffering trick described in Front-End Integration if a filter point already covers what you need.

Your own filter points

A plugin can also expose filter points for other plugins to hook into. The code producing the value has to explicitly call pl_apply_filter() where it wants to allow modification — if it doesn't, there's nothing to hook into.

// Plugin A
$price = pl_apply_filter('my-plugin/item-price', $rawPrice, $item);

// Plugin B
pl_add_filter('my-plugin/item-price', function (float $price, array $item): float {
    return ($item['category'] ?? '') === 'sale' ? $price * 0.90 : $price;
});

When several callbacks register on the same hook, they run in priority order, each receiving the value as modified by the previous one:

pl_add_filter('my-plugin/item-price', fn($price) => $price * 0.90, 10); // 10% off
pl_add_filter('my-plugin/item-price', fn($price) => $price - 5.00, 20); // then €5 off

Namespace filter names as {your-plugin-slug}/{what-is-filtered} to avoid collisions — my-plugin/item-price, my-plugin/email-subject, my-plugin/rendered-html.