Passed
Push — master ( ca49a6...af21e2 )
by Andy
02:13
created

Container::getEnvParameterKey()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 4

Importance

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