Паттерн Заместитель (Proxy) в PHP
Короткий скринкаст: паттерн «Заместитель» (Proxy) — подставляем объект-сурогат, который контролирует доступ к настоящему объекту. Сделаем ленивую загрузку картинки: тяжёлый объект создаётся при первом обращении и кэшируется.
Описание
Заместитель (Proxy) — это объект того же интерфейса, что и реальный объект. Он перехватывает вызовы и решает, когда и как передать их оригиналу. Частый случай — «ленивая» загрузка: ImageProxy не создаёт RealImage, пока не вызван display(). При повторном вызове настоящий объект уже закэширован, и повторной «загрузки» не происходит. Клиент работает через общий интерфейс Image и не знает, что имеет дело с заместителем.
UML-диаграмма
Код
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 инициирует запрос к БД только в момент доступа.