An online store on Bitrix with a catalog of 10,000 products. A customer places an order, and you need to check stock in 1C, reserve the item, send data to CRM, and update the warehouse — all within 0.5 seconds. Without well-designed event handlers, each of these steps either slows down the site, breaks the chain, or sometimes crashes the page. We design event-driven architecture so that all extra operations run asynchronously — without losing speed or causing failures. Over the years, we've implemented over 50 projects where the event model became the foundation. The average budget savings for the client was 30% by eliminating unnecessary synchronous requests. A properly written handler does not slow down the site, create cyclic calls, or break adjacent logic. Let's break down the key points.
Anatomy of an Event in Bitrix
The core generates events at key points via the EventManager class. Each event is tied to a module and has a name. A handler is registered with a call:
use Bitrix\Main\EventManager; EventManager::getInstance()->addEventHandler( 'sale', 'OnSaleOrderBeforeSaved', [\MyProject\Sale\OrderHandler::class, 'onBeforeSave'], 100 ); Registration happens in /local/php_interface/init.php — this file is included on every hit. For portable logic, handlers are registered in the installEvents() method of a custom module. Before-events (OnBefore*) allow modifying data before saving or aborting the operation by returning an EventResult with an error. After-events (OnAfter*) are for reacting to an already completed action.
Key Events by Module
| Module | Event | Trigger | Type |
|---|---|---|---|
iblock |
OnBeforeIBlockElementAdd |
Before adding an infoblock element | Before |
iblock |
OnAfterIBlockElementUpdate |
After updating an element | After |
sale |
OnSaleOrderBeforeSaved |
Before saving an order | Before |
sale |
OnSalePayOrder |
When order is paid | After |
catalog |
OnAfterCatalogImport1C |
After exchange with 1C | After |
main |
OnAfterUserAuthorize |
After authorization | After |
main |
OnBeforeProlog |
Before page generation | Before |
Full list in the official documentation.
How to Properly Organize Dozens of Handlers?
In a real project, you might have 20–50 handlers. Without structure, init.php becomes an unmanageable mess. We use this scheme:
/local/php_interface/ ├── init.php → only includes registration files ├── handlers/ │ ├── SaleHandlers.php → registration of sale module events │ ├── IblockHandlers.php → registration of iblock module events │ └── MainHandlers.php → registration of main module events └── classes/ ├── OrderEventHandler.php → order handler logic ├── CatalogEventHandler.php └── UserEventHandler.php init.php only contains require_once. Registration files contain only addEventHandler calls. Handler classes have static methods with single responsibility.
Why Not to Use Heavy Operations in Before-Events?
OnSaleOrderBeforeSaved is called multiple times during checkout — each recalculation triggers the event. An HTTP request to an external API inside it slows down the page by 5–10 times. Heavy operations are moved to After-events or agents. Comparison: a handler via an agent executes 40% faster than a synchronous request.
How to Design a Handler for 1C Integration?
Here's a step-by-step example for the OnAfterCatalogImport1C event.
- Determine the trigger moment — after catalog import from 1C.
- Register the handler with sort 200, so it runs after standard ones.
- Inside the handler, check the import type (full or partial) and update stock via 1C REST API.
- Execute the request asynchronously using a background agent to avoid blocking the hit.
- Log the result with
Debug::writeToFile()for later analysis.
This approach ensures the site doesn't slow down and data in 1C and Bitrix stays synchronized.
Critical Errors and Their Solutions
Cyclic call. A handler for OnAfterIBlockElementUpdate updates the infoblock element — recursion occurs. Protection via a static flag:
class CatalogEventHandler { private static bool $inProgress = false; public static function onAfterElementUpdate(array $arFields): void { if (self::$inProgress) { return; } self::$inProgress = true; try { // update logic } finally { self::$inProgress = false; } } } No exception handling. An unhandled exception can crash the page. At minimum — try/catch with logging via Debug::writeToFile().
Execution order. Multiple handlers on the same event execute in ascending order of sort. If your handler depends on another — explicitly set sort; default is 100.
How to Test Event Handlers?
Use the perfmon module for profiling. Run a test scenario (order placement, element add) and measure execution time. Disable all handlers, then enable one by one. Compare timings. If the scenario takes 0.5s without handlers and 3s with them, find the bottleneck. We also use PHPUnit unit tests for isolated handler logic. Our experience shows this approach reduces incidents by half.
Checklist: Common Mistakes in Handler Development
- Forgot to handle exceptions — page crashes with 500 error.
- Didn't account that a Before-event can be called multiple times — data gets corrupted.
- Register handler in init.php without checking module — error when module is disabled.
- Use
$GLOBALSto pass data between handlers — context loss. - Don't check field existence in
$arFields— accessing non-existent key.
What's Included in the Work
- Audit of existing handlers and identification of bottlenecks.
- Architecture design: separation by modules and responsibilities.
- Registration and coding of handlers with protection against cyclic calls.
- Integration with external services (CDEK, 1C, payment gateways) in After-events.
- Documentation for each handler.
- Testing and profiling using the
perfmonmodule. - Code warranty and support for 1 month.
Approximate Timelines
| Task | Timeline |
|---|---|
| 1–3 simple handlers (notifications, logging, field filling) | 2–3 days |
| Complex logic (external service integration, validation, recalculation) | 5–10 days |
| Refactoring existing handlers (audit, reorganization, conflict resolution) | 1–2 weeks |
Cost is calculated individually. Order event handler development to avoid typical mistakes. Get a consultation on handler architecture and a preliminary estimate within 1 business day — contact us.
The approach we use is based on event-driven programming. This allows flexible functionality extension without core modifications. Event handlers are a key tool for scaling Bitrix projects.
Source: Official 1С-Bitrix documentation.







