sortOrder Basics
How sortOrder Works
Plugins with lower sortOrder execute first:
<type name="Magento\Catalog\Model\Product">
<plugin name="first" type="Vendor\A\Plugin\A" sortOrder="10"/>
<plugin name="second" type="Vendor\B\Plugin\B" sortOrder="20"/>
<plugin name="third" type="Vendor\C\Plugin\C" sortOrder="30"/>
</type>
Execution for getName():
A::beforeGetName -> B::beforeGetName -> C::beforeGetName
-> Product::getName()
C::afterGetName -> B::afterGetName -> A::afterGetName
Before plugins: ascending sortOrder
After plugins: descending sortOrder (LIFO)
Default sortOrder
If no sortOrder is specified, plugins execute in config merge order:
<!-- These run in unpredictable order -->
<plugin name="a" type="Vendor\A\Plugin\A"/>
<plugin name="b" type="Vendor\B\Plugin\B"/>
Always specify sortOrder for predictable behavior.
sortOrder Ranges
| Range | Purpose |
|---|---|
| 10-99 | Core Magento plugins |
| 100-199 | Third-party extensions |
| 200-299 | Custom application plugins |
| 300+ | Late-execution plugins |
Around Plugin Execution Order
Around Plugins Stack
Around plugins nest like layers:
<type name="Magento\Catalog\Model\Product">
<plugin name="outer" type="Vendor\Outer\Plugin" sortOrder="10"/>
<plugin name="inner" type="Vendor\Inner\Plugin" sortOrder="20"/>
</type>
Execution:
outer::aroundGetName {
inner::aroundGetName {
Product::getName()
}
}
Around + Before/After Mixing
<type name="Magento\Catalog\Model\Product">
<plugin name="around_1" type="Vendor\Around1" sortOrder="10"/>
<plugin name="before_1" type="Vendor\Before1" sortOrder="15"/>
<plugin name="around_2" type="Vendor\Around2" sortOrder="20"/>
<plugin name="before_2" type="Vendor\Before2" sortOrder="25"/>
</type>
Execution:
around_1::around {
around_2::around {
before_2::before
before_1::before
Product::getName()
before_1::after
before_2::after
}
around_2::after
}
around_1::after
Around plugins at lower sortOrder wrap the entire inner chain.
Plugin Conflicts and Resolution
Common Conflict Scenario
Two modules plugin the same method with conflicting logic:
// Vendor A: converts name to uppercase
public function afterGetName($subject, $result) {
return strtoupper($result);
}
// Vendor B: adds prefix
public function afterGetName($subject, $result) {
return 'Product: ' . $result;
}
Result depends on sortOrder. If A=10, B=20:
- Original: "Widget"
- A after (sortOrder 10): "WIDGET" (after runs in reverse)
- B after (sortOrder 20): "Product: WIDGET"
Disabling Conflicting Plugins
<type name="Magento\Catalog\Model\Product">
<plugin name="conflicting_plugin" disabled="true"/>
</type>
Using di.xml Override Pattern
<!-- Module A di.xml -->
<type name="Magento\Catalog\Model\Product">
<plugin name="module_a_plugin" type="Vendor\A\Plugin" sortOrder="10"/>
</type>
<!-- Module B di.xml - can override via plugin name -->
<type name="Magento\Catalog\Model\Product">
<plugin name="module_a_plugin" disabled="true"/>
<plugin name="module_b_better" type="Vendor\B\Plugin" sortOrder="10"/>
</type>
Conflict Resolution Strategies
- Use specific sortOrder values to control order
- Disable conflicting plugins from other modules
- Use around plugins to wrap and modify behavior
- Check for existing plugins before implementing
- Document plugin sort order in README
Debugging Plugin Order
Inspecting Plugin Configuration
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$config = $objectManager->get(\Magento\Framework\Interception\Config\ConfigInterface::class);
$plugins = $config->getPlugins('Magento\Catalog\Model\Product');
print_r($plugins);
Plugin Profiling
public function aroundGetName($subject, callable $proceed)
{
$start = microtime(true);
$result = $proceed();
$elapsed = microtime(true) - $start;
$this->logger->info(static::class . '::getName: ' . round($elapsed, 4) . 's');
return $result;
}
CLI Debug Commands
# List plugins for a class
php bin/magento dev:di:info Magento\\Catalog\\Model\\Product
# Check generated interceptor
cat var/classes/Magento/Catalog/Model/ProductInterceptor.php
# Verify plugin configuration
php bin/magento setup:di:compile -vvv
Common Issues
| Issue | Cause | Fix |
|---|---|---|
| Plugin not executing | Missing di:compile | Run setup:di:compile |
| Wrong execution order | Missing sortOrder | Add explicit sortOrder |
| Plugin applies to wrong class | Interface vs class mismatch | Check type in di.xml |
| Around plugin breaks original | $proceed not called | Always call $proceed unless intentional |
Practice Problems
Three plugins on the same method produce unexpected results. Determine the correct sortOrder to achieve the desired output.
Quiz
1. In what order do before plugins execute?
2. In what order do after plugins execute?
3. How do you disable a third-party plugin?
4. What happens if sortOrder is not specified?
Flashcards
Question
What order do before plugins execute?
Click to reveal answer
Answer
Ascending sortOrder — lowest value first
Question
What order do after plugins execute?
Click to reveal answer
Answer
Descending sortOrder — highest value first (LIFO)
Question
How do around plugins nest?
Click to reveal answer
Answer
Lower sortOrder wraps outer; higher sortOrder wraps inner
Question
How to disable a plugin from another module?
Click to reveal answer
Answer
Set disabled="true" on the plugin name in your di.xml
Question
What command inspects plugins for a class?
Click to reveal answer
Answer
php bin/magento dev:di:info ClassName
Revision Notes
Key Takeaways
- 1. Before plugins: ascending sortOrder; after plugins: descending sortOrder (LIFO)
- 2. Around plugins at lower sortOrder wrap the entire inner chain
- 3. Always specify sortOrder for predictable plugin execution order
- 4. Disable conflicting plugins with disabled="true" in di.xml
- 5. Use dev:di:info to inspect plugin configuration for a class
Interview Tips
- • Explain before/after sortOrder behavior with a concrete example
- • Describe how around plugins nest and wrap inner chains
- • Discuss strategies for resolving plugin conflicts between modules
- • Know how to debug plugin execution order
Cheat Sheet
Plugin Ordering Cheat Sheet
Before plugins: sortOrder 10, 20, 30
-> executes: 10 -> 20 -> 30
After plugins: sortOrder 10, 20, 30
-> executes: 30 -> 20 -> 10
Around plugins: sortOrder 10, 20
-> 10 wraps { 20 wraps { original } }
Disable plugin:
<plugin name="name" disabled="true"/>
Debug:php bin/magento dev:di:info ClassName