Event Dispatching Internals
The ManagerInterface
Magento dispatches events through Magento\Framework\Event\ManagerInterface. The concrete implementation is Magento\Framework\Event\Manager.
$eventManager->dispatch(
'catalog_product_save_after',
['product' => $product, 'controller' => $this]
);
What Happens During dispatch()
- The Event Manager creates an
Eventobject - Event data is merged into the Event object
- All observers registered for the event name are collected
- Observers are sorted by
sort_orderfrom events.xml - Each observer's
execute()is called in order - The Event object (possibly modified) is returned
// Simplified internal flow
public function dispatch($eventName, array $data = [])
{
$event = $this->eventFactory->create();
$event->setName($eventName);
$event->setData($data);
$observers = $this->config->getObservers($eventName);
$observers = $this->sortObservers($observers);
foreach ($observers as $observerConfig) {
$observer = $this->createObserver($observerConfig);
$observer->dispatch($event);
}
return $event;
}
Event Propagation
Events propagate across areas. A global events.xml registers observers for all areas. Area-specific files add or override observers for that area.
<!-- etc/events.xml (global) -->
<event name="catalog_product_save_after">
<observer name="global_log" instance="Vendor\Logger\Observer\Log"/>
</event>
<!-- etc/adminhtml/events.xml (admin only) -->
<event name="catalog_product_save_after">
<observer name="admin_audit" instance="Vendor\Audit\Observer\AdminAudit"/>
</event>
Dispatching Events from Different Areas
Events dispatched in adminhtml area only trigger adminhtml + global observers. Frontend events only trigger frontend + global observers.
// In a frontend controller — frontend + global observers fire
$this->eventManager->dispatch('my_custom_event', ['data' => $data]);
// In an admin controller — adminhtml + global observers fire
$this->eventManager->dispatch('my_custom_event', ['data' => $data]);
Event Data Handling
The Event Object
Magento\Framework\Event\Event extends Magento\Framework\DataObject. This means it supports dynamic getters/setters.
// Data passed during dispatch
$this->eventManager->dispatch('order_place_after', [
'order' => $order,
'quote' => $quote,
'custom_value' => 42
]);
// In observer — accessing data
$order = $observer->getEvent()->getOrder();
$quote = $observer->getEvent()->getQuote();
$value = $observer->getEvent()->getCustomValue();
$allData = $observer->getEvent()->getData();
Modifying Event Data
Observers can modify event data. Changes persist to subsequent observers.
// Observer 1 — modifies the product
public function execute(Observer $observer)
{
$product = $observer->getEvent()->getProduct();
$product->setName($product->getName() . ' (Modified)');
// This change is visible to Observer 2
}
// Observer 2 — sees the modified product
public function execute(Observer $observer)
{
$product = $observer->getEvent()->getProduct();
echo $product->getName(); // 'Product Name (Modified)'
}
Passing Scalar Values
Scalar values in events are passed by value. Modifying them in an observer does not affect other observers.
$this->eventManager->dispatch('calculate_discount', [
'amount' => 100,
'discount' => 0
]);
// Observer 1
public function execute(Observer $observer)
{
$discount = $observer->getEvent()->getDiscount();
$observer->getEvent()->setDiscount($discount + 10);
// This change is visible to other observers via DataObject
}
Best Practices for Event Data
// Pass full objects, not IDs — observers may need relationships
$this->eventManager->dispatch('order_shipped', [
'order' => $order, // Good
'order_id' => $order->getId() // Avoid — forces observer to re-load
]);
// Name data clearly
$this->eventManager->dispatch('inventory_deducted', [
'product' => $product,
'qty_deducted' => $qty,
'source' => $source
]);
Observer Execution Order
sort_order in events.xml
Observers execute based on sort_order attribute. Lower values execute first.
<event name="catalog_product_save_after">
<observer name="first" instance="Vendor\First\Observer\Run"
sort_order="10"/>
<observer name="second" instance="Vendor\Second\Observer\Run"
sort_order="20"/>
<observer name="third" instance="Vendor\Third\Observer\Run"
sort_order="30"/>
</event>
Default sort_order
If no sort_order is specified, observers execute in the order they are loaded — which depends on module load order (alphabetical by module name, then config merge order).
<!-- These run in unpredictable order -->
<observer name="a" instance="Vendor\A\Observer\Run"/>
<observer name="b" instance="Vendor\B\Observer\Run"/>
<!-- These run in predictable order -->
<observer name="a" instance="Vendor\A\Observer\Run" sort_order="100"/>
<observer name="b" instance="Vendor\B\Observer\Run" sort_order="200"/>
Cross-Module Observer Ordering
When multiple modules register observers for the same event, execution order depends on:
- Module load order (app/etc/config.php)
- sort_order values across modules
- Config file merge order
// Check module load order
php bin/magento module:status
php bin/magento setup:module:status
// Check events configuration
grep -r 'catalog_product_save_after' vendor/*/etc/events.xml
Shared Sort Order Ranges
Follow conventions to avoid conflicts:
| Range | Purpose |
|---|---|
| 10-99 | Core Magento observers |
| 100-199 | Third-party extension observers |
| 200-299 | Custom module observers |
| 300-999 | Late-execution observers |
<!-- Your module's observer with explicit sort_order -->
<event name="checkout_cart_product_add_after">
<observer name="my_enhancement"
instance="Vendor\Enhance\Observer\CartEnhance"
sort_order="150"/>
</event>
Event Debugging Techniques
Logging All Dispatched Events
Create a module to log all events:
<!-- Vendor/EventLogger/etc/events.xml -->
<event name="*">
<observer name="event_logger"
instance="Vendor\EventLogger\Observer\LogAll"/>
</event>
namespace Vendor\EventLogger\Observer;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Psr\Log\LoggerInterface;
class LogAll implements ObserverInterface
{
public function __construct(
private LoggerInterface $logger
) {}
public function execute(Observer $observer)
{
$eventName = $observer->getEvent()->getName();
$this->logger->debug('Event dispatched: ' . $eventName, [
'data' => $observer->getEvent()->getData()
]);
}
}
Xdebug Profiling for Events
Set breakpoints in Magento\Framework\Event\Manager::dispatch() to trace events:
// In Manager.php or via IDE breakpoint
public function dispatch($eventName, array $data = [])
{
// Breakpoint here — inspect $eventName and $data
// Step through to see observer execution
}
Magento Event Profiler
Enable event profiling in app/etc/env.php:
return [
'system' => [
'logs' => [
'event_profiling' => [
'type' => 'file',
'file' => 'var/log/event_profile.log'
]
]
]
];
Debugging Observer Loading
// Check which observers are registered for an event
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$config = $objectManager->get(\Magento\Framework\Event\Config\Reader::class);
$observers = $config->getObservers('catalog_product_save_after');
var_dump($observers);
// Or check via CLI
php bin/magento dev:events:list
php bin/magento dev:events:watch catalog_product_save_after
Practice Problems
Two modules register observers for the same event without sort_order. Identify the problem and fix it.
An observer modifies an event object, but a subsequent observer doesn't see the change. Diagnose and fix.
Quiz
1. What attribute controls observer execution order?
2. What class does Magento\Framework\Event\Event extend?
3. If two modules register observers for the same event without sort_order, what determines execution order?
4. How can you log all dispatched events?
Flashcards
Question
What is sort_order in events.xml?
Click to reveal answer
Answer
An attribute controlling observer execution order; lower values execute first
Question
What class does the Event object extend?
Click to reveal answer
Answer
Magento\Framework\DataObject — providing dynamic getters and setters
Question
Can observers modify event data visible to subsequent observers?
Click to reveal answer
Answer
Yes, observers can modify the Event object and changes persist to later observers in the chain
Question
What determines observer order when sort_order is not set?
Click to reveal answer
Answer
Module load order from app/etc/config.php merge sequence
Question
How do you debug which observers are registered for an event?
Click to reveal answer
Answer
php bin/magento dev:events:watch <event_name>
Revision Notes
Key Takeaways
- 1. Event dispatching creates an Event object, collects observers, sorts by sort_order, and executes each
- 2. Event extends DataObject — changes to the Event object persist across observers
- 3. sort_order attribute controls execution order; lower values execute first
- 4. Area-specific events.xml adds observers for that area only; global events.xml applies everywhere
- 5. Debug with wildcard observers, Xdebug breakpoints in Manager::dispatch(), and dev:events:watch CLI
Interview Tips
- • Explain the full lifecycle of event dispatching from dispatch() call to observer execution
- • Discuss how sort_order resolves observer execution conflicts
- • Explain why passing full objects is better than passing IDs in event data
- • Describe debugging approaches for event-driven issues
Cheat Sheet
Events Deep Dive Cheat Sheet
dispatch() flow:
- Create Event object with data
- Collect observers from events.xml
- Sort by sort_order (lower first)
- Execute each observer sequentially
sort_order ranges:
- 10-99: Core
- 100-199: Third-party
- 200-299: Custom
Debug tools:
- Wildcard observer:
<event name="*"> - CLI:
dev:events:watch <event> - Xdebug breakpoint in Manager::dispatch()
Event data:
- Extends DataObject — dynamic getters/setters
- Changes persist to subsequent observers
- Pass full objects, not just IDs