Completed
Branch master (cb7f3b)
by Dan
03:01 queued 01:19
created

ConfigurableServiceLocator::validateConfig()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 5
nc 4
nop 1
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace danmurf\DependencyInjection;
4
5
use danmurf\DependencyInjection\Exception\ContainerException;
6
use danmurf\DependencyInjection\Exception\NotFoundException;
7
use Psr\Container\ContainerInterface;
8
use ReflectionClass;
9
10
/**
11
 * Service locator which can get find and build services using
12
 * pre-defined configuration.
13
 *
14
 * @author Dan Murfitt <[email protected]>
15
 */
16
class ConfigurableServiceLocator implements ServiceLocatorInterface
17
{
18
    const ARGUMENT_TYPE_SCALAR = 'scalar';
19
    const ARGUMENT_TYPE_SERVICE = 'service';
20
21
    const ARGUMENT_TYPES = [
22
            self::ARGUMENT_TYPE_SCALAR,
23
            self::ARGUMENT_TYPE_SERVICE,
24
        ];
25
26
    /** @var array */
27
    private $config;
28
29
    /**
30
     * @param array $config
31
     */
32
    public function __construct(array $config)
33
    {
34
        $this->validateConfig($config);
35
        $this->config = $config;
36
    }
37
38
    /**
39
     * Get a new instance of a service.
40
     *
41
     * @param string             $id        the service id
42
     * @param ContainerInterface $container the container to get service dependencies from
43
     *
44
     * @throws ContainerException
45
     * @throws NotFoundException
46
     */
47
    public function locate($id, ContainerInterface $container)
48
    {
49
        if (!isset($this->config[$id])) {
50
            throw new NotFoundException(sprintf('Unable to locate service `%s` from configuration.', $id));
51
        }
52
53
        $class = new ReflectionClass($this->config[$id]['class']);
54
55
        if (!isset($this->config[$id]['arguments'])) {
56
            return $class->newInstance();
57
        }
58
59
        $args = [];
60
        foreach ($this->config[$id]['arguments'] as $argumentConfig) {
61
            switch ($argumentConfig['type']) {
62
                case self::ARGUMENT_TYPE_SCALAR:
63
                    $args[] = $argumentConfig['value'];
64
                    break;
65
66
                case self::ARGUMENT_TYPE_SERVICE:
67
                    $args[] = $container->get($argumentConfig['value']);
68
                    break;
69
            }
70
        }
71
72
        return $class->newInstanceArgs($args);
73
    }
74
75
    /**
76
     * Ensure the config is in a valid format.
77
     *
78
     * @param array $config
79
     *
80
     * @throws ContainerException
81
     */
82
    private function validateConfig(array $config)
83
    {
84
        foreach ($config as $id => $service) {
85
            if (!isset($service['class'])) {
86
                throw new ContainerException(sprintf('Configured service `%s` has no `class` value.', $id));
87
            }
88
89
            if (isset($service['arguments'])) {
90
                $this->validateArguments($service['arguments'], $id);
91
            }
92
        }
93
    }
94
95
    /**
96
     * Ensure the arguments config is in a valid format.
97
     *
98
     * @param array  $arguments
99
     * @param string $id
100
     *
101
     * @throws ContainerException
102
     */
103
    private function validateArguments(array $arguments, string $id)
104
    {
105
        foreach ($arguments as $argument) {
106
            $this->validateArgumentProperties($argument, $id);
107
            $this->validateArgumentType($argument['type'], $id);
108
        }
109
    }
110
111
    /**
112
     * Ensure the argument has valid properties.
113
     *
114
     * @param array  $argument
115
     * @param string $id
116
     *
117
     * @throws ContainerException
118
     */
119
    private function validateArgumentProperties(array $argument, string $id)
120
    {
121
        if (!isset($argument['type']) || !isset($argument['value'])) {
122
            throw new ContainerException(sprintf(
123
                'Configuration for service `%s` must have `type` and `value` values.',
124
                $id
125
            ));
126
        }
127
    }
128
129
    /**
130
     * Ensure the argument type is valid.
131
     *
132
     * @param string $type
133
     * @param string $id
134
     *
135
     * @throws ContainerException
136
     */
137
    private function validateArgumentType(string $type, string $id)
138
    {
139
        if (false === array_search($type, self::ARGUMENT_TYPES)) {
140
            throw new ContainerException(sprintf(
141
                'Unknown argument type `%s` for service `%s`. Accepted values are `scalar` and `service`.',
142
                $type,
143
                $id
144
            ));
145
        }
146
    }
147
}
148