Fundamental Differences
Plugins
- Intercept specific method calls
- Can modify arguments and return values
- Applied via di.xml configuration
- Generated interceptor classes
- Execute per method call
Observers
- React to dispatched events
- Cannot modify return values directly
- Applied via events.xml configuration
- Use Event Manager dispatch pattern
- Execute per event dispatch
Key Distinction
Plugin: Method called -> Before -> Original -> After -> Return
Observer: Event dispatched -> Observers notified -> (no return value)
Plugins have direct access to method arguments and return values. Observers only see what the dispatcher passes in the event data.
When to Use Plugins
Best Plugin Use Cases
- Modifying method return values
public function afterGetName($subject, $result) {
return $result . ' - Custom Suffix';
}
- Modifying method arguments
public function beforeSave($subject, $product) {
$product->setData('custom_field', 'value');
return [$product];
}
- Adding pre/post logic to specific methods
public function aroundExecute($subject, $proceed) {
$this->logger->info('Request started');
$result = $proceed();
$this->logger->info('Request completed');
return $result;
}
- Method-level profiling and caching
public function aroundGetById($subject, $proceed, $id) {
$cacheKey = 'product_' . $id;
$cached = $this->cache->load($cacheKey);
if ($cached) return unserialize($cached);
$result = $proceed($id);
$this->cache->save(serialize($result), $cacheKey);
return $result;
}
- Conditional method skipping
public function aroundExecute($subject, $proceed) {
if (!$this->shouldRun()) {
return null;
}
return $proceed();
}
When to Use Observers
Best Observer Use Cases
- Reacting to actions without modifying results
public function execute(Observer $observer) {
$order = $observer->getEvent()->getOrder();
$this->notificationService->sendOrderConfirmation($order);
}
- Triggering side effects
public function execute(Observer $observer) {
$product = $observer->getEvent()->getProduct();
$this->searchIndexer->reindexProduct($product->getId());
}
- Cross-module communication
Module A dispatches: 'order_placed'
Module B observes: sync inventory
Module C observes: send loyalty points
Module D observes: update analytics
- One-to-many notifications
// One event, multiple observers across modules
$this->eventManager->dispatch('customer_registered', [
'customer' => $customer
]);
// Observer 1: send welcome email
// Observer 2: create loyalty account
// Observer 3: sync to CRM
// Observer 4: track analytics
- Fire-and-forget operations
public function execute(Observer $observer) {
$this->asyncQueue->addJob(
new SendEmailJob($observer->getEvent()->getOrder())
);
}
Performance and Testability
Performance Comparison
| Aspect | Plugin | Observer |
|---|---|---|
| Overhead per call | ~0.1-0.15ms | ~0.05-0.1ms |
| Memory usage | Higher (generated classes) | Lower |
| Method-level impact | Yes (interceptor overhead) | No (only when dispatched) |
| Scalability | Degrades with many plugins | Better for high-frequency events |
Testability
Plugins:
// Easy to test - just call the method
$product = new Product();
$result = $product->getName();
// Plugin logic executes automatically
Observers:
// Need to dispatch event to trigger observer
$eventManager = $this->objectManager->get(EventManager::class);
$eventManager->dispatch('my_event', ['data' => $data]);
// Assert side effects separately
Limitations Summary
| Limitation | Plugin | Observer |
|---|---|---|
| Modify return values | Yes | No |
| Modify arguments | Yes | No |
| Skip method execution | Yes (around) | No |
| Access method context | Yes (all args) | Only event data |
| One-to-many | No (1 plugin = 1 class) | Yes |
| Area-specific | All areas | Configurable |
| Works on protected methods | No | N/A |
| Requires event dispatch | No | Yes |
Practice Problems
Given a requirement to send a notification after an order is placed AND modify the order total before saving, determine which mechanisms to use.
Quiz
1. Which mechanism can modify a method's return value?
2. Which is better for one-to-many notifications?
3. What is the main advantage of plugins over observers?
4. When should you prefer an observer over a plugin?
Flashcards
Question
Can observers modify method return values?
Click to reveal answer
Answer
No, only plugins can modify return values via after/around plugins
Question
What is the key difference between plugins and observers?
Click to reveal answer
Answer
Plugins intercept methods; observers react to events. Plugins can modify args/returns; observers cannot
Question
Which is better for one-to-many scenarios?
Click to reveal answer
Answer
Observers — one event dispatch triggers multiple observers across modules
Question
Can plugins skip method execution?
Click to reveal answer
Answer
Yes, around plugins can choose not to call $proceed to skip the original method
Question
Which has lower per-call overhead?
Click to reveal answer
Answer
Observers (~0.05ms) have lower overhead than plugins (~0.1ms per interceptor)
Revision Notes
Key Takeaways
- 1. Plugins intercept specific methods; observers react to dispatched events
- 2. Plugins can modify arguments and return values; observers cannot modify returns
- 3. Observers are better for one-to-many notifications and side effects
- 4. Plugins have slightly higher per-call overhead due to generated interceptors
- 5. Use plugins when you need to modify method behavior; observers for reactive side effects
- 6. Both can be combined — plugins for method modification, observers for notifications
Interview Tips
- • Give concrete examples of when to use plugins vs observers
- • Discuss the trade-offs in performance and testability
- • Explain why observers cannot modify return values
- • Describe a scenario combining both mechanisms
Cheat Sheet
Plugin vs Observer Cheat Sheet
Use Plugin when:
- Modify return values
- Modify arguments
- Skip method execution
- Method-level caching
- Profiling specific methods
**Use Observer when:
- React to events (side effects)
- One-to-many notifications
- Cross-module communication
- Fire-and-forget operations
- Logging/auditing
Performance:
- Plugin: ~0.1ms per call
- Observer: ~0.05ms per call
Config:
- Plugin: di.xml
- Observer: events.xml