Skip to content
intermediate Phase 27 · DI Fundamentals

ObjectManager Deep Dive

Understanding Magento 2 ObjectManager internals: how it creates objects, shared instances, auto-wiring, configuration merging, and when to use it

45m
0 problems
Topic Progress 0%

ObjectManager Architecture

What ObjectManager Does

ObjectManager is Magento's DI container. It:

  1. Creates objects — instantiates classes with resolved dependencies
  2. Manages shared instances — returns same object for singletons
  3. Resolves types — maps interfaces to implementations via preferences
  4. 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()?

Question 1 options

2. When is direct ObjectManager usage acceptable?

Question 2 options

3. What happens when ObjectManager encounters an interface type-hint?

Question 3 options

Flashcards

Question

What does ObjectManager::get() do?

Answer

Returns a shared (singleton) instance of a class

Question

What does ObjectManager::create() do?

Answer

Creates a new instance every time

Question

How do you make a class non-shared?

Answer

Set shared="false" in di.xml <type>

Question

What is auto-wiring?

Answer

Automatically resolving concrete class dependencies without di.xml configuration

Question

When should you use ObjectManager directly?

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