GeniXCMS

Using Hooks

categoryHow To edit_calendar31 Mar 2026

How to Use Hooks

Hooks are the primary extension mechanism in GeniXCMS. They allow themes and modules to add functionality or modify output without editing core files.

There are two types of hooks:

  • Actions — Execute a function at a specific point (no return value expected).
  • Filters — Modify a value and return the modified result.

Actions

Attaching an Action

Usage: Hooks::attach(string $hook_name, callable $callback, int $priority = 10);

// Attach a function to the 'footer_load_lib' hook
Hooks::attach('footer_load_lib', ['MyTheme', 'loadAssets']);

// Or with a closure
Hooks::attach('post_submit_add_action', function($data) {
    // Send a notification email when a post is added
    Mail::send([
        'to'      => '[email protected]',
        'subject' => 'New Post Published',
        'message' => 'A new post was just published.',
    ]);
});

Running an Action

Usage: Hooks::run(string $hook_name, mixed $data = '');

// In a theme template (Latte)
{Hooks::run('footer_load_lib')|noescape}

// In PHP code
Hooks::run('post_submit_add_action', $_POST);

Filters

Attaching a Filter

Usage: Hooks::filter(string $hook_name, mixed $value);

The attached function must accept the value and return a (modified) value.

// Attach a filter to modify post titles
Hooks::attach('post_title_filter', function($title) {
    return strtoupper($title); // uppercase all titles
});

Applying a Filter

// In PHP
$title = Hooks::filter('post_title_filter', $post->title);

// In a Latte template
{Hooks::filter('post_title_filter', $post->title)|noescape}

Common System Hooks

Action Hooks

Hook Trigger Point $data Type
footer_load_lib Just before </body> in every page (none)
header_load_meta Inside <head> of every page (none)
init Standard system startup finished (none)
post_submit_add_action After a new post is saved $_POST array
post_sqldel_action After a post is deleted from database int $id
admin_sidebar_start Before the first admin sidebar menu (none)
admin_sidebar_end After the last admin sidebar menu (none)
theme_activated_action After a theme is successfully activated string $theme_name
admin_page_dashboard_action On the admin dashboard $data array
admin_footer_action At the bottom of every admin page (none)
user_login_action After a successful user login $data array
user_register_action After a new user registers $data array
post_param_form_bottom At the bottom of the main post form area $data array
post_param_form_sidebar At the bottom of the post form sidebar $data array
page_param_form_bottom At the bottom of the main page form area $data array
page_param_form_sidebar At the bottom of the page form sidebar $data array

Filter Hooks

Hook What It Filters
post_title_filter Post title string before display
post_pre_insert_filter Modify entire post data before inserting to DB
post_pre_update_filter Modify entire post data before updating in DB
user_pre_insert_filter Modify entire user data before creating user
user_pre_update_filter Modify entire user data before updating
post_submit_title_filter Title string during save (sanitize)
post_submit_content_filter Content string during save (sanitize)
post_content_filter Post body HTML before render
system_security_headers_args CSP rules array (directives and sources)
search_type_filter Array of post types to include in Search queries
breadcrumbs_filter Array of breadcrumb items (label and URL)
post_url Array containing the generated URL and post ID
widget_locations Associative array of available widget zones/locations
editor_type_options Associative array of available editor engines

Priority

Attach multiple callbacks to the same hook. Lower priority numbers run first.

Hooks::attach('footer_load_lib', ['Analytics', 'load'], 5);  // runs first
Hooks::attach('footer_load_lib', ['Ads', 'load'], 20);        // runs later

Registering Hooks in a Theme

For a modern theme implementation, put all hook registrations inside a dedicated Theme Class within your function.php. This approach keeps your code organized and allows for clean access to theme utilities like the Asset manager.

<?php
class MyModernTheme
{
    public function __construct()
    {
        // 1. Efficiently load assets (Alternative to footer_load_lib echo)
        $this->setupAssets();

        // 2. Attach filters (Title & Content)
        Hooks::attach('post_title_filter',   [self::class, 'injectTitleIcon'], 10);
        Hooks::attach('post_content_filter', [self::class, 'formatContent'], 20);

        // 3. Register Widget zones specifically for this theme
        Widget::addLocation('sidebar_landing', 'Landing Page Sidebar');
    }

    private function setupAssets()
    {
        // Load default Bootstrap from CDN
        Asset::enqueue(['bootstrap-css', 'bootstrap-js', 'bootstrap-icons']);

        // Register theme-specific JS/CSS
        Asset::register('theme-css', 'css', Url::theme() . '/css/style.css', 'header');
        Asset::register('theme-js',  'js',  Url::theme() . '/js/app.js',     'footer', ['jquery']);

        Asset::enqueue(['theme-css', 'theme-js']);
    }

    public static function injectTitleIcon($title)
    {
        // Add a book icon to every post title
        return '📖 ' . $title;
    }

    public static function formatContent($content)
    {
        // Automatically add a "Related Posts" section after content
        $related = '<hr><p class="fw-bold">Don\'t forget to share this post!</p>';
        return $content . $related;
    }
}
new MyModernTheme();

See Also