Completed
Pull Request — master (#1)
by Raffael
02:24
created

Config::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
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 42
    public function __construct(Iterable $config, ContainerInterface $container)
45
    {
46 42
        $this->config = $config;
47 42
        $this->container = $container;
48 42
    }
49
50
    /**
51
     * Get config.
52
     *
53
     * @return iterable
54
     */
55 33
    public function getConfig(): Iterable
56
    {
57 33
        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 36
    public function has(string $name): bool
68
    {
69 36
        return isset($this->config[$name]);
70
    }
71
72
    /**
73
     * Get service configuration.
74
     *
75
     * @param string $name
76
     *
77
     * @return array
78
     */
79 36
    public function get(string $name): array
80
    {
81 36
        if (isset($this->compiled[$name])) {
82 12
            $config = $this->compiled[$name];
83
        } else {
84 36
            $this->compiled[$name] = $this->createServiceConfig($name);
85 34
            $config = $this->compiled[$name];
86
        }
87
88 34
        if (!isset($config['use'])) {
89 31
            $config['use'] = $name;
90
        }
91
92 34
        return $config;
93
    }
94
95
    /**
96
     * Parse env param.
97
     *
98
     * @param string $param
99
     *
100
     * @return string
101
     */
102 26
    public function getEnv(string $param): string
103
    {
104 26
        if (preg_match_all('#\{ENV\(([A-Za-z0-9_]+)(?:(,?)([^}]*))\)\}#', $param, $matches)) {
105 4
            if (4 !== count($matches)) {
106
                return $param;
107
            }
108
109 4
            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 4
                $param = $this->parseEnv($param, $matches, $i);
111
            }
112
113 3
            return $param;
114
        }
115
116 22
        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 4
    protected function parseEnv(string $param, array $variables, int $key): string
129
    {
130 4
        $env = getenv($variables[1][$key]);
131 4
        if (false === $env && !empty($variables[3][$key])) {
132 1
            return str_replace($variables[0][$key], $variables[3][$key], $param);
133
        }
134 3
        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 2
        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 36
    protected function createServiceConfig(string $name): array
149
    {
150 36
        $config = [];
151 36
        if ($this->has($name)) {
152 33
            $config = $this->config[$name];
153
        }
154
155 36
        $class = $name;
156 36
        if (isset($config['use'])) {
157 9
            if (!is_string($config['use'])) {
158 1
                throw new Exception\InvalidConfiguration('use must be a string for service '.$name);
159
            }
160
161 8
            $class = $config['use'];
162
        }
163
164 35
        if (preg_match('#^\{([^{}]+)\}$#', $class)) {
165 1
            $config = array_merge($this->getServiceDefaults(), $config);
166
167 1
            return $config;
168
        }
169
170 35
        $config = $this->mergeServiceConfig($name, $class, $config);
171
172 35
        if (isset($config['use'])) {
173 8
            $class = $config['use'];
174
        }
175
176 35
        if (!class_exists($class)) {
177 3
            throw new Exception\InvalidConfiguration('class '.$class.' is either not a class or can not be found');
178
        }
179
180 34
        return $config;
181
    }
182
183
    /**
184
     * Get service defaults.
185
     *
186
     * @return array
187
     */
188 35
    protected function getServiceDefaults(): array
189
    {
190
        return [
191 35
            '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 35
    protected function mergeServiceConfig(string $name, string $class, array $config): array
209
    {
210 35
        $config = array_merge($this->getServiceDefaults(), $config);
211
212 35
        if (!class_exists($class) && !interface_exists($class)) {
213 3
            return $config;
214
        }
215
216 34
        if (false === $config['merge']) {
217 1
            return $config;
218
        }
219
220 33
        $tree = $this->getConfigTree();
221 33
        $parents = array_merge(class_implements($class), class_parents($class));
222 33
        foreach ($tree as $parent_config) {
223 33
            foreach ($parents as $parent) {
224 6
                if (isset($parent_config[$parent])) {
225 6
                    $config = array_replace_recursive($config, $parent_config[$parent]);
226
                }
227
            }
228
229 33
            if (isset($parent_config[$name])) {
230 33
                $config = array_replace_recursive($config, $parent_config[$name]);
231
            }
232
        }
233
234 33
        return $config;
235
    }
236
237
    /**
238
     * Get config tree.
239
     *
240
     * @return array
241
     */
242 33
    protected function getConfigTree(): array
243
    {
244 33
        $tree = [$this->getConfig()];
245 33
        $parent = $this->container;
246 33
        while ($parent = $parent->getParent()) {
247 2
            $tree[] = $parent->getConfig()->getConfig();
248
        }
249
250 33
        return $tree;
251
    }
252
}
253