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:
- Object creation - Instantiate classes with their dependencies
- Dependency resolution - Find and inject all constructor parameters
- Preference mapping - Resolve interfaces to concrete implementations
- Shared instances - Manage singleton objects per request
- Virtual types - Create configured variants of classes
- 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:
- Hidden dependencies - Dependencies aren't visible in the constructor
- Hard to test - Can't inject mocks without ObjectManager
- Violation of SRP - Class creates its own dependencies
- Tight coupling - Direct references to specific implementations
- Performance - ObjectManager resolution is slower than constructor injection
- 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?
2. Why is direct ObjectManager usage in business logic discouraged?
3. Where is compiled ObjectManager configuration stored?
Flashcards
Question
What method gets a shared (singleton) instance?
Click to reveal answer
Answer
$objectManager->get($type)
Question
What method creates a new instance?
Click to reveal answer
Answer
$objectManager->create($type, $arguments)
Question
Where does ObjectManager read compiled config from?
Click to reveal answer
Answer
generated/metadata/{area}.php
Question
What is a legitimate place to use ObjectManager directly?
Click to reveal answer
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.