This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Charcoal\Config; |
||
4 | |||
5 | use ArrayIterator; |
||
6 | use IteratorAggregate; |
||
7 | use Traversable; |
||
8 | use InvalidArgumentException; |
||
9 | |||
10 | // From PSR-11 |
||
11 | use Psr\Container\ContainerInterface; |
||
12 | |||
13 | /** |
||
14 | * Default configuration container / registry. |
||
15 | * |
||
16 | * ### Notes on {@see SeparatorAwareTrait}: |
||
17 | * |
||
18 | * - Provides the ability for a store to fetch data that is nested in a tree-like structure, |
||
19 | * often referred to as "dot" notation. |
||
20 | * |
||
21 | * ### Notes on {@see DelegatesAwareTrait}: |
||
22 | * |
||
23 | * - Provides the ability for a store to fetch data in another store. |
||
24 | * - Provides this store with a way to register one or more delegate stores. |
||
25 | */ |
||
26 | abstract class AbstractConfig extends AbstractEntity implements |
||
27 | ConfigInterface, |
||
28 | ContainerInterface, |
||
29 | IteratorAggregate |
||
30 | { |
||
31 | use DelegatesAwareTrait; |
||
32 | use FileAwareTrait; |
||
33 | use SeparatorAwareTrait; |
||
34 | |||
35 | const DEFAULT_SEPARATOR = '.'; |
||
36 | |||
37 | /** |
||
38 | * Create the configuration. |
||
39 | * |
||
40 | * @param mixed $data Initial data. Either a filepath, |
||
41 | * an associative array, or an {@see Traversable iterable object}. |
||
42 | * @param EntityInterface[] $delegates An array of delegates (config) to set. |
||
43 | * @throws InvalidArgumentException If $data is invalid. |
||
44 | */ |
||
45 | final public function __construct($data = null, array $delegates = null) |
||
46 | { |
||
47 | // Always set the default chaining notation |
||
48 | $this->setSeparator(self::DEFAULT_SEPARATOR); |
||
49 | |||
50 | // Always set the default data first. |
||
51 | $this->setData($this->defaults()); |
||
52 | |||
53 | // Set the delegates, if necessary. |
||
54 | if (isset($delegates)) { |
||
55 | $this->setDelegates($delegates); |
||
56 | } |
||
57 | |||
58 | if ($data === null) { |
||
59 | return; |
||
60 | } |
||
61 | |||
62 | if (is_string($data)) { |
||
63 | // Treat the parameter as a filepath |
||
64 | $this->addFile($data); |
||
65 | } elseif (is_array($data)) { |
||
66 | $this->merge($data); |
||
67 | } elseif ($data instanceof Traversable) { |
||
68 | $this->merge($data); |
||
69 | } else { |
||
70 | throw new InvalidArgumentException(sprintf( |
||
71 | 'Data must be a config file, an associative array, or an object implementing %s', |
||
72 | Traversable::class |
||
73 | )); |
||
74 | } |
||
75 | } |
||
76 | |||
77 | /** |
||
78 | * Gets all default data from this store. |
||
79 | * |
||
80 | * Pre-populates new stores. |
||
81 | * |
||
82 | * May be reimplemented in inherited classes if any default values should be defined. |
||
83 | * |
||
84 | * @return array Key-value array of data |
||
85 | */ |
||
86 | public function defaults() |
||
87 | { |
||
88 | return []; |
||
89 | } |
||
90 | |||
91 | /** |
||
92 | * Adds new data, replacing / merging existing data with the same key. |
||
93 | * |
||
94 | * @uses self::offsetReplace() |
||
95 | * @param array|Traversable $data Key-value dataset to merge. |
||
96 | * Either an associative array or an {@see Traversable iterable object} |
||
97 | * (such as {@see ConfigInterface}). |
||
98 | * @return self |
||
99 | */ |
||
100 | public function merge($data) |
||
101 | { |
||
102 | foreach ($data as $key => $value) { |
||
103 | $this->offsetReplace($key, $value); |
||
104 | } |
||
105 | return $this; |
||
106 | } |
||
107 | |||
108 | /** |
||
109 | * Create a new iterator from the configuration instance. |
||
110 | * |
||
111 | * @see IteratorAggregate |
||
112 | * @return ArrayIterator |
||
113 | */ |
||
114 | public function getIterator() |
||
115 | { |
||
116 | return new ArrayIterator($this->data()); |
||
117 | } |
||
118 | |||
119 | /** |
||
120 | * Determines if this store contains the specified key and if its value is not NULL. |
||
121 | * |
||
122 | * Routine: |
||
123 | * - If the data key is {@see SeparatorAwareTrait::$separator nested}, |
||
124 | * the data-tree is traversed until the endpoint is found, if any; |
||
125 | * - If the data key does NOT exist on the store, a lookup is performed |
||
126 | * on each delegate store until a key is found, if any. |
||
127 | * |
||
128 | * @see \ArrayAccess |
||
129 | * @uses SeparatorAwareTrait::hasWithSeparator() |
||
130 | * @uses DelegatesAwareTrait::hasInDelegates() |
||
131 | * @param string $key The data key to check. |
||
132 | * @throws InvalidArgumentException If the $key is not a string or is a numeric value. |
||
133 | * @return boolean TRUE if $key exists and has a value other than NULL, FALSE otherwise. |
||
134 | */ |
||
135 | public function offsetExists($key) |
||
136 | { |
||
137 | if (is_numeric($key)) { |
||
138 | throw new InvalidArgumentException( |
||
139 | 'Entity array access only supports non-numeric keys' |
||
140 | ); |
||
141 | } |
||
142 | |||
143 | if ($this->separator && strstr($key, $this->separator)) { |
||
144 | return $this->hasWithSeparator($key); |
||
145 | } |
||
146 | |||
147 | $key = $this->camelize($key); |
||
148 | |||
149 | /** @internal Edge Case: "_" → "" */ |
||
150 | if ($key === '') { |
||
151 | return false; |
||
152 | } |
||
153 | |||
154 | $getter = 'get'.ucfirst($key); |
||
155 | if (!isset($this->mutatorCache[$getter])) { |
||
156 | $this->mutatorCache[$getter] = is_callable([ $this, $getter ]); |
||
157 | } |
||
158 | |||
159 | if ($this->mutatorCache[$getter]) { |
||
160 | return ($this->{$getter}() !== null); |
||
161 | } |
||
162 | |||
163 | // -- START DEPRECATED |
||
164 | View Code Duplication | if (!isset($this->mutatorCache[$key])) { |
|
0 ignored issues
–
show
|
|||
165 | $this->mutatorCache[$key] = is_callable([ $this, $key ]); |
||
166 | } |
||
167 | |||
168 | if ($this->mutatorCache[$key]) { |
||
169 | return ($this->{$key}() !== null); |
||
170 | } |
||
171 | // -- END DEPRECATED |
||
172 | |||
173 | if (isset($this->{$key})) { |
||
174 | return true; |
||
175 | } |
||
176 | |||
177 | return $this->hasInDelegates($key); |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * Returns the value from the specified key on this entity. |
||
182 | * |
||
183 | * Routine: |
||
184 | * - If the data key is {@see SeparatorAwareTrait::$separator nested}, |
||
185 | * the data-tree is traversed until the endpoint to return its value, if any; |
||
186 | * - If the data key does NOT exist on the store, a lookup is performed |
||
187 | * on each delegate store until a value is found, if any. |
||
188 | * |
||
189 | * @see \ArrayAccess |
||
190 | * @uses SeparatorAwareTrait::getWithSeparator() |
||
191 | * @uses DelegatesAwareTrait::getInDelegates() |
||
192 | * @param string $key The data key to retrieve. |
||
193 | * @throws InvalidArgumentException If the $key is not a string or is a numeric value. |
||
194 | * @return mixed Value of the requested $key on success, NULL if the $key is not set. |
||
195 | */ |
||
196 | public function offsetGet($key) |
||
197 | { |
||
198 | if (is_numeric($key)) { |
||
199 | throw new InvalidArgumentException( |
||
200 | 'Entity array access only supports non-numeric keys' |
||
201 | ); |
||
202 | } |
||
203 | |||
204 | if ($this->separator && strstr($key, $this->separator)) { |
||
205 | return $this->getWithSeparator($key); |
||
206 | } |
||
207 | |||
208 | $key = $this->camelize($key); |
||
209 | |||
210 | /** @internal Edge Case: "_" → "" */ |
||
211 | if ($key === '') { |
||
212 | return null; |
||
213 | } |
||
214 | |||
215 | $getter = 'get'.ucfirst($key); |
||
216 | if (!isset($this->mutatorCache[$getter])) { |
||
217 | $this->mutatorCache[$getter] = is_callable([ $this, $getter ]); |
||
218 | } |
||
219 | |||
220 | if ($this->mutatorCache[$getter]) { |
||
221 | return $this->{$getter}(); |
||
222 | } |
||
223 | |||
224 | // -- START DEPRECATED |
||
225 | View Code Duplication | if (!isset($this->mutatorCache[$key])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
226 | $this->mutatorCache[$key] = is_callable([ $this, $key ]); |
||
227 | } |
||
228 | |||
229 | if ($this->mutatorCache[$key]) { |
||
230 | return $this->{$key}(); |
||
231 | } |
||
232 | // -- END DEPRECATED |
||
233 | |||
234 | if (isset($this->{$key})) { |
||
235 | return $this->{$key}; |
||
236 | } |
||
237 | |||
238 | return $this->getInDelegates($key); |
||
239 | } |
||
240 | |||
241 | /** |
||
242 | * Assigns the value to the specified key on this entity. |
||
243 | * |
||
244 | * Routine: |
||
245 | * - If the data key is {@see SeparatorAwareTrait::$separator nested}, |
||
246 | * the data-tree is traversed until the endpoint to assign its value; |
||
247 | * |
||
248 | * @see \ArrayAccess |
||
249 | * @uses SeparatorAwareTrait::setWithSeparator() |
||
250 | * @param string $key The data key to assign $value to. |
||
251 | * @param mixed $value The data value to assign to $key. |
||
252 | * @throws InvalidArgumentException If the $key is not a string or is a numeric value. |
||
253 | * @return void |
||
254 | */ |
||
255 | public function offsetSet($key, $value) |
||
256 | { |
||
257 | if (is_numeric($key)) { |
||
258 | throw new InvalidArgumentException( |
||
259 | 'Entity array access only supports non-numeric keys' |
||
260 | ); |
||
261 | } |
||
262 | |||
263 | if ($this->separator && strstr($key, $this->separator)) { |
||
264 | $this->setWithSeparator($key, $value); |
||
265 | return; |
||
266 | } |
||
267 | |||
268 | $key = $this->camelize($key); |
||
269 | |||
270 | /** @internal Edge Case: "_" → "" */ |
||
271 | if ($key === '') { |
||
272 | return; |
||
273 | } |
||
274 | |||
275 | $setter = 'set'.ucfirst($key); |
||
276 | View Code Duplication | if (!isset($this->mutatorCache[$setter])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
277 | $this->mutatorCache[$setter] = is_callable([ $this, $setter ]); |
||
278 | } |
||
279 | |||
280 | if ($this->mutatorCache[$setter]) { |
||
281 | $this->{$setter}($value); |
||
282 | } else { |
||
283 | $this->{$key} = $value; |
||
284 | } |
||
285 | |||
286 | $this->keyCache[$key] = true; |
||
287 | } |
||
288 | |||
289 | /** |
||
290 | * Replaces the value from the specified key. |
||
291 | * |
||
292 | * Routine: |
||
293 | * - When the value in the Config and the new value are both arrays, |
||
294 | * the method will replace their respective value recursively. |
||
295 | * - Then or otherwise, the new value is {@see self::offsetSet() assigned} to the Config. |
||
296 | * |
||
297 | * @uses self::offsetSet() |
||
298 | * @uses array_replace_recursive() |
||
299 | * @param string $key The data key to assign or merge $value to. |
||
300 | * @param mixed $value The data value to assign to or merge with $key. |
||
301 | * @throws InvalidArgumentException If the $key is not a string or is a numeric value. |
||
302 | * @return void |
||
303 | */ |
||
304 | public function offsetReplace($key, $value) |
||
305 | { |
||
306 | if (is_numeric($key)) { |
||
307 | throw new InvalidArgumentException( |
||
308 | 'Entity array access only supports non-numeric keys' |
||
309 | ); |
||
310 | } |
||
311 | |||
312 | $key = $this->camelize($key); |
||
313 | |||
314 | /** @internal Edge Case: "_" → "" */ |
||
315 | if ($key === '') { |
||
316 | return; |
||
317 | } |
||
318 | |||
319 | View Code Duplication | if (is_array($value) && isset($this[$key])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
320 | $data = $this[$key]; |
||
321 | if (is_array($data)) { |
||
322 | $value = array_replace_recursive($data, $value); |
||
323 | } |
||
324 | } |
||
325 | |||
326 | $this[$key] = $value; |
||
327 | } |
||
328 | |||
329 | /** |
||
330 | * Adds a configuration file to the configset. |
||
331 | * |
||
332 | * Natively supported file formats: INI, JSON, PHP. |
||
333 | * |
||
334 | * @uses FileAwareTrait::loadFile() |
||
335 | * @param string $path The file to load and add. |
||
336 | * @return self |
||
337 | */ |
||
338 | public function addFile($path) |
||
339 | { |
||
340 | $config = $this->loadFile($path); |
||
341 | if (is_array($config)) { |
||
342 | $this->merge($config); |
||
343 | } |
||
344 | return $this; |
||
345 | } |
||
346 | } |
||
347 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.