Extension Mechanisms Overview
Four Core Mechanisms
- Plugins — intercept specific method calls
- Observers — react to dispatched events
- Preferences — replace entire class implementations
- Service Contracts — define stable API interfaces
How They Relate
Service Contracts define the API
-> Preferences replace implementations
-> Plugins modify behavior on top
-> Observers react to side effects
Comparison Table
| Feature | Plugin | Observer | Preference | Service Contract |
|---|---|---|---|---|
| Scope | Method | Event | Class | Interface |
| Modify args | Yes | No | Yes | N/A |
| Modify returns | Yes | No | Yes | N/A |
| Skip methods | Yes (around) | No | Yes | N/A |
| One-to-many | No | Yes | No | No |
| Composable | Yes | Yes | No | Yes |
| Upgrade safe | High | High | Low | High |
| Testability | High | Medium | Medium | High |
Decision Matrix
When to Use What
| Requirement | Best Mechanism | Why |
|---|---|---|
| Modify method return value | Plugin (after) | Direct access to return value |
| Modify method arguments | Plugin (before) | Can change args before execution |
| Skip method execution | Plugin (around) | Can choose not to call $proceed |
| React to action (no modification) | Observer | Lightweight, one-to-many |
| Send notification after action | Observer | Side effect, fire-and-forget |
| Replace interface implementation | Preference | Full class replacement |
| Add new methods to class | Preference | Only way to add methods |
| Define stable API | Service Contract | Interface-based, upgrade safe |
| Add custom fields to entity | Extension Attributes | Standard pattern for entities |
| Cross-module communication | Observer | Decoupled, multiple listeners |
| Method-level caching | Plugin (around) | Can cache per method call |
| Profiling method execution | Plugin (around) | Access to method timing |
Combining Mechanisms
// Service Contract defines the interface
interface ProductRepositoryInterface {
public function get($sku, $editMode = false, $storeId = null, $forceReload = false);
}
// Preference provides default implementation
class ProductRepository implements ProductRepositoryInterface {
public function get($sku, ...) { /* ... */ }
}
// Plugin adds caching
public function aroundGet($subject, $proceed, $sku, ...) {
return $this->cache->remember('product_' . $sku, fn() => $proceed($sku, ...));
}
// Observer handles side effects after save
$this->eventManager->dispatch('catalog_product_save_after', ['product' => $product]);
Maintainability Trade-offs
Plugin Maintainability
Pros:
- Composable across modules
- Easy to disable
- Upgrade safe
- Granular control
Cons:
- Generated code can be confusing
- Debugging requires understanding interception
- Too many plugins on one method create complex chains
Observer Maintainability
Pros:
- Clean separation of concerns
- Easy to add/remove
- One-to-many patterns
- No method coupling
Cons:
- Hard to trace execution flow
- No return values
- Event data can be modified unexpectedly
- Debugging requires logging
Preference Maintainability
Pros:
- Full control over class behavior
- Can add new methods
- Direct override
Cons:
- Only one per class
- Upgrade fragile
- Hard to compose
- Can break other modules
Service Contract Maintainability
Pros:
- Stable API across versions
- Interface-based testing
- Clear boundaries
- Upgrade safe
Cons:
- More upfront design
- Cannot modify existing contracts
- Requires versioning strategy
Performance Comparison
Performance Metrics
| Mechanism | Overhead | Memory | Scalability |
|---|---|---|---|
| Plugin | ~0.1ms/call | Higher (generated classes) | Degrades with many plugins |
| Observer | ~0.05ms/call | Lower | Good |
| Preference | ~0.02ms/call | Minimal | Best |
| Service Contract | None (interface only) | None | Best |
Performance Best Practices
// Bad: too many plugins on one method
<type name="Product">
<plugin name="plugin1" type="..." sortOrder="10"/>
<plugin name="plugin2" type="..." sortOrder="20"/>
<plugin name="plugin3" type="..." sortOrder="30"/>
<plugin name="plugin4" type="..." sortOrder="40"/>
<plugin name="plugin5" type="..." sortOrder="50"/>
</type>
// Good: consolidate into one plugin with internal logic
<type name="Product">
<plugin name="product_enhancer" type="Vendor\Plugin\ProductEnhancer" sortOrder="10"/>
</type>
Decision Checklist
- Need to modify args/returns? -> Plugin
- Need side effects? -> Observer
- Need full class replacement? -> Preference
- Need stable API? -> Service Contract
- Need custom fields? -> Extension Attributes
- Can multiple modules extend? -> Plugin or Observer (not Preference)
- Upgrade safety critical? -> Plugin, Observer, or Service Contract
Practice Problems
Design an extension strategy for: (1) adding loyalty points after order placement, (2) modifying shipping cost calculation, (3) replacing the search engine implementation.
Quiz
1. Which mechanism is best for one-to-many notifications?
2. Which mechanism has the lowest per-call overhead?
3. What is the primary purpose of service contracts?
4. When should you combine plugins with observers?
Flashcards
Question
Best mechanism for modifying return values?
Click to reveal answer
Answer
After plugin — direct access to return value
Question
Best mechanism for one-to-many side effects?
Click to reveal answer
Answer
Observer — one event, multiple listeners
Question
Best mechanism for full class replacement?
Click to reveal answer
Answer
Preference — replaces entire class implementation
Question
Best mechanism for stable API?
Click to reveal answer
Answer
Service Contract — interface-based, upgrade safe
Question
Best mechanism for custom entity fields?
Click to reveal answer
Answer
Extension Attributes — standard pattern with interface
Revision Notes
Key Takeaways
- 1. Plugins: method-level modification, composable, upgrade safe
- 2. Observers: event-driven side effects, one-to-many, decoupled
- 3. Preferences: full class replacement, one per class, upgrade fragile
- 4. Service Contracts: stable API interfaces, upgrade safe
- 5. Extension Attributes: standard pattern for adding custom entity fields
- 6. Combine mechanisms: plugins for behavior, observers for side effects
Interview Tips
- • Present the decision matrix with concrete examples
- • Explain when to combine plugins and observers
- • Discuss maintainability trade-offs between mechanisms
- • Give performance comparison numbers
Cheat Sheet
Extension Trade-offs Cheat Sheet
Decision flow:
- Modify args/returns? -> Plugin
- Side effects? -> Observer
- Replace class? -> Preference
- Stable API? -> Service Contract
- Custom fields? -> Extension Attributes
Performance:
- Plugin: ~0.1ms
- Observer: ~0.05ms
- Preference: ~0.02ms
- Service Contract: 0ms
Upgrade safety:
- High: Plugin, Observer, Service Contract
- Low: Preference
Composable:
- Yes: Plugin, Observer
- No: Preference