Completed
Push — master ( 086554...ec0577 )
by Raffael
02:12
created

Config   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 225
Duplicated Lines 0 %

Test Coverage

Coverage 98.51%

Importance

Changes 0
Metric Value
dl 0
loc 225
ccs 66
cts 67
cp 0.9851
rs 10
c 0
b 0
f 0
wmc 30

10 Methods

Rating   Name   Duplication   Size   Complexity  
A has() 0 3 1
A get() 0 14 3
A parseEnv() 0 11 4
A getEnv() 0 15 4
A getConfig() 0 3 1
A __construct() 0 4 1
A getServiceDefaults() 0 8 1
C mergeServiceConfig() 0 27 8
B createServiceConfig() 0 23 5
A getConfigTree() 0 9 2
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 32
            $config = $this->compiled[$name];
86
        }
87
88 32
        if (!isset($config['use'])) {
89 29
            $config['use'] = $name;
90
        }
91
92 32
        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) {
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
        return $this->mergeServiceConfig($name, $class, $config);
171
    }
172
173
    /**
174
     * Get service defaults.
175
     *
176
     * @return array
177
     */
178 32
    protected function getServiceDefaults(): array
179
    {
180
        return [
181 32
            'merge' => true,
182
            'singleton' => false,
183
            'lazy' => false,
184
            'calls' => [],
185
            'selects' => [],
186
        ];
187
    }
188
189
    /**
190
     * Find parent classes or interfaces and merge service configurations.
191
     *
192
     * @param string $name
193
     * @param string $class
194
     * @param array  $config
195
     *
196
     * @return array
197
     */
198 32
    protected function mergeServiceConfig(string $name, string $class, array $config): array
199
    {
200 32
        $config = array_merge($this->getServiceDefaults(), $config);
201
202 32
        if (!class_exists($class) && !interface_exists($class)) {
203 1
            return $config;
204
        }
205
206 31
        if (false === $config['merge']) {
207 1
            return $this->config[$name];
208
        }
209
210 30
        $tree = $this->getConfigTree();
211 30
        $parents = array_merge(class_implements($class), class_parents($class));
212 30
        foreach ($tree as $parent_config) {
213 30
            foreach ($parents as $parent) {
214 7
                if (isset($parent_config[$parent])) {
215 7
                    $config = array_replace_recursive($config, $parent_config[$parent]);
216
                }
217
            }
218
219 30
            if (isset($parent_config[$name])) {
220 30
                $config = array_replace_recursive($config, $parent_config[$name]);
221
            }
222
        }
223
224 30
        return $config;
225
    }
226
227
    /**
228
     * Get config tree.
229
     *
230
     * @return array
231
     */
232 30
    protected function getConfigTree(): array
233
    {
234 30
        $tree = [$this->getConfig()];
235 30
        $parent = $this->container;
236 30
        while ($parent = $parent->getParent()) {
237 1
            $tree[] = $parent->getConfig()->getConfig();
238
        }
239
240 30
        return $tree;
241
    }
242
}
243