2.10. Proxy
2.10.1. Propósito
Para interactuar con cualquier cosa que sea costosa o imposible de duplicar.
2.10.2. Ejemplos
Doctrine2 usa proxies para implementar la magia del framework (e.j., inicialización diferida) en ellos, mientras el usuario trabaja con su propia entidad de clases y nunca usará ni tocará los proxies
2.10.3. Diagrama UML

2.10.4. Código
Puedes encontrar el código en GitHub
BankAccount.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Structural\Proxy;
6
7interface BankAccount
8{
9 public function deposit(int $amount);
10
11 public function getBalance(): int;
12}
HeavyBankAccount.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Structural\Proxy;
6
7class HeavyBankAccount implements BankAccount
8{
9 /**
10 * @var int[]
11 */
12 private array $transactions = [];
13
14 public function deposit(int $amount)
15 {
16 $this->transactions[] = $amount;
17 }
18
19 public function getBalance(): int
20 {
21 // this is the heavy part, imagine all the transactions even from
22 // years and decades ago must be fetched from a database or web service
23 // and the balance must be calculated from it
24
25 return array_sum($this->transactions);
26 }
27}
BankAccountProxy.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Structural\Proxy;
6
7class BankAccountProxy extends HeavyBankAccount implements BankAccount
8{
9 private ?int $balance = null;
10
11 public function getBalance(): int
12 {
13 // because calculating balance is so expensive,
14 // the usage of BankAccount::getBalance() is delayed until it really is needed
15 // and will not be calculated again for this instance
16
17 if ($this->balance === null) {
18 $this->balance = parent::getBalance();
19 }
20
21 return $this->balance;
22 }
23}
2.10.5. Test
ProxyTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Structural\Proxy\Tests;
6
7use DesignPatterns\Structural\Proxy\BankAccountProxy;
8use PHPUnit\Framework\TestCase;
9
10class ProxyTest extends TestCase
11{
12 public function testProxyWillOnlyExecuteExpensiveGetBalanceOnce()
13 {
14 $bankAccount = new BankAccountProxy();
15 $bankAccount->deposit(30);
16
17 // this time balance is being calculated
18 $this->assertSame(30, $bankAccount->getBalance());
19
20 // inheritance allows for BankAccountProxy to behave to an outsider exactly like ServerBankAccount
21 $bankAccount->deposit(50);
22
23 // this time the previously calculated balance is returned again without re-calculating it
24 $this->assertSame(30, $bankAccount->getBalance());
25 }
26}