Completed
Push — master ( 67f431...ed31db )
by Andy
01:20
created

Container::setParameter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Palmtree\Container;
4
5
use Palmtree\Container\Definition\Definition;
6
use Palmtree\Container\Exception\ParameterNotFoundException;
7
use Palmtree\Container\Exception\ServiceNotFoundException;
8
use Palmtree\Container\Exception\ServiceNotPublicException;
9
use Psr\Container\ContainerInterface;
10
11
class Container implements ContainerInterface
12
{
13
    /** Regex for single parameters e.g '%my.parameter%' */
14
    const PATTERN_PARAMETER = '/^%([^%\s]+)%$/';
15
    /** Regex for multiple parameters in a string */
16
    const PATTERN_MULTI_PARAMETER = '/%%|%([^%\s]+)%/';
17
    /** Regex for services e.g '@myservice' */
18
    const PATTERN_SERVICE = '/^@(.+)$/';
19
20
    /** @var Definition[] */
21
    private $definitions = [];
22
    /** @var mixed[] */
23
    private $services = [];
24
    /** @var array */
25
    private $parameters = [];
26
27
    /** @var array */
28
    private $envCache = [];
29
30 19
    public function __construct(array $definitions = [], array $parameters = [])
31
    {
32 19
        foreach ($definitions as $key => $definitionArgs) {
33 19
            $this->definitions[$key] = Definition::fromYaml($definitionArgs);
34
        }
35
36 19
        $this->parameters = $parameters;
37 19
        $this->parameters = $this->resolveArgs($this->parameters);
38 19
    }
39
40
    /**
41
     * Instantiates non-lazy services.
42
     */
43 19
    public function instantiateServices()
44
    {
45 19
        foreach ($this->definitions as $key => $definition) {
46 19
            if (!$definition->isLazy()) {
47 19
                $this->services[$key] = $this->create($definition);
48
            }
49
        }
50 19
    }
51
52
    /**
53
     * @param string $key
54
     *
55
     * @return bool
56
     */
57 19
    public function has($key)
58
    {
59 19
        return isset($this->services[$key]) || array_key_exists($key, $this->services);
60
    }
61
62
    /**
63
     * @param string $key
64
     *
65
     * @return bool
66
     */
67 19
    public function hasDefinition($key)
68
    {
69 19
        return isset($this->definitions[$key]) || array_key_exists($key, $this->services);
70
    }
71
72
    /**
73
     * @param string $key
74
     *
75
     * @return mixed
76
     * @throws ServiceNotFoundException
77
     * @throws ServiceNotPublicException
78
     */
79 19
    public function get($key)
80
    {
81 19
        if (!$this->has($key)) {
82 19
            if (!$this->hasDefinition($key)) {
83 1
                throw new ServiceNotFoundException($key);
84
            }
85
86 19
            $this->services[$key] = $this->create($this->definitions[$key]);
87
        }
88
89 19
        if (!$this->definitions[$key]->isPublic()) {
90 19
            throw new ServiceNotPublicException($key);
91
        }
92
93 19
        return $this->services[$key];
94
    }
95
96
    /**
97
     * @param string $key
98
     *
99
     * @return mixed
100
     * @throws ServiceNotFoundException
101
     */
102 19
    private function inject($key)
103
    {
104
        try {
105
            // Ensure the service has been created
106 19
            $this->get($key);
107 19
        } catch (ServiceNotPublicException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
108
        }
109
110 19
        return $this->services[$key];
111
    }
112
113
    /**
114
     * @param string $key
115
     * @param mixed  $default
116
     *
117
     * @return mixed
118
     * @throws ParameterNotFoundException
119
     */
120 19
    public function getParameter($key, $default = null)
121
    {
122 19
        if (!$this->hasParameter($key)) {
123 1
            if (func_num_args() < 2) {
124 1
                throw new ParameterNotFoundException($key);
125
            }
126
127
            return $default;
128
        }
129
130 19
        return $this->parameters[$key];
131
    }
132
133
    /**
134
     * @param $key
135
     * @param $value
136
     *
137
     * @throws ParameterNotFoundException
138
     * @throws ServiceNotFoundException
139
     */
140 19
    public function setParameter($key, $value)
141
    {
142 19
        $this->parameters[$key] = $this->resolveArg($value);
143 19
    }
144
145
    /**
146
     * @param string $key
147
     *
148
     * @return bool
149
     */
150 19
    public function hasParameter($key)
151
    {
152 19
        return isset($this->parameters[$key]) || array_key_exists($key, $this->parameters);
153
    }
154
155
    /**
156
     * Creates a service as defined by the Definition object.
157
     *
158
     * @param Definition $definition
159
     *
160
     * @return mixed
161
     */
162 19
    private function create(Definition $definition)
163
    {
164 19
        $args = $this->resolveArgs($definition->getArguments());
165
166 19
        if ($definition->getFactory()) {
167
            list($class, $method) = $definition->getFactory();
168
            $class  = $this->resolveArg($class);
169
            $method = $this->resolveArg($method);
170
            $service = $class::$method(...$args);
171
        } else {
172 19
            $class = $this->resolveArg($definition->getClass());
173 19
            $service = new $class(...$args);
174
        }
175
176 19
        foreach ($definition->getMethodCalls() as $methodCall) {
177 19
            $methodName = $methodCall->getName();
178 19
            $methodArgs = $this->resolveArgs($methodCall->getArguments());
179 19
            $service->$methodName(...$methodArgs);
180
        }
181
182 19
        return $service;
183
    }
184
185
    /**
186
     * @param array $args
187
     *
188
     * @return array
189
     */
190 19
    private function resolveArgs(array $args)
191
    {
192 19
        foreach ($args as $key => &$arg) {
193 19
            if (is_array($arg)) {
194 19
                $arg = $this->resolveArgs($arg);
195
            } else {
196 19
                $arg = $this->resolveArg($arg);
197
            }
198
        }
199
200 19
        return $args;
201
    }
202
203
    /**
204
     * @param $arg
205
     *
206
     * @return mixed|string
207
     * @throws ParameterNotFoundException
208
     * @throws ServiceNotFoundException
209
     */
210 19
    private function resolveArg($arg)
211
    {
212 19
        if (!is_string($arg)) {
213 19
            return $arg;
214
        }
215
216 19
        if (preg_match(self::PATTERN_SERVICE, $arg, $matches)) {
217 19
            return $this->inject($matches[1]);
218
        }
219
220
        // Resolve a single parameter value e.g %my_param%
221
        // Used for non-string values (boolean, integer etc)
222 19
        if (preg_match(self::PATTERN_PARAMETER, $arg, $matches)) {
223 19
            $envKey = $this->getEnvParameterKey($matches[1]);
224
225 19
            if (!is_null($envKey)) {
226 19
                return $this->getEnv($envKey);
227
            }
228
229 19
            return $this->getParameter($matches[1]);
230
        }
231
232
        // Resolve multiple parameters in a string e.g /%parent_dir%/somewhere/%child_dir%
233 19
        return preg_replace_callback(self::PATTERN_MULTI_PARAMETER, function ($matches) {
234
            // Skip %% to allow escaping percent signs
235 19
            if (!isset($matches[1])) {
236 19
                return '%';
237 19
            } elseif ($envKey = $this->getEnvParameterKey($matches[1])) {
238
                return $this->getEnv($envKey);
239
            } else {
240 19
                return $this->getParameter($matches[1]);
241
            }
242 19
        }, $arg);
243
    }
244
245
    /**
246
     * @param string $value
247
     *
248
     * @return null|string
249
     */
250 19
    private function getEnvParameterKey($value)
251
    {
252 19
        if (strpos($value, 'env(') === 0 && substr($value, -1) === ')' && $value !== 'env()') {
253 19
            return substr($value, 4, -1);
254
        }
255
256 19
        return null;
257
    }
258
259
    /**
260
     * @param string $key
261
     *
262
     * @return string|bool
263
     */
264 19
    private function getEnv($key)
265
    {
266 19
        if (isset($this->envCache[$key]) || array_key_exists($key, $this->envCache)) {
267
            return $this->envCache[$key];
268
        }
269
270 19
        $envVar = getenv($key);
271
272 19
        if (!$envVar) {
273
            try {
274 18
                $envVar = $this->resolveArg($this->getParameter("env($key)"));
275
            } catch (ParameterNotFoundException $exception) {
276
                // do nothing
277
            }
278
        }
279
280 19
        $this->envCache[$key] = $envVar;
281
282 19
        return $this->envCache[$key];
283
    }
284
}
285