agentleFS
AnyOrgNameAnyOrgName/php-skills/skills/bitrix/d7-events/SKILL.md

d7-events

Skill4 starsChanged 4 months agoMITView on GitHub

Register and handle 1C-Bitrix events with the D7 EventManager — addEventHandler, sending and handling Event objects, module vs global events, handler priority (sort), and D7-to-legacy event compatibility. Use when the user needs to control the order in which handlers run for an event — setting the sort/priority argument so one handler runs before or after another; the user is declaring a module's event handlers — registering them from the module's `include.php`/installer, or hooking events of another module such as `main:OnBeforeUserAdd` or `sale:OnSaleOrderSaved`; the user needs to register an event handler — `EventManager::addEventHandler()` (D7) or the legacy `AddEventHandler()` — to react to a module or system event; the user is firing a custom event so other code can hook in — constructing an `\Bitrix\Main\Event` and calling `$event->send()`, or reading results back from its handlers. Keywords: bitrix, events, eventmanager, addeventhandler, handler, priority, sort, module-events, d7, legacy, битрикс,…

The file

We split this skill into the rules it gives an agent

9 rules. About 0 are common, 6 are distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈6

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Do not use this skill for scheduled background work (Bitrix agents, cron).

    line 89
  2. Very specific:

    Do not use this skill for a non-Bitrix event system (Symfony EventDispatcher, Laravel events, PSR-14).

    line 91
  3. Very specific:

    Put the handler in an autoloadable class with a static method, e.g.

    line 133
  4. Distinctive:

    Verify: The handler class autoloads and its static method is callable.

    line 135
  5. Distinctive:

    Verify: A fresh request fires the handler without any page-level registration.

    line 138
  6. Distinctive:

    Verify: Invalid input is rejected and a veto returns EventResult::ERROR, not an exception.

    line 141
  7. Distinctive:

    Verify: Handlers registered for that module and event id are invoked.

    line 157
  8. Distinctive:

    Verify: A handler returning EventResult::ERROR halts the caller's action.

    line 160
  9. Very specific:

    When your handler must run before or after another for the same event, pass an explicit sort value to addEventHandler.

    line 173

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md
<!-- generated: do not edit — source: knowledge/atoms/bitrix/d7-events/ --><!-- schema: atomVersion=3 skillVersion=2 graphVersion=1 --><!-- compile: @php-skills/compiler v1.0.0 (deterministic build) -->

Bitrix D7 Events

When to use

  • The user needs to control the order in which handlers run for an event — setting the sort/priority argument so one handler runs before or after another.
  • The user is declaring a module's event handlers — registering them from the module's include.php/installer, or hooking events of another module such as main:OnBeforeUserAdd or sale:OnSaleOrderSaved.
  • The user needs to register an event handler — EventManager::addEventHandler() (D7) or the legacy AddEventHandler() — to react to a module or system event.
  • The user is firing a custom event so other code can hook in — constructing an \Bitrix\Main\Event and calling $event->send(), or reading results back from its handlers.

When NOT to use

  • Do not use this skill for scheduled background work (Bitrix agents, cron); those run on a timer, whereas events fire in response to an action.
  • Do not use this skill for a non-Bitrix event system (Symfony EventDispatcher, Laravel events, PSR-14); the \Bitrix\Main\EventManager/Event API is Bitrix-specific.

Core guidance

  • Prefer the D7 EventManager for new handlers — In new code register handlers through \Bitrix\Main\EventManager::getInstance()->addEventHandler() rather than the legacy AddEventHandler(); keep the legacy call only when patching legacy code.

Decision Engine

Walk this section before choosing an implementation path.

Are you registering a new handler, or editing an existing legacy AddEventHandler call?

├── Editing legacy handler: → legacy — keep the existing AddEventHandler call and make a minimal local edit; do not rewrite it to EventManager opportunistically.

└── New handler: Is this a handler that belongs to a module and should fire on every request it is active for?

    ├── Module-scoped handler: → eventmanager-include — register via EventManager in the module include.php, pointing at a static handler method with an explicit sort.

    └── Ad hoc handler: → eventmanager-runtime — call addEventHandler at runtime for this request only when the hook is genuinely one-off and not module-wide.

Full engine: references/decision-engine.md — Decision Tree, Priority Matrix, Decision Table.

Workflows

Workflow: Register Module Handler

Progress:
- [ ] Put the handler in an autoloadable class with a static method, e.g
- [ ] In the module `include.php` call
`EventManager::getInstance()->addEventHandler('iblock', 'OnAfterIBlockElementUpdate', [Handler::class, 'onIblockElementUpdate'], false, $sort)`
- [ ] Inside the handler, read `$event->getParameter('...')`, validate and cast it, do
minimal work, and return an `EventResult` when you need to veto or extend the action
  1. Put the handler in an autoloadable class with a static method, e.g. Vendor\Module\EventHandlers::onIblockElementUpdate(\Bitrix\Main\Event $event).
    • Verify: The handler class autoloads and its static method is callable.
  2. In the module include.php call EventManager::getInstance()->addEventHandler('iblock', 'OnAfterIBlockElementUpdate', [Handler::class, 'onIblockElementUpdate'], false, $sort).
    • Verify: A fresh request fires the handler without any page-level registration.
  3. Inside the handler, read $event->getParameter('...'), validate and cast it, do minimal work, and return an EventResult when you need to veto or extend the action.
    • Verify: Invalid input is rejected and a veto returns EventResult::ERROR, not an exception.

