The Proxy Pattern Explained
Definition
The Proxy pattern provides a surrogate or placeholder for another object to control access to it. The proxy has the same interface as the real object.
Types of Proxies
1. Lazy Proxy (Virtual Proxy)
Delays creation of expensive objects until they're actually used:
namespace Vendor\Catalog\Proxy;
class HeavyProductExporterProxy
{
private ?\Vendor\Catalog\Model\ProductExporter $real = null;
public function __construct(
private array $config // Configuration, not the exporter
) {}
public function export(array $products): string
{
// Create real exporter only when needed
if ($this->real === null) {
$this->real = new \Vendor\Catalog\Model\ProductExporter(
$this->config['format'],
$this->config['output_dir']
);
}
return $this->real->export($products);
}
}
2. Protection Proxy
Controls access based on permissions:
class AccessControlProxy
{
public function __construct(
private \Magento\Framework\Model\AbstractModel $real,
private string $requiredRole
) {}
public function save(): void
{
if (!$this->hasPermission()) {
throw new \Magento\Framework\Exception\AuthorizationException(
__('Insufficient permissions for this operation')
);
}
$this->real->save();
}
public function __call(string $method, array $args)
{
return $this->real->$method(...$args);
}
private function hasPermission(): bool
{
return $this->auth->getUser()->getRole() === $this->requiredRole;
}
}
3. Logging Proxy
Adds logging without modifying the real class:
class LoggingProxy
{
public function __construct(
private object $real,
private LoggerInterface $logger
) {}
public function __call(string $method, array $args)
{
$this->logger->info('Calling {method}', [
'method' => get_class($this->real) . '::' . $method,
'args' => $args,
]);
$result = $this->real->$method(...$args);
$this->logger->info('Result from {method}', [
'method' => $method,
'result' => $result,
]);
return $result;
}
}
Proxy vs Decorator: The Difference
They Look Similar But Differ
Both implement the same interface and wrap another object. The key difference:
| Aspect | Proxy | Decorator |
|---|---|---|
| Purpose | Controls access to object | Adds behavior to object |
| Knowledge | Knows about the real object | Doesn't know about the real object |
| Lifecycle | May create/manage real object | Always receives real object |
| When created | Often at the same time as real | Always after real object |
| Examples | Lazy loading, access control, caching | Logging, pricing modification, validation |
The Telltale Sign
// Proxy: controls whether/how the real object is used
class CacheProxy
{
private ?RealService $real = null;
public function getData(string $key): mixed
{
// Check cache first — may never call real object
$cached = $this->cache->get($key);
if ($cached !== null) {
return $cached; // Real object never created
}
$this->real = $this->createReal();
$data = $this->real->getData($key);
$this->cache->set($key, $data);
return $data;
}
}
// Decorator: always delegates to real object
class LoggingDecorator
{
public function __construct(
private RealService $real // Always provided
) {}
public function getData(string $key): mixed
{
$this->log('Getting data');
return $this->real->getData($key); // Always calls real
}
}
The proxy decides whether to use the real object. The decorator always uses it and adds behavior.
Magento Auto-Generated Proxies
How Magento Generates Proxies
Magento automatically generates proxy classes for lazy loading. When you type-hint with Proxy suffix, Magento creates a lazy-loading proxy.
// Instead of loading the product repository immediately:
namespace Vendor\Catalog\Model;
class ProductLister
{
public function __construct(
private \Magento\Catalog\Model\ProductRepository\Proxy $productRepo
) {}
// ProductRepository is NOT loaded yet — only when first used
}
Generated Proxy (var/di/)
// Magento generates this in var/di/
class ProductRepositoryProxy
{
private ?\Magento\Catalog\Model\ProductRepository $real = null;
private \Magento\Framework\ObjectManager\ObjectManager $objectManager;
public function __construct(
\Magento\Framework\ObjectManager\ObjectManager $objectManager
) {
$this->objectManager = $objectManager;
}
public function get($sku, $editMode = false, $storeId = null, $forceReload = false)
{
if ($this->real === null) {
$this->real = $this->objectManager->create(
\Magento\Catalog\Model\ProductRepository::class
);
}
return $this->real->get($sku, $editMode, $storeId, $forceReload);
}
// ... other methods delegate similarly
}
Why Proxies Matter for Performance
// WITHOUT proxy: ProductRepository loads immediately
public function __construct(
private ProductRepository $repo // ObjectManager creates this on construction
// If ProductRepository depends on 50 other classes,
// they ALL get created immediately
) {}
// WITH proxy: Only loads when first method is called
public function __construct(
private ProductRepository\Proxy $repo // Lightweight proxy created
// Nothing heavy is loaded yet
) {}
public function listProducts(): void
{
// Only NOW does the real ProductRepository get created
$products = $this->repo->getList($criteria);
}
This is especially valuable for CLI commands, cron jobs, and API endpoints that might not need all dependencies.
Quiz
1. What distinguishes a Proxy from a Decorator?
2. Magento's auto-generated proxies provide:
3. When using a lazy proxy, when is the real object created?
Flashcards
Question
What does the Proxy pattern do?
Click to reveal answer
Answer
Controls access to another object via a placeholder with the same interface
Question
Proxy vs Decorator?
Click to reveal answer
Answer
Proxy controls access/creation; Decorator adds behavior (always delegates)
Question
Magento proxy suffix does what?
Click to reveal answer
Answer
Auto-generates a lazy-loading proxy that delays object creation
Question
Types of proxies?
Click to reveal answer
Answer
Lazy (virtual), Protection (access control), Logging, Caching, Remote
Revision Notes
Key Takeaways
- 1. Proxy controls access to an object; Decorator adds behavior
- 2. Lazy proxies delay creation until first use — saves memory and startup time
- 3. Magento auto-generates proxies with the Proxy suffix in type hints
- 4. Proxies are valuable for heavy dependencies that may not always be needed
- 5. Protection proxies add access control transparently
Interview Tips
- • Explain when Magento proxies improve performance (heavy dependencies, CLI tools)
- • Distinguish Proxy (controls access) from Decorator (adds behavior)
- • Give an example: ProductRepository\Proxy delays loading until first get() call
Cheat Sheet
Proxy Pattern:
Controls access to real object (may delay, restrict, or cache)
Same interface as real object
Types:
Lazy → Delays creation until first use
Protection → Access control
Logging → Logs calls
Caching → Returns cached results
Magento: Type\Proxy suffix → auto-generated lazy proxy
Proxy vs Decorator:
Proxy → Controls WHETHER to call real object
Decorator → Always calls real object + adds behavior