Skip to content
intermediate Phase 18 · Events, Plugins & DI

Magento 2 ObjectManager

What ObjectManager does, why direct usage is discouraged, and how it works under the hood.

45m
0 problems
Topic Progress 0%

What the ObjectManager Does

The ObjectManager is Magento's dependency injection container. It's responsible for creating objects, resolving dependencies, and managing object lifecycle.

Core responsibilities:

  1. Object creation - Instantiate classes with their dependencies
  2. Dependency resolution - Find and inject all constructor parameters
  3. Preference mapping - Resolve interfaces to concrete implementations
  4. Shared instances - Manage singleton objects per request
  5. Virtual types - Create configured variants of classes
  6. Proxy generation - Create lazy-loading wrappers

ObjectManager interface:

<?php
namespace Magento\Framework\ObjectManager;

interface ObjectManagerInterface
{
    // Get shared instance (singleton)
    public function get($type, array $arguments = []);
    
    // Create new instance
    public function create($type, array $arguments = []);
    
    // Get existing shared or create new
    public function getSharedOrCreate($type, array $arguments = []);
}

Internal flow:

// Simplified ObjectManager::create() flow
public function create($type, array $arguments = [])
{
    // 1. Resolve type alias or preference
    $type = $this->config->getMappedType($type);
    
    // 2. Get constructor parameters
    $constructor = $this->reflection->getConstructor($type);
    
    // 3. Resolve each parameter
    $resolvedArgs = [];
    foreach ($constructor->getParameters() as $param) {
        $resolvedArgs[] = $this->resolve($param, $arguments);
    }
    
    // 4. Create instance with resolved dependencies
    return new $type(...$resolvedArgs);
}

Why Direct Usage Is Discouraged

Using ObjectManager directly in business code is considered an anti-pattern in Magento 2 for several reasons.

Problems with direct usage:

// BAD - Direct ObjectManager usage
public function doSomething()
{
    $product = $this->objectManager->create(
        \Magento\Catalog\Model\Product::class
    );
    
    $repository = $this->objectManager->get(
        \Magento\Catalog\Model\ProductRepository::class
    );
    
    // Hidden dependencies
    // Unclear what this class actually needs
    // Hard to test (can't mock dependencies)
    // Violates SRP
}

Issues:

  1. Hidden dependencies - Dependencies aren't visible in the constructor
  2. Hard to test - Can't inject mocks without ObjectManager
  3. Violation of SRP - Class creates its own dependencies
  4. Tight coupling - Direct references to specific implementations
  5. Performance - ObjectManager resolution is slower than constructor injection
  6. Code smell - Indicates poor architectural design

Correct approach:

// GOOD - Constructor injection
public function __construct(
    \Magento\Catalog\Api\ProductRepositoryInterface $productRepository,
    \Magento\Catalog\Api\ProductFactoryInterface $productFactory
) {
    $this->productRepository = $productRepository;
    $this->productFactory = $productFactory;
}

public function doSomething()
{
    $product = $this->productFactory->create();
    // Dependencies are explicit and injectable
}

Benefits of constructor injection:

  • All dependencies visible at class instantiation
  • Easy to mock for unit testing
  • IDE autocompletion and type checking
  • Enforces single responsibility
  • Clear class contract

Legitimate ObjectManager Use Cases

There are specific scenarios where ObjectManager usage is acceptable or necessary.

1. Entry point (index.php, bootstrap):

// pub/index.php - Legitimate use
$bootstrap = \Magento\Framework\App\Bootstrap::create(
    BP,
    $_SERVER
);
$bootstrap->run(
    $bootstrap->getObjectManager()->get(
        \Magento\Framework\App\Http::class
    )
);

2. Plugin third-party code:

// When third-party code doesn't use DI
public function __construct(
    \Magento\Framework\ObjectManager\ObjectManager $objectManager
) {
    // Use for classes you can't modify via DI
    $this->thirdPartyService = $objectManager->create(
        \ThirdParty\Service\class
    );
}

3. Factory and proxy creation:

