ObjectManager Architecture
What ObjectManager Does
ObjectManager is Magento's DI container. It:
- Creates objects — instantiates classes with resolved dependencies
- Manages shared instances — returns same object for singletons
- Resolves types — maps interfaces to implementations via preferences
- Applies configuration — merges di.xml arguments into objects
Direct Usage (Rarely Recommended)
use Magento\Framework\ObjectManager;
// Get ObjectManager instance
$objectManager = ObjectManager::getInstance();
// Create object
$product = $objectManager->create(\Magento\Catalog\Model\Product::class);
// Get shared instance
$session = $objectManager->get(\Magento\Customer\Model\Session::class);
When Direct Usage is Acceptable
- In setup/install scripts (before DI is available)
- In legacy code refactoring (temporary)
- In tests (for bootstrapping)
- In entry points (index.php, cron.php)
When NOT to Use
- In regular classes (use constructor injection)
- In models, blocks, controllers
- Anywhere DI is available
// BAD: Direct ObjectManager usage
class BadService
{
public function doSomething()
{
$product = ObjectManager::getInstance()
->create(Product::class);
}
}
// GOOD: Constructor injection
class GoodService
{
public function __construct(
private ProductFactory $productFactory
) {}
public function doSomething()
{
$product = $this->productFactory->create();
}
}
Object Creation Process
create() Method
Creates a NEW instance every time:
$product1 = $objectManager->create(Product::class);
$product2 = $objectManager->create(Product::class);
// $product1 !== $product2 (different instances)
get() Method
Returns SHARED instance (singleton):
$session1 = $objectManager->get(CustomerSession::class);
$session2 = $objectManager->get(CustomerSession::class);
// $session1 === $session2 (same instance)
Creation Steps
1. Read class constructor
2. Check di.xml type configuration
3. For each parameter:
a. Check di.xml arguments
b. Check di.xml preferences (for interfaces)
c. Try auto-wiring (for concrete classes)
d. Use default value (for optional)
4. Apply afterConfigured callback
5. Create instance with resolved parameters
6. If shared, store in registry
7. Return instance
With Arguments
$product = $objectManager->create(
Product::class,
[
'data' => [
'name' => 'Test Product',
'sku' => 'TEST-001',
'price' => 29.99
]
]
);
Factory Creation
ObjectManager also creates factory classes:
$factory = $objectManager->get(ProductFactory::class);
$product = $factory->create(['data' => ['name' => 'Test']]);
Factories are auto-generated in generated/ directory.
Shared Instances and Singletons
Shared Instance Registry
ObjectManager maintains an internal registry of shared instances:
// First call creates and stores
$session = $objectManager->get(CustomerSession::class);
// Second call returns stored instance
$sameSession = $objectManager->get(CustomerSession::class);
// $session === $sameSession
Controlling Sharing
<!-- Default: shared (singleton) -->
<type name="Amazon\Prep\Service\WarrantyService"/>
<!-- Explicitly non-shared -->
<type name="Amazon\Prep\Model\Warranty" shared="false"/>
<!-- Non-shared collection -->
<type name="Amazon\Prep\Model\ResourceModel\Warranty\Collection" shared="false"/>
Factory Pattern
Factories always create new instances:
// Factory itself is shared
$factory1 = $objectManager->get(WarrantyFactory::class);
$factory2 = $objectManager->get(WarrantyFactory::class);
// $factory1 === $factory2 (same factory)
// But objects created by factory are new
$warranty1 = $factory1->create();
$warranty2 = $factory2->create();
// $warranty1 !== $warranty2 (different objects)
Shared Instance Reset
// Remove specific shared instance
$objectManager->removeSharedInstance(WarrantyService::class);
// Reset all shared instances
$objectManager->clearSharedInstances();
Thread Safety
// In multi-threaded environments (Swoole, etc.):
// Each thread gets its own ObjectManager instance
// Shared instances are per-thread, not global
Auto-wiring and Configuration Merging
Auto-wiring
ObjectManager attempts to automatically resolve concrete class dependencies:
// If ProductRepository has no interface preference,
// ObjectManager will create it directly
class WarrantyService
{
public function __construct(
private ProductRepository $productRepo // Auto-wired
) {}
}
Auto-wiring works for:
- Concrete classes (no preference needed)
- Classes with only concrete dependencies
Fails for:
- Interfaces (need preference)
- Scalar types (need di.xml arguments)
- Circular dependencies
Configuration Merging
Multiple di.xml files are merged:
<!-- etc/di.xml (global) -->
<type name="Service">
<arguments>
<argument name="timeout" xsi:type="number">30</argument>
</arguments>
</type>
<!-- etc/frontend/di.xml (area-specific) -->
<type name="Service">
<arguments>
<argument name="timeout" xsi:type="number">60</argument> <!-- Override -->
<argument name="apiKey" xsi:type="string">frontend_key</argument> <!-- Add -->
</arguments>
</type>
Result: timeout=60, apiKey='frontend_key'
Merge Priority
1. Area-specific (etc/frontend/di.xml)
2. Module global (etc/di.xml)
3. Magento core di.xml
Type Arguments
<!-- Add arguments to specific class -->
<type name="Amazon\Prep\Service\WarrantyService">
<arguments>
<argument name="apiKey" xsi:type="string">sk_live_abc</argument>
<argument name="options" xsi:type="array">
<item name="timeout" xsi:type="number">30</item>
<item name="retries" xsi:type="number">3</item>
</argument>
</arguments>
</type>
Shared Flag in Configuration
<!-- Make normally shared class non-shared -->
<type name="Amazon\Prep\Model\Warranty" shared="false"/>
<!-- Make normally non-shared class shared -->
<type name="Amazon\Prep\Helper\Data" shared="true"/>
Quiz
1. What is the difference between ObjectManager::get() and create()?
2. When is direct ObjectManager usage acceptable?
3. What happens when ObjectManager encounters an interface type-hint?
Flashcards
Question
What does ObjectManager::get() do?
Click to reveal answer
Answer
Returns a shared (singleton) instance of a class
Question
What does ObjectManager::create() do?
Click to reveal answer
Answer
Creates a new instance every time
Question
How do you make a class non-shared?
Click to reveal answer
Answer
Set shared="false" in di.xml <type>
Question
What is auto-wiring?
Click to reveal answer
Answer
Automatically resolving concrete class dependencies without di.xml configuration
Question
When should you use ObjectManager directly?
Click to reveal answer
Answer
Only in setup scripts and entry points where DI is not available
Revision Notes
Key Takeaways
- 1. ObjectManager creates objects and manages shared instances
- 2. get() returns singletons; create() returns new instances
- 3. Auto-wiring resolves concrete classes automatically
- 4. Configuration merges from core → module → area-specific di.xml
- 5. Avoid direct ObjectManager usage in regular code
Interview Tips
- • Explain the get() vs create() difference
- • Know when direct ObjectManager usage is acceptable
- • Understand how configuration merging works
- • Be ready to discuss shared vs non-shared trade-offs
Cheat Sheet
ObjectManager:
get() → shared instance (singleton)
create() → new instance
Auto-wiring:
Concrete classes → auto-resolved
Interfaces → need preference
Scalars → need di.xml arguments
Configuration merge:
core di.xml → module di.xml → area di.xml