Skip to content
TubePress — free, self-hosted & actively maintained
Developer reference

Hooks & plugin development

Extend TubePress with its WordPress-style action and filter hook system: 15+ hook points, the plugin lifecycle, and how to build and package your own plugin.

Plugins are how you add features to TubePress without touching the core. They hook into the CMS through a small WordPress-style system of actions (fire-and-forget events) and filters (modify a value and return it). The three bundled plugins — Age Gate, Backup Pro and CTR Ranking — are built entirely with the same API documented here.

The hook system

The static HookSystem class manages both kinds of hook. Callbacks run in ascending priority order (default 10); any exception a callback throws is caught and logged, so one bad plugin can never take the page down.

// Actions — do something when an event fires
HookSystem::addAction('head.meta', function () {
    echo '<meta name="rating" content="adult">';
}, 10);

// Filters — receive a value, return a (possibly) changed value
HookSystem::addFilter('theme.template_data', function ($data, $template) {
    if ($template === 'home') {
        $data['promo'] = Promo::current();
    }
    return $data;
});
MethodPurpose
addAction($hook, $cb, $priority = 10)Register an action listener.
doAction($hook, ...$args)Fire an action (core calls these).
addFilter($hook, $cb, $priority = 10)Register a filter.
applyFilter($hook, $value, ...$args)Run a value through its filters and return it.
hasAction() / hasFilter()Check whether anything is listening.
removeAction() / removeFilter()Detach all listeners for a hook.

Available hook points

The core fires these hooks. You can also define and fire your own from a plugin.

Actions

HookWhen it fires
head.metaInside <head>, after the CSS — add meta tags, verification tags, etc.
footer.scriptsBefore </body>, after the JS — add analytics or widgets.
routes.registeredAfter all core routes are registered, before the page catch-all.
router.before_dispatchJust before a matched handler runs. Arg: the resolved path.

Filters

HookModifies (args)
theme.template_dataThe data array before a template renders. Args: ($data, $templateName).
head.cssThe combined CSS output string.
footer.scriptsThe combined JS output string.
video.card.htmlThe entire video-card HTML. Args: ($html, $video). Return a non-empty string to replace it.

Anatomy of a plugin

A plugin is a folder under plugins/ with a manifest and a class implementing PluginInterface.

plugins/myplugin/
├── plugin.json     Manifest (name, slug, version, …)
├── myplugin.php    Main class implementing PluginInterface
├── icon.svg        Optional icon shown in the admin
└── …               Your assets, templates, migrations

The manifest mirrors the bundled plugins:

{
  "name": "My Plugin",
  "slug": "myplugin",
  "description": "What it does, in one sentence.",
  "version": "1.0.0",
  "author": "You",
  "requires": "1.0.0",
  "icon": "icon.svg"
}

The main class implements the six lifecycle methods of PluginInterface:

MethodCalled when
boot()Every request while the plugin is active — register hooks, routes and assets here.
activate()The admin enables the plugin.
deactivate()The admin disables it.
install()First activation — create tables, seed settings.
uninstall()The plugin is removed — clean up.
info()Returns the manifest data as an array.

Registering routes & assets

Do all of your wiring in boot():

public function boot(): void
{
    // A front-end route
    Router::get('/promo/{code}', [PromoController::class, 'show']);

    // Inject a meta tag
    HookSystem::addAction('head.meta', [$this, 'meta']);

    // Tweak every video card
    HookSystem::addFilter('video.card.html', [$this, 'badge'], 20);
}
Self-contained by design. A plugin should add everything it needs — its own schema, settings, routes, admin pages and scheduled tasks — and remove them again on uninstall, without changes to the base CMS. The CTR Ranking plugin is a good model of this.

Scheduled work in a plugin

Register a recurring task with the CronManager from boot() — it runs on the built-in pseudo-cron, so no system crontab is required. This is how the CTR plugin recalculates scores and Backup Pro runs scheduled backups.

Bundled plugins as references

Distributing & licensing

You can keep a plugin private, share it freely, or sell it through the marketplace, which adds signed licensing and per-domain activation. See Theme development for the equivalent on the presentation side, and the Theme API reference for everything available to template code.

Still stuck?

Open a ticket from your dashboard and our team will help you out.

Try the live demo → Download TubePress →