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 PluginManager or ThemeManager lifecycle.
  • Priority: A numeric value (default: 10). Lower numbers execute earlier. Hooks with the same priority execute in the order they were registered.
  • Execution:
    • do_action executes callbacks and returns nothing.
    • apply_filters passes 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 $post object).
  • 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_action or apply_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 become null for subsequent hooks.
  • Wrong Argument Count: Registering a hook with 3 arguments but forgetting to set $accepted_args = 3 in add_action or add_filter.
  • Early Execution: Hooking into an event that has already fired (e.g., hooking into valpress_init after 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.