Issues (11)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Charcoal/Config/AbstractConfig.php (4 issues)

Upgrade to new PHP Analysis Engine

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
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.

Loading history...
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.

Loading history...
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.

Loading history...
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.

Loading history...
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