# Hooks and events

## Two shapes

**Actions** announce that something happened. Return values are ignored.

**Filters** transform a value and must return the same shape.

The catalog is intentionally small — every public hook is a compatibility promise. The authoritative list is `App\Support\CoreHooks::catalog()`; the table is in [Hooks reference](../05-extensions/HOOKS-REFERENCE.md).

## Registration

```php
use App\Support\CoreHooks;
use App\Support\Hooks;

Hooks::on(CoreHooks::ANSWER_ACCEPTED, function (array $payload): void {
    $questionId = (int) ($payload['question_id'] ?? 0);
    $answerId   = (int) ($payload['answer_id'] ?? 0);
    // …
});
```

Use the constants. A hook rename then breaks at review and in tests instead of silently disabling a plugin in production.

## Timing

Post-success events fire **after** the transaction commits. A handler for `question.published` can read the published row; a handler that fired inside the transaction could observe state that later rolled back.

## Isolation

A handler that throws is caught and logged. The request continues, the site stays up, and the admin panel stays reachable. One plugin cannot take a site down through a hook.

This isolation is **not** a licence to throw. A handler that fails silently every request is a bug that only shows up in the log.

## Security boundaries

A filter cannot bypass a security decision. Returning `true` from a generic filter does not:

- grant a permission
- skip CSRF validation
- publish content that moderation policy holds
- authenticate an account that cannot sign in

Security checks are made by the core, on the server, after filters have run.

## Payload contract

Payloads may gain keys. Existing keys do not change meaning. Read defensively with `??` and casts, and never assume a key that the documentation does not list.

## Performance

Handlers execute inline, in the request. There is no queue and no cron. Do not perform slow network calls in a hook that fires on every page render — move that work to an explicit admin action.

## Where core emits

`AuthService` (`user.registered`), `QuestionService` (`question.created`, `question.published`), `AnswerService` (`answer.created`, `answer.accepted`), `CommentService` (`comment.created`, `topic.comment_created`), `AdminRepository` (`answer.accepted`, `answer.published`), the SEO layer (`seo.meta`, `seo.structured_data`), the sitemap service (`sitemap.entries`), and the spam service (`spam.assessment`).
