PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

Proxy pattern – lazy loading, access control, caching, Proxy in Magento 2

by Henryk Tews / Tuesday, 10 August 2021 / Published in Magento 2, Wzorce projektowe

Proxy is a structural pattern that places a surrogate in front of an object to control access to it. Three practical scenarios justify Proxy: lazy loading (deferring expensive creation), access control (checking permissions before executing), and caching. Magento 2 generates Proxy classes automatically for a specific purpose – I show both the GoF pattern and the Magento implementation.

Classic Proxy pattern

<?php

declare(strict_types=1);

interface ImageInterface
{
    public function display(): void;
    public function getWidth(): int;
    public function getHeight(): int;
}

// Real heavyweight object - loads image from disk on creation
class RealImage implements ImageInterface
{
    private \GdImage $resource;

    public function __construct(private string $path)
    {
        // Expensive operation - loads and decodes the file
        $this->resource = imagecreatefromjpeg($path);
        echo "Loaded image: {$path}\n";
    }

    public function display(): void
    {
        imagejpeg($this->resource);
    }

    public function getWidth(): int  { return imagesx($this->resource); }
    public function getHeight(): int { return imagesy($this->resource); }
}

// Lazy loading Proxy - defers creation until actually needed
class LazyImageProxy implements ImageInterface
{
    private ?RealImage $realImage = null;

    public function __construct(private string $path) {}

    private function getRealImage(): RealImage
    {
        if ($this->realImage === null) {
            $this->realImage = new RealImage($this->path); // created only on first use
        }
        return $this->realImage;
    }

    public function display(): void  { $this->getRealImage()->display(); }
    public function getWidth(): int  { return $this->getRealImage()->getWidth(); }
    public function getHeight(): int { return $this->getRealImage()->getHeight(); }
}

// Images created but NOT loaded until display() is called
$images = [
    new LazyImageProxy('/uploads/image1.jpg'),
    new LazyImageProxy('/uploads/image2.jpg'),
    new LazyImageProxy('/uploads/image3.jpg'),
];

// Only this image gets loaded from disk - the others are never touched
$images[0]->display();

Protection Proxy – access control

<?php

declare(strict_types=1);

interface OrderRepositoryInterface
{
    public function getById(int $id): OrderInterface;
    public function delete(OrderInterface $order): bool;
}

// Proxy adds authorisation without modifying the real repository
class AuthorisedOrderRepository implements OrderRepositoryInterface
{
    public function __construct(
        private OrderRepositoryInterface $realRepository,
        private \Magento\Authorization\Model\Acl\AclRetriever $acl,
        private \Magento\Backend\Model\Auth\Session $adminSession
    ) {}

    public function getById(int $id): OrderInterface
    {
        // Everyone can read - no check needed
        return $this->realRepository->getById($id);
    }

    public function delete(OrderInterface $order): bool
    {
        // Deletion requires specific ACL resource
        if (!$this->adminSession->isAllowed('Magento_Sales::delete')) {
            throw new \Magento\Framework\Exception\AuthorizationException(
                __('You are not authorised to delete orders.')
            );
        }
        return $this->realRepository->delete($order);
    }
}

Proxy in Magento 2 – auto-generated lazy loading

<!-- etc/di.xml - declare that a Proxy should be used for this dependency -->
<type name="Vendor\Module\Model\SomeCommand">
    <arguments>
        <!-- \Proxy suffix tells Magento DI to auto-generate a Proxy class -->
        <argument name="heavyService" xsi:type="object">
            Vendor\Module\Model\HeavyService\Proxy
        </argument>
    </arguments>
</type>
<?php

// Why use Magento Proxy?
// If HeavyService has an expensive constructor (DB queries, API calls)
// and SomeCommand only sometimes uses it, Proxy defers instantiation.

class SomeCommand
{
    public function __construct(
        private \Vendor\Module\Model\HeavyService $heavyService // via Proxy in di.xml
    ) {
        // HeavyService is NOT instantiated here - Proxy takes its place
        // Real object only created on first method call
    }

    public function execute(string $mode): string
    {
        if ($mode === 'quick') {
            return 'quick result'; // HeavyService never instantiated
        }

        // HeavyService is created NOW - only when actually needed
        return $this->heavyService->doExpensiveWork();
    }
}
# Magento generates the Proxy class during DI compilation
bin/magento setup:di:compile

# Generated class location
# generated/code/Vendor/Module/Model/HeavyService/Proxy.php

When to use Proxy in Magento 2

  • CLI commands – commands that load many services but use only a few depending on arguments. Proxy prevents instantiating everything on every bin/magento list.
  • Event observers – observers loaded on every request but rarely triggered. Proxy defers construction of heavy dependencies.
  • Circular dependencies – when two services depend on each other, a Proxy on one of them breaks the cycle for DI.

Summary

Proxy is a transparent intermediary – callers do not know they are talking to a proxy. In GoF form it is useful for access control and caching without modifying the original class. In Magento 2 the auto-generated Proxy gives lazy loading for free with a single line in di.xml. It is most valuable in CLI contexts and event observers where you want fast startup times regardless of what the command actually does.

About Henryk Tews

What you can read next

Command and Chain of Responsibility in PHP – behavioural patterns
Flyweight pattern – object sharing, instance cache, Magento 2
Decorator pattern in PHP – composition over inheritance, cached repository example

© 2026 Created by

TOP
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Always active
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Manage options
  • Manage services
  • Manage {vendor_count} vendors
  • Read more about these purposes
Zobacz preferencje
  • {title}
  • {title}
  • {title}