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 | 64 | public function __construct(iterable $config, RuntimeContainer $container) |
|||
41 | { |
||||
42 | 64 | $this->config = $config; |
|||
43 | 64 | $this->container = $container; |
|||
44 | 64 | } |
|||
45 | |||||
46 | /** |
||||
47 | * Get config. |
||||
48 | */ |
||||
49 | 58 | public function getConfig(): iterable |
|||
50 | { |
||||
51 | 58 | return $this->config; |
|||
52 | } |
||||
53 | |||||
54 | /** |
||||
55 | * Check if service is known to container config. |
||||
56 | */ |
||||
57 | 62 | public function has(string $name): bool |
|||
58 | { |
||||
59 | 62 | return isset($this->config[$name]); |
|||
60 | } |
||||
61 | |||||
62 | /** |
||||
63 | * Get service configuration. |
||||
64 | */ |
||||
65 | 62 | public function get(string $name): array |
|||
66 | { |
||||
67 | 62 | if (isset($this->compiled[$name])) { |
|||
68 | 59 | $config = $this->compiled[$name]; |
|||
69 | } else { |
||||
70 | 62 | $this->compiled[$name] = $this->createServiceConfig($name); |
|||
71 | 59 | $config = $this->compiled[$name]; |
|||
72 | } |
||||
73 | |||||
74 | 59 | if (!isset($config['use'])) { |
|||
75 | 51 | $config['use'] = $name; |
|||
76 | } |
||||
77 | |||||
78 | 59 | return $config; |
|||
79 | } |
||||
80 | |||||
81 | /** |
||||
82 | * Parse env param. |
||||
83 | */ |
||||
84 | 45 | public function getEnv(string $param, string $type = 'string') |
|||
85 | { |
||||
86 | 45 | if (preg_match_all('#\{ENV\(([A-Za-z0-9_]+)(?:(,?)(.*?))\)(?:\(([a-z]+)\))?\}#s', $param, $matches)) { |
|||
87 | 10 | for ($i = 0; $i < count($matches[0]); ++$i) { |
|||
0 ignored issues
–
show
|
|||||
88 | 10 | $param = $this->parseEnv($param, $matches, $i, $type); |
|||
89 | } |
||||
90 | |||||
91 | 9 | return $param; |
|||
92 | } |
||||
93 | |||||
94 | 36 | return $param; |
|||
95 | } |
||||
96 | |||||
97 | /** |
||||
98 | * Parse env. |
||||
99 | */ |
||||
100 | 10 | protected function parseEnv(string $param, array $variables, int $key, string $type = 'string') |
|||
101 | { |
||||
102 | 10 | $type = $type ?? 'string'; |
|||
103 | 10 | $value = null; |
|||
104 | |||||
105 | 10 | $env = getenv($variables[1][$key]); |
|||
106 | 10 | if (false === $env && !empty($variables[3][$key])) { |
|||
107 | 2 | $value = str_replace($variables[0][$key], $variables[3][$key], $param); |
|||
108 | 8 | } 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 | 7 | $value = str_replace($variables[0][$key], $env, $param); |
|||
112 | } |
||||
113 | |||||
114 | 9 | if (!empty($variables[4][$key])) { |
|||
115 | 2 | $type = $variables[4][$key]; |
|||
116 | } |
||||
117 | |||||
118 | 9 | if ('json' === $type) { |
|||
119 | 1 | return json_decode($value, true); |
|||
120 | } |
||||
121 | |||||
122 | 8 | settype($value, $type); |
|||
123 | |||||
124 | 8 | return $value; |
|||
125 | } |
||||
126 | |||||
127 | /** |
||||
128 | * Create service config. |
||||
129 | */ |
||||
130 | 62 | protected function createServiceConfig(string $name): array |
|||
131 | { |
||||
132 | 62 | $config = []; |
|||
133 | 62 | if ($this->has($name)) { |
|||
134 | 54 | $config = $this->config[$name]; |
|||
135 | } |
||||
136 | |||||
137 | 62 | $class = $name; |
|||
138 | |||||
139 | 62 | 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 | 61 | if (preg_match('#^\{([^{}]+)\}$#', $class)) { |
|||
148 | 1 | $config = array_merge($this->getServiceDefaults(), $config); |
|||
149 | |||||
150 | 1 | return $config; |
|||
151 | } |
||||
152 | |||||
153 | 61 | $config = $this->mergeServiceConfig($name, $class, $config); |
|||
154 | |||||
155 | 61 | if (isset($config['use'])) { |
|||
156 | 13 | $class = $config['use'] = $this->getEnv($config['use']); |
|||
157 | } |
||||
158 | |||||
159 | 61 | 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 | 59 | return $config; |
|||
164 | } |
||||
165 | |||||
166 | /** |
||||
167 | * Get service defaults. |
||||
168 | */ |
||||
169 | 61 | protected function getServiceDefaults(): array |
|||
170 | { |
||||
171 | return [ |
||||
172 | 61 | '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 | 61 | protected function mergeServiceConfig(string $name, string $class, array $config): array |
|||
185 | { |
||||
186 | 61 | if (!class_exists($class) && !interface_exists($class) || isset($config['merge']) && false === $config['merge']) { |
|||
0 ignored issues
–
show
|
|||||
187 | 5 | return array_merge($this->getServiceDefaults(), $config); |
|||
188 | } |
||||
189 | |||||
190 | 58 | $tree = $this->getConfigTree(); |
|||
191 | 58 | $parents = array_merge(class_implements($class), class_parents($class)); |
|||
192 | |||||
193 | 58 | foreach ($tree as $parent_config) { |
|||
194 | 58 | foreach ($parents as $parent) { |
|||
195 | 7 | if (isset($parent_config[$parent])) { |
|||
196 | 7 | $config = array_replace_recursive($parent_config[$parent], $config); |
|||
197 | } |
||||
198 | } |
||||
199 | |||||
200 | 58 | if (isset($parent_config[$name])) { |
|||
201 | 50 | $config = array_replace_recursive($parent_config[$name], $config); |
|||
202 | } |
||||
203 | } |
||||
204 | |||||
205 | 58 | return array_merge($this->getServiceDefaults(), $config); |
|||
206 | } |
||||
207 | |||||
208 | /** |
||||
209 | * Get config tree. |
||||
210 | */ |
||||
211 | 58 | protected function getConfigTree(): array |
|||
212 | { |
||||
213 | 58 | $tree = [$this->getConfig()]; |
|||
214 | 58 | $parent = $this->container; |
|||
215 | 58 | while ($parent = $parent->getParent()) { |
|||
216 | 2 | $tree[] = $parent->getConfig()->getConfig(); |
|||
0 ignored issues
–
show
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
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. ![]() |
|||||
217 | } |
||||
218 | |||||
219 | 58 | return $tree; |
|||
220 | } |
||||
221 | } |
||||
222 |
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: