Proxy Fundamentals
What is a Proxy?
A proxy is a placeholder object that delays creation of the actual object until it's first used. This is lazy loading.
// Without proxy - created immediately
$heavyService = new HeavyService(); // Expensive!
// With proxy - created on first use
$proxy = new HeavyServiceProxy(); // Cheap!
$proxy->doWork(); // Creates HeavyService here
Why Use Proxies?
Some dependencies are expensive to create:
- Database connections
- External API clients
- Large object graphs
- Objects with many dependencies
Proxies defer this cost until actually needed.
How Proxies Work
1. Proxy is created (lightweight)
2. Proxy stores the class name and arguments
3. On first method call, proxy creates the real object
4. Subsequent calls are forwarded to the real object
Proxy vs Factory
| Aspect | Factory | Proxy |
|---|---|---|
| Creation | Explicit create() call | Automatic on first use |
| Use case | Multiple instances | Single instance, lazy |
| Control | You control when | Framework controls |
| DI | Constructor injection | Transparent wrapper |
Configuring Proxies
di.xml Configuration
<type name="Amazon\Prep\Service\WarrantyService">
<arguments>
<argument name="heavyDependency" xsi:type="object">
Amazon\Prep\Service\HeavyService\Proxy
</argument>
</arguments>
</type>
Generated Proxy Class
After bin/magento setup:di:compile, a proxy class is generated:
<?php
namespace Amazon\Prep\Service\HeavyService;
class Proxy
{
private $instance;
private $objectManager;
private $instanceName = 'Amazon\Prep\Service\HeavyService';
private $shared = true;
public function __construct(
\Magento\Framework\ObjectManager $objectManager,
array $arguments = []
) {
$this->objectManager = $objectManager;
}
public function __call($method, $arguments)
{
if ($this->instance === null) {
$this->instance = $this->objectManager->create(
$this->instanceName,
['arguments' => $arguments]
);
}
return $this->instance->$method(...$arguments);
}
}
Proxy with Arguments
<type name="Amazon\Prep\Service\WarrantyService">
<arguments>
<argument name="apiClient" xsi:type="object">
Amazon\Prep\Service\ApiClient\Proxy
</argument>
<argument name="timeout" xsi:type="number">30</argument>
</arguments>
</type>
Multiple Proxies
<type name="Amazon\Prep\Service\Orchestrator">
<arguments>
<argument name="serviceA" xsi:type="object">ServiceA\Proxy</argument>
<argument name="serviceB" xsi:type="object">ServiceB\Proxy</argument>
<argument name="serviceC" xsi:type="object">ServiceC\Proxy</argument>
</arguments>
</type>
Proxy Performance Benefits
Before Proxy (Eager Loading)
class WarrantyService
{
public function __construct(
private HeavyApiClient $apiClient, // Created always
private DatabaseConnection $dbConnection, // Created always
private CacheManager $cacheManager // Created always
) {}
public function simpleOperation(): void
{
// Might not even use apiClient or dbConnection
$this->cacheManager->clean();
}
}
All three dependencies are created even if only cacheManager is needed.
After Proxy (Lazy Loading)
<type name="Amazon\Prep\Service\WarrantyService">
<arguments>
<argument name="apiClient" xsi:type="object">HeavyApiClient\Proxy</argument>
<argument name="dbConnection" xsi:type="object">DatabaseConnection\Proxy</argument>
<argument name="cacheManager" xsi:type="object">CacheManager</argument> <!-- Not proxied -->
</arguments>
</type>
Now apiClient and dbConnection are only created when their methods are actually called.
Performance Impact
Without proxies:
Request → Create Service → Create API Client → Create DB → ... → Response
Time: 500ms
With proxies:
Request → Create Service (lightweight) → Response
Time: 100ms (if API client not used)
When to Proxy
✅ Proxy when:
- Dependency is expensive to create
- Dependency might not be used in all code paths
- Multiple heavy dependencies exist
- Performance is critical
⌠Don't proxy when:
- Dependency is lightweight
- Dependency is always used
- You need direct object access
- Adds unnecessary complexity
Memory Considerations
// Proxy uses minimal memory until instantiated
$proxy = new ServiceProxy(); // ~1KB
// Real object uses full memory
$real = new HeavyService(); // ~100KB
// After first call, proxy holds reference to real object
$proxy->doWork(); // Now uses ~101KB total
Proxy Best Practices
Naming Convention
<!-- Append \Proxy to class name -->
<argument xsi:type="object">ClassName\Proxy</argument>
<!-- Correct -->
<argument xsi:type="object">Amazon\Prep\Service\ApiClient\Proxy</argument>
<!-- Wrong -->
<argument xsi:type="object">Amazon\Prep\Service\Proxy\ApiClient</argument>
Proxy Chain
<!-- Avoid chaining proxies -->
<argument xsi:type="object">ServiceA\Proxy\Proxy</argument> <!-- Don't do this -->
<!-- Better: proxy only the expensive dependency -->
<argument xsi:type="object">ServiceA\Proxy</argument>
Testing Proxies
// In tests, use the real object (not proxy)
class WarrantyServiceTest extends TestCase
{
public function testWithRealDependencies(): void
{
$apiClient = $this->createMock(ApiClient::class);
$service = new WarrantyService($apiClient);
// Test with real mock, not proxy
}
}
Debug Proxies
# Generate proxy classes
bin/magento setup:di:compile
# Check generated proxies
cat generated/Amazon/Prep/Service/HeavyService/Proxy.php
# Verify proxy usage
grep -r "Proxy" var/di/
Common Proxy Patterns
<!-- API clients -->
<argument xsi:type="object">Api\Client\Proxy</argument>
<!-- Database-heavy services -->
<argument xsi:type="object">Service\HeavyService\Proxy</argument>
<!-- External service integrations -->
<argument xsi:type="object">Integration\ExternalService\Proxy</argument>
<!-- File system operations -->
<argument xsi:type="object">File\Handler\Proxy</argument>
Quiz
1. What does a proxy do in Magento DI?
2. How do you configure a proxy in di.xml?
3. When should you use a proxy?
Flashcards
Question
What is a proxy?
Click to reveal answer
Answer
A placeholder that delays object creation until first use (lazy loading)
Question
How do you configure a proxy?
Click to reveal answer
Answer
Append \Proxy to class name in di.xml arguments
Question
When are proxies useful?
Click to reveal answer
Answer
For expensive dependencies that might not be used in all code paths
Question
Proxy vs Factory: which is for lazy loading?
Click to reveal answer
Answer
Proxies for lazy loading; factories for explicit creation
Question
Where are proxy classes generated?
Click to reveal answer
Answer
In the generated/ directory after setup:di:compile
Revision Notes
Key Takeaways
- 1. Proxies delay object creation until first method call
- 2. Configure by appending \Proxy to class name in di.xml
- 3. Proxies improve performance by avoiding unnecessary object creation
- 4. Generated proxy classes are in the generated/ directory
- 5. Use proxies for expensive dependencies that might not always be used
Interview Tips
- • Explain how proxies enable lazy loading
- • Know when to use proxies vs direct injection
- • Be ready to configure proxies in di.xml
- • Discuss performance implications of proxy usage
Cheat Sheet
Proxy:
Delays creation until first use
Configure: ClassName\Proxy
<argument xsi:type="object">Service\Proxy</argument>
Generated: generated/Vendor/Module/Service/Proxy.php
Use for: expensive deps, optional deps, performance
Avoid for: lightweight, always-used, simple objects