Shared vs Non-Shared Fundamentals
Shared Objects (Singletons)
By default, ObjectManager returns the same instance:
$service1 = $objectManager->get(WarrantyService::class);
$service2 = $objectManager->get(WarrantyService::class);
// $service1 === $service2 (same object)
Non-Shared Objects
Creates new instance each time:
$warranty1 = $objectManager->create(Warranty::class);
$warranty2 = $objectManager->create(Warranty::class);
// $warranty1 !== $warranty2 (different objects)
Configuration
<!-- Default: shared -->
<type name="Amazon\Prep\Service\WarrantyService"/>
<!-- Explicitly non-shared -->
<type name="Amazon\Prep\Model\Warranty" shared="false"/>
<!-- Override to shared -->
<type name="Amazon\Prep\Helper\Data" shared="true"/>
Visual Comparison
Shared (Singleton):
ObjectManager -> [Same Instance] -> All consumers
Consumer A --+
+--> Same WarrantyService
Consumer B --+
Non-Shared:
ObjectManager -> [New Instance] -> Each consumer
Consumer A -> New Warranty
Consumer B -> New Warranty (different)
When to Use Each Pattern
Use Shared Objects When
- Stateless services
class WarrantyService
{
public function process(Warranty $warranty): void
{
// Only uses passed objects, no internal state
}
}
- Configuration holders
class ConfigService
{
public function __construct(
private ScopeConfigInterface $config
) {}
public function getValue(string $path): mixed
{
return $this->config->getValue($path);
}
}
- Factories and helpers
// Factory itself is shared, objects it creates are new
$factory = $objectManager->get(WarrantyFactory::class);
- Cache and session managers
// Must be shared to maintain state
$session = $objectManager->get(CustomerSession::class);
Use Non-Shared Objects When
- Models with data
$warranty1 = $warrantyFactory->create();
$warranty1->setName('Warranty 1');
$warranty2 = $warrantyFactory->create();
$warranty2->setName('Warranty 2');
// Different objects with different data
- Collections
$collection1 = $collectionFactory->create();
$collection1->addFieldToFilter('status', 'active');
$collection2 = $collectionFactory->create();
$collection2->addFieldToFilter('status', 'inactive');
- Request/response objects
$request = $this->requestFactory->create();
- Data transfer objects
$dto = $this->dtoFactory->create(['data' => $specificData]);
State Management
Stateful Shared Objects
Shared objects can accumulate state within a process:
// BAD: Shared object with mutable state
class BadService
{
private $cache = []; // Accumulates over time
public function process($key, $value)
{
$this->cache[$key] = $value; // Never cleared!
}
}
Stateless Shared Objects
// GOOD: Shared object without mutable state
class GoodService
{
public function process($key, $value)
{
return $this->doWork($key, $value);
}
}
Resetting Shared State
class CacheService
{
private $cache = [];
public function reset(): void
{
$this->cache = [];
}
}
// Call periodically or in tests
$cacheService->reset();
Request Lifecycle
In web requests, shared instances are typically reset between requests:
Request 1 -> Shared instance created -> Response -> Instance destroyed
Request 2 -> New shared instance created -> Response -> Instance destroyed
Exception: Long-running processes (Swoole, queues) keep instances alive.
Thread Safety and Best Practices
Thread Safety
In multi-threaded environments (Swoole, etc.):
// Each thread gets its own ObjectManager instance
// Shared instances are per-thread, not global
// Thread 1:
$service1 = ObjectManager::getInstance()->get(Service::class);
// Thread 2:
$service2 = ObjectManager::getInstance()->get(Service::class);
// $service1 !== $service2 (different threads)
Best Practices
Default Patterns
<!-- Services: shared (default) -->
<type name="Amazon\Prep\Service\WarrantyService"/>
<!-- Models: non-shared -->
<type name="Amazon\Prep\Model\Warranty" shared="false"/>
<!-- Collections: non-shared -->
<type name="Amazon\Prep\Model\ResourceModel\Warranty\Collection" shared="false"/>
<!-- Factories: shared -->
<type name="Amazon\Prep\Model\WarrantyFactory"/>
Quick Reference
| Pattern | Shared? | Why |
|---|---|---|
| Services | Yes | Stateless, no side effects |
| Helpers | Yes | Stateless utilities |
| Factories | Yes | Factory itself is singleton |
| Registries | Yes | Must persist state |
| Models | No | Each holds different data |
| Collections | No | Each is independent query |
| DTOs | No | Each holds different data |
| Request | No | Each request is unique |
Debugging Shared Instances
// Check if same instance
$ref1 = spl_object_hash($service1);
$ref2 = spl_object_hash($service2);
echo $ref1 === $ref2 ? 'Same' : 'Different';
// List shared instances in ObjectManager
// (Debug tool required)
Common Mistakes
- Mutable state in shared objects
- Using shared for models (data bleeds between requests)
- Using non-shared for services (wasted resources)
- Forgetting factory is shared (creates objects, doesn't become object)
Quiz
1. What is the default sharing behavior in Magento DI?
2. When should models be non-shared?
3. What is a risk of mutable state in shared objects?
Flashcards
Question
What is the default sharing in Magento DI?
Click to reveal answer
Answer
Shared (singleton) - same instance returned
Question
When use non-shared objects?
Click to reveal answer
Answer
Models, collections, DTOs - anything holding per-instance data
Question
When use shared objects?
Click to reveal answer
Answer
Services, helpers, factories, registries - stateless or persistent state
Question
How do you make an object non-shared?
Click to reveal answer
Answer
Set shared='false' on the type in di.xml
Question
What is the risk of mutable state in shared objects?
Click to reveal answer
Answer
State persists between requests causing bugs
Revision Notes
Key Takeaways
- 1. Default is shared (singleton); use shared='false' for new instances
- 2. Services and factories should be shared; models and collections should not
- 3. Avoid mutable state in shared objects to prevent data leaks
- 4. Factories are shared but the objects they create are new
- 5. Consider thread safety in multi-process environments
Interview Tips
- • Explain shared vs non-shared trade-offs
- • Know the default patterns for services, models, factories
- • Be ready to identify mutable state issues
- • Discuss request lifecycle and instance management
Cheat Sheet
Shared (default):
Services, helpers, factories, registries
Same instance returned
Non-shared:
Models, collections, DTOs, requests
New instance each time
Config: <type name="..." shared="false"/>
Check: spl_object_hash($obj1) === spl_object_hash($obj2)