// Factories internally use ObjectManager
public function create(array $data = [])
{
    return $this->objectManager->create(
        $this->instanceName,
        $data
    );
}

4. CLI commands and setup scripts:

// bin/magento commands - bootstrap context
$objectManager = \Magento\Framework\App\Bootstrap::create(
    BP,
    $_SERVER
)->getObjectManager();

$productRepo = $objectManager->get(
    \Magento\Catalog\Api\ProductRepositoryInterface::class
);

Rule of thumb: ObjectManager is acceptable at application entry points and in generated code. Never use it in business logic classes.

ObjectManager Internals

Understanding how ObjectManager works internally helps debug DI issues and optimize performance.

Configuration loading:

// ObjectManager reads compiled DI config
$compiledConfig = include BP . '/generated/metadata/global.php';

// Config contains:
// - preferences: interface → class mappings
// - types: constructor arguments and plugins
// - virtualTypes: named configurations
// - shared: singleton configuration

Dependency resolution:

// Simplified resolution process
public function resolve($type, $arguments)
{
    // 1. Check if type is a preference
    if (isset($this->config['preferences'][$type])) {
        $type = $this->config['preferences'][$type];
    }
    
    // 2. Check if type is a virtual type
    if (isset($this->config['virtualTypes'][$type])) {
        $type = $this->config['virtualTypes'][$type]['type'];
        $arguments = array_merge(
            $this->config['virtualTypes'][$type]['arguments'],
            $arguments
        );
    }
    
    // 3. Get constructor and resolve parameters
    $class = new \ReflectionClass($type);
    $constructor = $class->getConstructor();
    
    if ($constructor === null) {
        return new $type();
    }
    
    $params = [];
    foreach ($constructor->getParameters() as $parameter) {
        if (isset($arguments[$parameter->getName()])) {
            $params[] = $arguments[$parameter->getName()];
        } elseif ($parameter->getType()) {
            $params[] = $this->get($parameter->getType()->getName());
        } else {
            $params[] = $parameter->getDefaultValue();
        }
    }
    
    return new $type(...$params);
}

Performance considerations:

  • Compiled config in generated/metadata/ speeds up resolution
  • Shared objects cached in ObjectManager instance
  • Proxies defer creation until first method call
  • Metadata caching reduces reflection overhead

Quiz

1. What is the primary function of ObjectManager?

Question 1 options

2. Why is direct ObjectManager usage in business logic discouraged?

Question 2 options

3. Where is compiled ObjectManager configuration stored?

Question 3 options

Flashcards

Question

What method gets a shared (singleton) instance?

Answer

$objectManager->get($type)

Question

What method creates a new instance?

Answer

$objectManager->create($type, $arguments)

Question

Where does ObjectManager read compiled config from?

Answer

generated/metadata/{area}.php

Question

What is a legitimate place to use ObjectManager directly?

Answer

Application entry points (index.php, bootstrap) and generated code

Revision Notes

Key Takeaways

  • 1. ObjectManager is Magento's DI container for creating and managing objects
  • 2. Direct usage in business logic is an anti-pattern
  • 3. Use constructor injection instead of ObjectManager
  • 4. Legitimate uses: entry points, factories, proxies, generated code
  • 5. Compiled config in generated/metadata/ speeds up resolution
  • 6. get() returns singleton, create() returns new instance

Interview Tips

  • Explain what ObjectManager does and why it exists
  • Discuss why direct ObjectManager usage is discouraged
  • Know the legitimate use cases for ObjectManager
  • Explain how ObjectManager resolves dependencies internally
  • Discuss the performance implications of ObjectManager vs constructor injection

Cheat Sheet

ObjectManager Cheat Sheet

Methods:

  • get($type) → Shared instance (singleton)
  • create($type, $args) → New instance

Config Location: generated/metadata/{area}.php

When to Use Directly:

  • Entry points (index.php, bootstrap)
  • Generated factories and proxies
  • CLI commands and setup scripts

Never Use In:

  • Business logic classes
  • Models, blocks, controllers
  • Service classes

Alternative: Always use constructor injection with type-hinted interfaces.