Completed
Push — master ( 7bacbb...3b8d74 )
by Raffael
05:36 queued 03:11
created

Config::getServiceDefaults()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 9
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Micro\Container
7
 *
8
 * @copyright   Copryright (c) 2018-2019 gyselroth GmbH (https://gyselroth.com)
9
 * @license     MIT https://opensource.org/licenses/MIT
10
 */
11
12
namespace Micro\Container;
13
14
class Config
15
{
16
    /**
17
     * Config.
18
     *
19
     * @var iterable
20
     */
21
    protected $config = [];
22
23
    /**
24
     * Compiled config.
25
     *
26
     * @var array
27
     */
28
    protected $compiled = [];
29
30
    /**
31
     * Container.
32
     *
33
     * @var RuntimeContainer
34
     */
35
    protected $container;
36
37
    /**
38
     * Create container.
39
     */
40 62
    public function __construct(iterable $config, RuntimeContainer $container)
41
    {
42 62
        $this->config = $config;
43 62
        $this->container = $container;
44 62
    }
45
46
    /**
47
     * Get config.
48
     */
49 56
    public function getConfig(): iterable
50
    {
51 56
        return $this->config;
52
    }
53
54
    /**
55
     * Check if service is known to container config.
56
     */
57 60
    public function has(string $name): bool
58
    {
59 60
        return isset($this->config[$name]);
60
    }
61
62
    /**
63
     * Get service configuration.
64
     */
65 60
    public function get(string $name): array
66
    {
67 60
        if (isset($this->compiled[$name])) {
68 57
            $config = $this->compiled[$name];
69
        } else {
70 60
            $this->compiled[$name] = $this->createServiceConfig($name);
71 57
            $config = $this->compiled[$name];
72
        }
73
74 57
        if (!isset($config['use'])) {
75 49
            $config['use'] = $name;
76
        }
77
78 57
        return $config;
79
    }
80
81
    /**
82
     * Parse env param.
83
     */
84 44
    public function getEnv(string $param, string $type = 'string')
85
    {
86 44
        if (preg_match_all('#\{ENV\(([A-Za-z0-9_]+)(?:(,?)(.*?))\)(?:\(([a-z]+)\))?\}#', $param, $matches)) {
87 9
            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...
88 9
                $param = $this->parseEnv($param, $matches, $i, $type);
89
            }
90
91 8
            return $param;
92
        }
93
94 36
        return $param;
95
    }
96
97
    /**
98
     * Parse env.
99
     */
100 9
    protected function parseEnv(string $param, array $variables, int $key, string $type = 'string')
101
    {
102 9
        $type = $type ?? 'string';
103 9
        $value = null;
104
105 9
        $env = getenv($variables[1][$key]);
106 9
        if (false === $env && !empty($variables[3][$key])) {
107 2
            $value = str_replace($variables[0][$key], $variables[3][$key], $param);
108 7
        } elseif (false === $env) {
109 1
            throw new Exception\EnvVariableNotFound('env variable '.$variables[1][$key].' required but it is neither set not a default value exists');
110
        } else {
111 6
            $value = str_replace($variables[0][$key], $env, $param);
112
        }
113
114 8
        if (!empty($variables[4][$key])) {
115 2
            $type = $variables[4][$key];
116
        }
117
118 8
        if ('json' === $type) {
119 1
            return json_decode($value, true);
120
        }
121
122 7
        settype($value, $type);
123
124 7
        return $value;
125
    }
126
127
    /**
128
     * Create service config.
129
     */
130 60
    protected function createServiceConfig(string $name): array
131
    {
132 60
        $config = [];
133 60
        if ($this->has($name)) {
134 53
            $config = $this->config[$name];
135
        }
136
137 60
        $class = $name;
138
139 60
        if (isset($config['use'])) {
140 14
            if (!is_string($config['use'])) {
141 1
                throw new Exception\InvalidConfiguration('use must be a string for service '.$name);
142
            }
143
144 13
            $class = $config['use'] = $this->getEnv($config['use']);
145
        }
146
147 59
        if (preg_match('#^\{([^{}]+)\}$#', $class)) {
148 1
            $config = array_merge($this->getServiceDefaults(), $config);
149
150 1
            return $config;
151
        }
152
153 59
        $config = $this->mergeServiceConfig($name, $class, $config);
154
155 59
        if (isset($config['use'])) {
156 13
            $class = $config['use'] = $this->getEnv($config['use']);
157
        }
158
159 59
        if (!class_exists($class)) {
160 4
            throw new Exception\InvalidConfiguration('class '.$class.' is either not a class or can not be found');
161
        }
162
163 57
        return $config;
164
    }
165
166
    /**
167
     * Get service defaults.
168
     */
169 59
    protected function getServiceDefaults(): array
170
    {
171
        return [
172 59
            'merge' => true,
173
            'singleton' => true,
174
            'lazy' => false,
175
            'wrap' => false,
176
            'calls' => [],
177
            'selects' => [],
178
        ];
179
    }
180
181
    /**
182
     * Find parent classes or interfaces and merge service configurations.
183
     */
184 59
    protected function mergeServiceConfig(string $name, string $class, array $config): array
185
    {
186 59
        $config = array_merge($this->getServiceDefaults(), $config);
187
188 59
        if (!class_exists($class) && !interface_exists($class)) {
189 4
            return $config;
190
        }
191
192 57
        if (false === $config['merge']) {
193 1
            return $config;
194
        }
195
196 56
        $tree = $this->getConfigTree();
197 56
        $parents = array_merge(class_implements($class), class_parents($class));
198 56
        foreach ($tree as $parent_config) {
199 56
            foreach ($parents as $parent) {
200 6
                if (isset($parent_config[$parent])) {
201 6
                    $config = array_replace_recursive($parent_config[$parent], $config);
202
                }
203
            }
204
205 56
            if (isset($parent_config[$name])) {
206 49
                $config = array_replace_recursive($parent_config[$name], $config);
207
            }
208
        }
209
210 56
        return $config;
211
    }
212
213
    /**
214
     * Get config tree.
215
     */
216 56
    protected function getConfigTree(): array
217
    {
218 56
        $tree = [$this->getConfig()];
219 56
        $parent = $this->container;
220 56
        while ($parent = $parent->getParent()) {
221 2
            $tree[] = $parent->getConfig()->getConfig();
0 ignored issues
show
Bug introduced by
The method getConfig() does not exist on Psr\Container\ContainerInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

221
            $tree[] = $parent->/** @scrutinizer ignore-call */ getConfig()->getConfig();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
222
        }
223
224 56
        return $tree;
225
    }
226
}
227