PHP Proxy Design Pattern Структурный паттерн

Паттерн Заместитель (Proxy) в PHP

Короткий скринкаст: паттерн «Заместитель» (Proxy) — подставляем объект-сурогат, который контролирует доступ к настоящему объекту. Сделаем ленивую загрузку картинки: тяжёлый объект создаётся при первом обращении и кэшируется.

Proxy Pattern — открыть страницу урока

Описание

Заместитель (Proxy) — это объект того же интерфейса, что и реальный объект. Он перехватывает вызовы и решает, когда и как передать их оригиналу. Частый случай — «ленивая» загрузка: ImageProxy не создаёт RealImage, пока не вызван display(). При повторном вызове настоящий объект уже закэширован, и повторной «загрузки» не происходит. Клиент работает через общий интерфейс Image и не знает, что имеет дело с заместителем.

UML-диаграмма

classDiagram class Image { <<interface>> +display() string } class RealImage { -string file +display() string } class ImageProxy { -string file -RealImage real +display() string } Image <|-- RealImage : implements Image <|-- ImageProxy : implements ImageProxy o-- RealImage : lazily creates

Код

Image.php

interface Image
{
    public function display(): string;
}

RealImage.php

class RealImage implements Image
{
    public function __construct(private string $file)
    {
        echo 'loading: '.$this->file.PHP_EOL;
    }

    public function display(): string
    {
        return 'displaying: '.$this->file;
    }
}

ImageProxy.php

class ImageProxy implements Image
{
    private ?RealImage $real = null;

    public function __construct(private string $file) {}

    public function display(): string
    {
        if ($this->real === null) {
            $this->real = new RealImage($this->file);
        }

        return $this->real->display();
    }
}

demo.php

require_once 'Image.php';
require_once 'RealImage.php';
require_once 'ImageProxy.php';

$image = new ImageProxy('photo.jpg');

echo $image->display().PHP_EOL;
echo $image->display().PHP_EOL;

Пример результата

display() вызван дважды, но «загрузка» произошла ровно один раз — заместитель создал настоящий объект при первом обращении и закэшировал его:

loading: photo.jpg
displaying: photo.jpg
displaying: photo.jpg

Преимущества

  • Контролирует доступ к настоящему объекту, не меняя клиента.
  • Ленивая инициализация: тяжёлый объект создаётся при первом обращении.
  • Позволяет добавить кэширование, логирование или проверку прав.
  • Клиент работает через тот же интерфейс и ничего не замечает.

Когда использовать

  • Когда нужна ленивая загрузка тяжёлого объекта.
  • Когда требуется контроль доступа или проверка прав.
  • Когда нужно кэшировать или логировать обращения.
  • Когда настоящий объект удалённый или дорогой в создании.

Использование в Laravel

В Laravel Заместитель — это фасады: Illuminate\Support\Facades\Facade подставляет статический вызов реальному объекту из контейнера. Отложенная загрузка связей Eloquent (Illuminate\Database\Eloquent\Relations\...) тоже работает как заместитель: обращение к $post->comments инициирует запрос к БД только в момент доступа.