Workflow: Send Custom Event

Progress:
- [ ] Build the event with
`$event = new \Bitrix\Main\Event('vendor.module', 'OnSomethingHappened', $params);`
and call `$event->send();` at the extension point
- [ ] Iterate `$event->getResults()` and, if any `EventResult` has type
`EventResult::ERROR`, treat the action as vetoed and stop
  1. Build the event with $event = new \Bitrix\Main\Event('vendor.module', 'OnSomethingHappened', $params); and call $event->send(); at the extension point.
    • Verify: Handlers registered for that module and event id are invoked.
  2. Iterate $event->getResults() and, if any EventResult has type EventResult::ERROR, treat the action as vetoed and stop.
    • Verify: A handler returning EventResult::ERROR halts the caller's action.

Best practices

  • Handlers for D7 events sent via Event::send() receive one Event object; some legacy/core events are still raised the old way and pass positional arguments — write the handler signature to match how that specific event is fired
  • Register a module's event handlers in its include.php (loaded whenever the module is included), pointing at a static handler class method, instead of calling addEventHandler from page or component code
  • To veto or alter an action from a D7 handler, return an \Bitrix\Main\EventResult with the appropriate type (for example EventResult::ERROR), rather than throwing an exception from the handler
  • When your handler must run before or after another for the same event, pass an explicit sort value to addEventHandler; lower sort runs first, and the default (100) leaves order undefined relative to other defaults
  • Register handlers as a [Namespace\\Class::class, 'onEvent'] static callable, not an inline closure, so the handler is autoloadable and does not run its class body until the event fires

Anti-patterns

Anti-patternWhy it failsDo instead
Registering a closure handler from include.phpA closure defined in include.php loads its whole enclosing scope eagerly, cannot<br>be autoloaded on demand, and is harder to test or unregister.Move the handler into an autoloadable handler class and register<br>[Handler::class, 'onEvent'].
Doing heavy or blocking work inside a handlerThe handler runs inline with the action that raised the event, so slow work<br>blocks the user request and can time out or partially apply.Do minimal work in the handler and offload heavy processing to an agent, a queue,<br>or a background job.
Throwing an exception to cancel an actionThe event system expects an EventResult to decide whether to continue; an<br>uncaught throw escapes it and destabilises the whole dispatch.Return an \Bitrix\Main\EventResult with the appropriate type (for example<br>EventResult::ERROR) to veto the action cleanly.

See references/anti-patterns.md.

Debugging

  1. Duplicate registration makes a handler run twice — Each addEventHandler call adds another entry; the event system does not deduplicate. Fix: Register each handler in exactly one place — the module include.php — and remove any redundant page-level or installer registrations.
  2. Handler never fires because registration ran too late — addEventHandler only affects events sent after registration, not ones already dispatched. Fix: Register the handler early, from the module's include.php, so it is in place before the event is raised.
  3. Wrong handler signature for a D7 vs legacy event — D7 events pass one Event object while legacy events pass positional arguments. Fix: Check how the specific event is fired and match the signature — accept a single \Bitrix\Main\Event for D7 events, positional parameters for legacy ones.

Production checklist

  • The module that raises the event you hook is included with a checked Loader::includeModule before registering the handler, so the event actually fires.
  • Every module-wide handler is registered from the module's include.php via the EventManager, not from page, template, or component code.
  • Each handler's signature matches the calling convention of the event it hooks — a single Event object for D7 events, positional arguments for legacy events.
  • When more than one handler listens to an event and order matters, each passes an explicit sort value rather than depending on registration order.
  • Each handler is a [Class::class, 'method'] static callable whose class is autoloadable, not an inline closure or a call into unloaded code.
  • Values read from an event's parameters are validated and cast before being used in queries, filesystem paths, or other side effects, since they may originate from user input.

Version notes

  • The D7 EventManager supersedes AddEventHandler — \Bitrix\Main\EventManager with Event/EventResult objects is the current D7 event API; the legacy AddEventHandler()/ExecuteModuleEventEx() functions remain as a compatibility layer, and many core events are still raised the legacy way.

Security

  • Treat event parameters as untrusted input — Data carried in an event's parameters can originate from user input (a submitted form, an API body, an edited element); validate and cast it inside the handler before using it in SQL, filesystem paths, or other side effects.

Testing

  • Activation and decision behavior are verified in eval/scenarios/bitrix/d7-events.yaml.

Additional resources

  • references/anti-patterns.md
  • references/core.md
  • references/decision-engine.md
  • references/examples.md
  • references/pitfalls.md
  • references/recipes.md
  • references/version-differences.md

Knowledge graph

Prerequisites: bitrix/kernel-main-module
Related: ai-rules/strategy-bitrix-d7-incremental, bitrix/d7-loader
Behavior constraints: ai-rules/strategy-legacy-safe-edits

<!-- graph-fragment: graph/fragments/bitrix/d7-events.yaml -->

Skill contract

Produces: events.d7-handler-registered
Requires: bitrix.module-loaded
Risk: medium · Confidence: 0.9

<!-- contract-fragment: contracts/fragments/bitrix/d7-events.yaml -->

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.