Passed
Branch dev (aff17b)
by Raffael
02:53
created

Config::parseEnv()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 6
nc 3
nop 3
dl 0
loc 11
ccs 7
cts 7
cp 1
crap 4
rs 9.2
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Micro\Container
7
 *
8
 * @copyright   Copryright (c) 2018 gyselroth GmbH (https://gyselroth.com)
9
 * @license     MIT https://opensource.org/licenses/MIT
10
 */
11
12
namespace Micro\Container;
13
14
use Psr\Container\ContainerInterface;
15
16
class Config
17
{
18
    /**
19
     * Config.
20
     *
21
     * @var iterable
22
     */
23
    protected $config = [];
24
25
    /**
26
     * Compiled config.
27
     *
28
     * @var array
29
     */
30
    protected $compiled = [];
31
32
    /**
33
     * Container.
34
     *
35
     * @var ContainerInterface
36
     */
37
    protected $container;
38
39
    /**
40
     * Create container.
41
     *
42
     * @param iterable $config
43
     */
44 41
    public function __construct(Iterable $config, ContainerInterface $container)
45
    {
46 41
        $this->config = $config;
47 41
        $this->container = $container;
48 41
    }
49
50
    /**
51
     * Get config.
52
     *
53
     * @return iterable
54
     */
55 30
    public function getConfig(): Iterable
56
    {
57 30
        return $this->config;
58
    }
59
60
    /**
61
     * Check if service is known to container config.
62
     *
63
     * @param string $name
64
     *
65
     * @return bool
66
     */
67 34
    public function has(string $name): bool
68
    {
69 34
        return isset($this->config[$name]);
70
    }
71
72
    /**
73
     * Get service configuration.
74
     *
75
     * @param string $name
76
     *
77
     * @return array
78
     */
79 33
    public function get(string $name): array
80
    {
81 33
        if (isset($this->compiled[$name])) {
82 8
            $config = $this->compiled[$name];
83
        } else {
84 33
            $this->compiled[$name] = $this->createServiceConfig($name);
85 31
            $config = $this->compiled[$name];
86
        }
87
88 31
        if (!isset($config['use'])) {
89 28
            $config['use'] = $name;
90
        }
91
92 31
        return $config;
93
    }
94
95
    /**
96
     * Parse env param.
97
     *
98
     * @param string $param
99
     *
100
     * @return string
101
     */
102 23
    public function getEnv(string $param): string
103
    {
104 23
        if (preg_match_all('#\{ENV\(([A-Za-z0-9_]+)(?:(,?)([^}]*))\)\}#', $param, $matches)) {
105 5
            if (4 !== count($matches)) {
106
                return $param;
107
            }
108
109 5
            for ($i = 0; $i < count($matches[0]); ++$i) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
110 5
                $param = $this->parseEnv($param, $matches, $i);
111
            }
112
113 4
            return $param;
114
        }
115
116 18
        return $param;
117
    }
118
119
    /**
120
     * Parse env.
121
     *
122
     * @param string $param
123
     * @param array  $variables
124
     * @param int    $key
125
     *
126
     * @return string
127
     */
128 5
    protected function parseEnv(string $param, array $variables, int $key): string
129
    {
130 5
        $env = getenv($variables[1][$key]);
131 5
        if (false === $env && !empty($variables[3][$key])) {
132 1
            return str_replace($variables[0][$key], $variables[3][$key], $param);
133
        }
134 4
        if (false === $env) {
0 ignored issues
show
introduced by
The condition false === $env is always false.
Loading history...
135 1
            throw new Exception\EnvVariableNotFound('env variable '.$variables[1][$key].' required but it is neither set not a default value exists');
136
        }
137
138 3
        return str_replace($variables[0][$key], $env, $param);
139
    }
140
141
    /**
142
     * Create service config.
143
     *
144
     * @param string $name
145
     *
146
     * @return array
147
     */
148 33
    protected function createServiceConfig(string $name): array
149
    {
150 33
        $config = [];
151 33
        if ($this->has($name)) {
152 30
            $config = $this->config[$name];
153
        }
154
155 33
        $class = $name;
156 33
        if (isset($config['use'])) {
157 6
            if (!is_string($config['use'])) {
158 1
                throw new Exception\InvalidConfiguration('use must be a string for service '.$name);
159
            }
160
161 5
            $class = $config['use'];
162
        }
163
164 32
        if (preg_match('#^\{([^{}]+)\}$#', $class)) {
165 1
            $config = array_merge($this->getServiceDefaults(), $config);
166
167 1
            return $config;
168
        }
169
170 32
        $config = $this->mergeServiceConfig($name, $class, $config);
171
172 32
        if (isset($config['use'])) {
173 5
            $class = $config['use'];
174
        }
175
176 32
        if (!class_exists($class)) {
177 1
            throw new Exception\InvalidConfiguration('class '.$class.' is either not a class or can not be found');
178
        }
179
180 31
        return $config;
181
    }
182
183
    /**
184
     * Get service defaults.
185
     *
186
     * @return array
187
     */
188 32
    protected function getServiceDefaults(): array
189
    {
190
        return [
191 32
            'merge' => true,
192
            'singleton' => false,
193
            'lazy' => false,
194
            'calls' => [],
195
            'selects' => [],
196
        ];
197
    }
198
199
    /**
200
     * Find parent classes or interfaces and merge service configurations.
201
     *
202
     * @param string $name
203
     * @param string $class
204
     * @param array  $config
205
     *
206
     * @return array
207
     */
208 32
    protected function mergeServiceConfig(string $name, string $class, array $config): array
209
    {
210 32
        $config = array_merge($this->getServiceDefaults(), $config);
211
212 32
        if (!class_exists($class) && !interface_exists($class)) {
213 1
            return $config;
214
        }
215
216 31
        if (false === $config['merge']) {
217 1
            return $config;
218
        }
219
220 30
        $tree = $this->getConfigTree();
221 30
        $parents = array_merge(class_implements($class), class_parents($class));
222 30
        foreach ($tree as $parent_config) {
223 30
            foreach ($parents as $parent) {
224 7
                if (isset($parent_config[$parent])) {
225 7
                    $config = array_replace_recursive($config, $parent_config[$parent]);
226
                }
227
            }
228
229 30
            if (isset($parent_config[$name])) {
230 30
                $config = array_replace_recursive($config, $parent_config[$name]);
231
            }
232
        }
233
234 30
        return $config;
235
    }
236
237
    /**
238
     * Get config tree.
239
     *
240
     * @return array
241
     */
242 30
    protected function getConfigTree(): array
243
    {
244 30
        $tree = [$this->getConfig()];
245 30
        $parent = $this->container;
246 30
        while ($parent = $parent->getParent()) {
247 1
            $tree[] = $parent->getConfig()->getConfig();
248
        }
249
250 30
        return $tree;
251
    }
252
}
253