3.7. Proxy

3.7.1. Intent

According to the Gang of Four, the Proxy pattern is a way to “provide a surrogate or placeholder for another object to control access to it” (Design Patterns: Elements of Reusable Object-Oriented Software, 2013, p. 207).

3.7.2. When to use it?

Proxy pattern should be used when it’s useful to have a placeholder instead of another object (remote and/or resource hungry actual object, access control, etc.). This has several benefits such as creating objects only on demand or hiding the fact that objects are remote located.

3.7.3. Diagram

Created using PhpStorm and yFiles.

Class diagram of the Proxy pattern

3.7.4. Implementation

Employee.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php

namespace Phpatterns\Structural\Proxy;

class Employee
{
    const ACCESS_LEVEL_LOW = 0; //no access to WiFi
    const ACCESS_LEVEL_HIGH = 1; //access granted to WiFi

    /** @var string */
    private $firstName;

    /** @var string */
    private $lastName;

    /** @var int */
    private $accessLevel;

    /**
     * @param string $firstName
     * @param string $lastName
     * @param int $accessLevel
     *  0 = No access to WiFi
     *  1 = Access granted to WiFi
     */
    public function __construct($firstName, $lastName, $accessLevel)
    {
        $this->firstName = $firstName;
        $this->lastName = $lastName;
        $this->accessLevel = $accessLevel;
    }

    /**
     * @return int
     */
    public function getAccessLevel()
    {
        return $this->accessLevel;
    }
}

WifiNetworkInterface.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<?php

namespace Phpatterns\Structural\Proxy;

interface WifiNetworkInterface
{
    /**
     * Grant access to the WiFi network to an employee
     * @param Employee $employee
     * @return bool
     */
    public function grantAccess(Employee $employee);
}

WifiNetwork.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php

namespace Phpatterns\Structural\Proxy;

class WifiNetwork implements WifiNetworkInterface
{
    /** @var string */
    private $name;

    /**
     * @param string $name
     */
    public function __construct($name)
    {
        $this->name = $name;
    }

    /**
     * Grant access to the WiFi network to an employee
     * @param Employee $employee
     * @return bool
     */
    public function grantAccess(Employee $employee)
    {
        //it's a public network, access granted to everyone!
        return true;
    }
}

WifiNetworkProxy.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php

namespace Phpatterns\Structural\Proxy;

class WifiNetworkProxy implements WifiNetworkInterface
{
    /** @var WifiNetwork */
    private $wifiNetwork;

    /**
     * @param string $name
     */
    public function __construct($name)
    {
        $this->wifiNetwork = new WifiNetwork($name);
    }

    /**
     * Grant access to the WiFi network to an employee
     * @param Employee $employee
     * @return bool
     */
    public function grantAccess(Employee $employee)
    {
        //now, it's a network with "access control"
        if ($employee->getAccessLevel() === Employee::ACCESS_LEVEL_HIGH) {
            return $this->wifiNetwork->grantAccess($employee);
        }

        return false;
    }
}

3.7.5. Tests

ProxyTest.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php

namespace Test\Phpatterns\Structural\Proxy;

use Phpatterns\Structural\Proxy;

class ProxyTest extends \PHPUnit_Framework_TestCase
{
    /** @var Proxy\WifiNetworkProxy */
    private $wifiNetworkProxy;

    /** @var Proxy\WifiNetwork */
    private $wifiNetwork;

    protected function setUp()
    {
        $this->wifiNetworkProxy = new Proxy\WifiNetworkProxy("My Company Name Network");
        $this->wifiNetwork = new Proxy\WifiNetwork("My Company Name Network");
    }


    public function testInterfaceImplementation()
    {
        $this->assertInstanceOf(Proxy\WifiNetworkInterface::class, $this->wifiNetworkProxy);
        $this->assertInstanceOf(Proxy\WifiNetworkInterface::class, $this->wifiNetwork);
    }

    public function testWifiNetworkProxyAccess()
    {
        $userLowLevel = new Proxy\Employee('John', 'Doe', Proxy\Employee::ACCESS_LEVEL_LOW);
        $this->assertFalse($this->wifiNetworkProxy->grantAccess($userLowLevel));

        $userHighLevel = new Proxy\Employee('Jason', 'Statham', Proxy\Employee::ACCESS_LEVEL_HIGH);
        $this->assertTrue($this->wifiNetworkProxy->grantAccess($userHighLevel));
    }

    public function testWifiNetworkAccess()
    {
        $userLowLevel = new Proxy\Employee('Halle', 'Berry', Proxy\Employee::ACCESS_LEVEL_LOW);
        $this->assertTrue($this->wifiNetwork->grantAccess($userLowLevel));

        $userHighLevel = new Proxy\Employee('Jennifer', 'Lawrence', Proxy\Employee::ACCESS_LEVEL_HIGH);
        $this->assertTrue($this->wifiNetwork->grantAccess($userHighLevel));
    }
}