Skip to main content

Add WordPress hooks

Simply registers WordPress actions and filters in two ways:

Timing

Hooks are registered on the after_setup_theme action, when the framework builds its container. Hooks that run earlier (plugins_loaded, setup_theme...) are not triggered: register them with add_action() in your plugin file.

With PHP attributes​

Add #[Action] or #[Filter] to a public method of a service (any class of the src/ directory of a registered plugin or theme). No tag nor configuration is needed.

<?php

namespace MyPlugin\Hook;

use Simply\Core\Attributes\Action;

class Setup
{
#[Action('init')]
public function registerBlocks(): void
{
// ...
}
}

Both attributes take (string $hook, int $priority = 10, int $numberArguments = 1), and can be repeated on the same method. Give optional parameters a default value: other code may apply the same hook with fewer arguments.

  • The service is created when WordPress runs the hook, not on every request: inject what you need in the constructor without worrying about performance.
  • The method must be public, and must not take a parameter by reference: otherwise the container build fails with an explicit message. For a hook that passes values by reference (do_action_ref_array), use HookableInterface.
  • A service whose id is not its class name, and several services of the same class, all get their hooks.

Use other services lazily​

Constructor injection creates the dependencies with the hook service. To create a dependency only when it is used, subscribe to it with ServiceMethodsSubscriberTrait:

<?php

namespace MyPlugin\Hook;

use MyPlugin\Service\TokenGenerator;
use Simply\Core\Attributes\Action;
use Symfony\Contracts\Service\Attribute\SubscribedService;
use Symfony\Contracts\Service\ServiceMethodsSubscriberTrait;
use Symfony\Contracts\Service\ServiceSubscriberInterface;

class LiveEndpoint implements ServiceSubscriberInterface
{
use ServiceMethodsSubscriberTrait;

#[Action('rest_api_init')]
public function registerEndpoint(): void
{
$token = $this->tokenGenerator()->generate();
// ...
}

#[SubscribedService]
private function tokenGenerator(): TokenGenerator
{
return $this->container->get(__METHOD__);
}
}

(ServiceSubscriberTrait, deprecated by Symfony, is still supported.)

Debug the registered hooks​

php bin/console simply:debug:hook --hook_name=the_title

lists the callbacks of a hook with their priority and source file. See the CLI.

Unhooking an attribute hook​

The WordPress callback of an attribute hook is a Simply\Core\Hook\LazyHookListener, not [$service, 'method']: remove_action() with [$service, 'method'] does not find it. Find the listener instead, once the framework is ready:

<?php

use MyPlugin\Hook\Excerpt;
use Simply\Core\Hook\LazyHookListener;

add_action('simply/core/after_bootstrap', function (): void {
global $wp_filter;
foreach ($wp_filter['the_title']->callbacks[10] ?? [] as $callback) {
$listener = $callback['function'];
if ($listener instanceof LazyHookListener && $listener->class === Excerpt::class && $listener->method === 'title') {
remove_filter('the_title', $listener, 10);
}
}
});

LazyHookListener exposes the serviceId, class and method of the hook.

With HookableInterface​

Implement Simply\Core\Contract\HookableInterface: its register() method is called on after_setup_theme, and adds the hooks. The class is tagged wp.hook automatically.

<?php

namespace MyPlugin\Hook;

use Simply\Core\Contract\HookableInterface;

class Excerpt implements HookableInterface
{
public function register(): void
{
add_filter('excerpt_length', [$this, 'length']);
add_filter('excerpt_more', [$this, 'more']);
}

public function length(int $length): int
{
return 30;
}

public function more(string $more): string
{
return '…';
}
}

Unlike attribute hooks, the class is created on every request. simply:make:hook generates this kind of class.

caution

Do not add the wp.hook tag to a class that only uses attributes: the framework would call a register() method that does not exist.

With the CLI​

php bin/console simply:make:hook

creates a HookableInterface class in src/Hook/ and a YAML file declaring it with the wp.hook tag. The tag is already added by autoconfiguration, and this definition replaces the one Simply creates for the class: if the class has constructor dependencies, add autowire: true to it, or delete the YAML file.