Pular para conteúdo

Hooks

O MIDDAG possui dois tipos de hooks: actions (side effects) e filters (transformacao de valor). Ambos são pontos de extensão públicos, registrados via hook_manager.


Dois tipos de hook

Tipo Operacao Retorno Semantica de falha
Action Side effect (reagir a um fato) void Lateral: falha isolada, demais listeners continuam
Filter Transformar um valor em pipeline Valor modificado Obrigatoria: falha propaga

Registrar uma action

Actions reagem a um evento sem retornar valor. Use add_action():

use local_middag\facade\hook_manager;

// Em boot()
hook_manager::add_action('middag/item/created', function (array $payload): void {
    // Reagir a criação do item
    $item_id = $payload['id'];
    // ... enviar notificação, atualizar cache, etc.
}, priority: 10);

Disparar uma action:

hook_manager::do_action('middag/item/created', ['id' => $item->get_id()]);

Registrar um filter

Filters transformam um valor em pipeline. Cada callback recebe o valor atual e deve retornar o valor modificado:

use local_middag\facade\hook_manager;

hook_manager::add_filter('middag/item/title', function (string $title, array $context): string {
    return strtoupper($title);
}, priority: 20);

Aplicar um filter:

$title = hook_manager::apply_filters('middag/item/title', $raw_title, ['locale' => 'pt_BR']);

Subscriber com #[on] attribute

Para auto-discovery, use o atributo #[on] nos métodos de um subscriber. O framework registra automaticamente os callbacks durante boot():

use local_middag\framework\contract\attributes\on;

class item_lifecycle_subscriber
{
    #[on('middag/item/created')]
    public function handle_item_created(array $payload): void
    {
        // Reagir a criação
    }

    #[on('middag/item/title')]
    public function transform_title(string $title, array $context): string
    {
        // Retorno não-void = registrado como filter
        return mb_strtoupper($title);
    }
}

O tipo de retorno determina o comportamento:

  • void → registrado como add_action()
  • qualquer outro tipo → registrado como add_filter()

Subscriber no nível de classe

O #[on] pode ser aplicado na classe para definir um type padrão herdado pelos métodos:

use local_middag\framework\contract\attributes\on;
use local_middag\framework\shared\enum\signal;

#[on(type: 'product')]
class product_lifecycle
{
    #[on(Signal::CREATED)]                   // herda type: 'product'
    public function on_created(/* ... */): void {}

    #[on(Signal::SAVED, type: 'variant')]    // override para 'variant'
    public function on_variant_saved(/* ... */): void {}
}

Convencao de nomes

Hooks públicos seguem o padrão:

Padrao Exemplo Uso
middag/{aggregate}/{action} middag/item/created Acao geral sobre aggregate
middag/{aggregate}/{action}/{type} middag/item/created/product Acao específica por tipo
middag/extension/{slug}/{action} middag/extension/ecommerce/order_processed Evento de extension

Signals é a ponte para hooks

O framework possui um modelo reativo em tres camadas:

graph LR
    S["Signal (dispatch)"] -->|auto-bridge| H["Action hook (do_action)"]
    H -.->|nunca| S
    F["Filter (apply_filters)"] -.->|independente| F
  1. Signals são o mecanismo interno (objetos PHP tipados via Symfony EventDispatcher).
  2. Todo dispatch($signal) gera automaticamente um do_action() correspondente.
  3. Hooks são o ponto de extensão público. Extensions e plugins reagem via hooks, não via signals diretamente.
  4. A direcao é unidirecional: signals disparam hooks; hooks não disparam signals.

Priority

O parametro priority segue a convenção WordPress: menor valor executa primeiro.

hook_manager::add_action('middag/item/created', $callback_a, priority: 5);  // executa primeiro
hook_manager::add_action('middag/item/created', $callback_b, priority: 10); // executa depois
hook_manager::add_action('middag/item/created', $callback_c, priority: 20); // executa por ultimo

O valor padrão e 10.


Verificar existência

if (hook_manager::has_action('middag/item/created')) {
    // Pelo menos um listener registrado
}

Monitoramento

Hooks que excedem 100ms de execução geram warnings no log. O threshold e configuravel.


Onde registrar hooks

Contexto Local
Extension interna No método boot() da extension
Plugin Moodle externo Via Hooks API (veja Modos de Integração)
Customizacao rapida Via Hooks File (middag_hooks.php)

Referencias