Skip to content
intermediate Phase 10 · More Design Patterns

Proxy Pattern

Proxy pattern for lazy loading, access control, and logging, with Magento proxy classes for performance

45m
0 problems
Topic Progress 0%

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?

Question 1 options

2. Magento's auto-generated proxies provide:

Question 2 options

3. When using a lazy proxy, when is the real object created?

Question 3 options

Flashcards

Question

What does the Proxy pattern do?

Answer

Controls access to another object via a placeholder with the same interface

Question

Proxy vs Decorator?

Answer

Proxy controls access/creation; Decorator adds behavior (always delegates)

Question

Magento proxy suffix does what?

Answer

Auto-generates a lazy-loading proxy that delays object creation

Question

Types of proxies?

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