Hooks (Actions & Filters)
Overview
ValPress is built on an event-driven architecture that allows developers to extend or modify its core behavior without touching the application's source files. This is achieved through Hooks, which are categorized into Actions and Filters.
- Actions: Hooks that allow you to "do something" at a specific point in the execution cycle (e.g., sending an email after a post is published).
- Filters: Hooks that allow you to "modify something" (usually data) before it is processed or displayed (e.g., changing the title of a post).
Why It Matters
The hook system is the foundation of ValPress's extensibility. It allows for:
- Decoupling: Plugins and themes can interact with the core without being tightly coupled to its implementation.
- Maintainability: Core updates can be applied without overwriting custom modifications.
- Ecosystem Growth: Developers can create complex integrations (SEO, E-commerce, Analytics) using the same entry points as the core team.
- Flexibility: Multiple plugins can hook into the same event, with fine-grained control over execution order via priorities.
How It Works
ValPress uses a custom Hook manager that tracks registered callbacks for specific "tags" (event names). When an event occurs, the manager executes all registered callbacks in order of their priority.
- Storage: Registered hooks are stored in an internal registry, typically within the
PluginManagerorThemeManagerlifecycle. - Priority: A numeric value (default: 10). Lower numbers execute earlier. Hooks with the same priority execute in the order they were registered.
- Execution:
do_actionexecutes callbacks and returns nothing.apply_filterspasses a value through each callback and returns the final modified value.
Usage
Actions
Actions are used to trigger custom code during the ValPress lifecycle.
/**
* add_action( string $tag, callable $callback, int $priority = 10, int $accepted_args = 1 )
*/
add_action('valpress_init', function() {
// Initialize your plugin services here
MyPluginService::boot();
});
/**
* do_action( string $tag, ...$args )
*/
do_action('valpress_init');
Filters
Filters are used to intercept and modify data.
/**
* add_filter( string $tag, callable $callback, int $priority = 10, int $accepted_args = 1 )
*/
add_filter('the_content', function( $content ) {
// Add a custom signature to the end of every post
return $content . '<p>Published with ValPress.</p>';
});
/**
* apply_filters( string $tag, mixed $value, ...$args )
*/
$content = apply_filters('the_content', $post->content);
Code Examples
Custom Admin Menu Item (Action)
Adding a new menu item to the ValPress dashboard.
add_filter('valpress_admin_menu_items', function ($items) {
$items['custom-settings'] = [
'id' => 'custom-settings',
'title' => __('Plugin Settings'),
'url' => fn() => route('admin.plugin_settings'),
'icon' => 'bi-gear',
'order' => 100,
'parent' => null,
];
return $items;
});
Modifying Page Titles (Filter)
Appending the site name to every page title.
add_filter('valpress_page_title', function ($title) {
$site_name = get_option('site_name', 'ValPress');
return "{$title} | {$site_name}";
}, 15);
Enqueueing Scripts (Action)
Loading custom CSS/JS in the admin area.
add_action('admin_enqueue_scripts', function () {
ScriptManager::enqueue('my-plugin-css', asset('plugins/my-plugin/assets/style.css'));
ScriptManager::enqueue('my-plugin-js', asset('plugins/my-plugin/assets/app.js'), ['jquery']);
});
Advanced Usage
Removing Hooks
You can remove a hook registered by the core or another plugin if you know the tag, callback, and priority.
// Remove a core filter
remove_filter('the_content', 'valpress_default_autop');
// Remove an action with a specific priority
remove_action('valpress_init', [MyService::class, 'init'], 20);
Dynamic Hooks
Some hooks include dynamic parts in their tags, such as post types or statuses.
// Hook specific to the 'product' post type
add_action('save_post_product', function ($post) {
// Logic for saving products
});
Hooking into Anonymous Functions
While anonymous functions are convenient, they cannot be easily removed. For high-extensibility plugins, use class methods or named functions.
// Prefer this for extensibility
add_action('valpress_init', [MyPlugin::class, 'boot']);
Update pipeline hooks
ValPress 0.6 adds hooks and filters for the staged Update Center pipeline. Extension authors use these to validate updates, register repair actions, and observe pipeline steps.
Pipeline stage filters
| Filter | Args | Purpose |
|---|---|---|
valpress_update_verify_stages |
stages[] | Reorder/add core verify stages |
valpress_update_apply_stages |
stages[] | Reorder/add core apply stages |
valpress_update_extension_verify_stages |
stages[] | Extension verify stages |
valpress_update_extension_apply_stages |
stages[] | Extension apply stages |
valpress_update_pre_{phase} |
context, step | Before a step runs |
valpress_update_post_{phase} |
context, step, report | After a step runs |
Health and validation
| Hook / Filter | Purpose |
|---|---|
valpress_health_checks |
Site health definitions (update_relevant flag supported) |
valpress_update_health_checks |
Extra pipeline health checks |
valpress_update_validate_{slug} |
Filter-based extension validation ($report, $context, $type) |
valpress_update_extension_reports |
Adjust aggregated extension reports |
Repair
| Filter | Purpose |
|---|---|
valpress_update_repair_actions |
Register RepairAction buttons in reports |
valpress_update_repair_handlers |
Register executable repair handlers |
Extension lifecycle
| Action | When |
|---|---|
update_plugin_{slug} |
After plugin update apply ($old_version, $new_version) |
update_theme_{slug} |
After theme update apply ($old_version, $new_version) |
valpress_extension_updated |
After any extension apply ($type, $slug, $old_version, $new_version) |
activate_{slug} / deactivate_{slug} |
Plugin activation/deactivation |
activated_plugin / deactivated_plugin |
Plugin activation/deactivation |
See Update Pipeline, Plugin Update Validation, and Repair Actions.
Best Practices
- Use Unique Tags: Always prefix your custom hook tags (e.g.,
myplugin_after_save) to avoid collisions. - Pass Context: When creating hooks, pass as much relevant data as possible (e.g., the current
$postobject). - Respect Priorities: Use the default 10 unless you explicitly need to run before ( < 10) or after ( > 10) other hooks.
- Keep Filters Pure: Filters should only modify the data passed to them. Avoid side effects (like sending emails) inside a filter; use an action instead.
- Check for Existence: Before calling
do_actionorapply_filters, ensure the event name makes sense within the request lifecycle.
Common Mistakes
- Forgetting to Return: In a filter, if you don't
return $value, the data will be lost or becomenullfor subsequent hooks. - Wrong Argument Count: Registering a hook with 3 arguments but forgetting to set
$accepted_args = 3inadd_actionoradd_filter. - Early Execution: Hooking into an event that has already fired (e.g., hooking into
valpress_initafter the system has finished booting). - Logic in Anonymous Functions: Placing too much logic inside an anonymous function makes it hard to debug and impossible for others to unhook.
Summary
Hooks are the heart of ValPress customization. By mastering Actions (to trigger behavior) and Filters (to modify data), you can transform the CMS into a tailored solution for any project while maintaining a clean, upgradeable codebase.