Issues (201)

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/Api/Config/Config.php (9 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
/*
4
 * This file is part of the puli/manager package.
5
 *
6
 * (c) Bernhard Schussek <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Puli\Manager\Api\Config;
13
14
use Puli\Manager\Api\InvalidConfigException;
15
16
/**
17
 * Stores configuration values.
18
 *
19
 * Use the methods {@link get()}, {@link set()} and {@link merge()} to retrieve
20
 * and store values:
21
 *
22
 * ```php
23
 * $config = new Config();
24
 * $config->set(Config::PULI_DIR, '.puli');
25
 *
26
 * echo $config->get(Config::PULI_DIR);
27
 * // => .puli
28
 * ```
29
 *
30
 * You can customize the value returned by {@link get()} if a key is not set
31
 * by passing that value in the second parameter:
32
 *
33
 * ```php
34
 * $config = new Config();
35
 *
36
 * echo $config->get(Config::PULI_DIR, '.puli');
37
 * ```
38
 *
39
 * A configuration may also inherit default values from another configuration:
40
 *
41
 * ```php
42
 * $defaultConfig = new Config();
43
 * $defaultConfig->set(Config::PULI_DIR, '.puli');
44
 *
45
 * $config = new Config($defaultConfig);
46
 *
47
 * $config->get(Config::PULI_DIR);
48
 * // => .puli
49
 * ```
50
 *
51
 * You can disable the fallback to the default value by passing `false` to
52
 * {@link get()}:
53
 *
54
 * ```php
55
 * $defaultConfig = new Config();
56
 * $defaultConfig->set(Config::PULI_DIR, '.puli');
57
 *
58
 * $config = new Config($defaultConfig);
59
 *
60
 * $config->get(Config::PULI_DIR, null, false);
61
 * // => null
62
 * ```
63
 *
64
 * Configuration values support placeholders for other values in the format
65
 * `{$<key>}`. These placeholders will be replaced by the actual values of the
66
 * referenced keys when the values are accessed:
67
 *
68
 * ```php
69
 * $config = new Config();
70
 * $config->set(Config::PULI_DIR, '.puli');
71
 * $config->set(Config::FACTORY_FILE, '{$puli-dir}/PuliFactory.php');
72
 *
73
 * echo $config->get(Config::FACTORY_FILE);
74
 * // => .puli/PuliRegistry.php
75
 * ```
76
 *
77
 * @since  1.0
78
 *
79
 * @author Bernhard Schussek <[email protected]>
80
 */
81
class Config
82
{
83
    const PULI_DIR = 'puli-dir';
84
85
    const BOOTSTRAP_FILE = 'bootstrap-file';
86
87
    const FACTORY = 'factory';
88
89
    const FACTORY_AUTO_GENERATE = 'factory.auto-generate';
90
91
    const FACTORY_IN = 'factory.in';
92
93
    const FACTORY_IN_CLASS = 'factory.in.class';
94
95
    const FACTORY_IN_FILE = 'factory.in.file';
96
97
    const FACTORY_OUT = 'factory.out';
98
99
    const FACTORY_OUT_CLASS = 'factory.out.class';
100
101
    const FACTORY_OUT_FILE = 'factory.out.file';
102
103
    const REPOSITORY = 'repository';
104
105
    const REPOSITORY_TYPE = 'repository.type';
106
107
    const REPOSITORY_PATH = 'repository.path';
108
109
    const REPOSITORY_SYMLINK = 'repository.symlink';
110
111
    const REPOSITORY_OPTIMIZE = 'repository.optimize';
112
113
    const REPOSITORY_STORE = 'repository.store';
114
115
    const REPOSITORY_STORE_TYPE = 'repository.store.type';
116
117
    const REPOSITORY_STORE_PATH = 'repository.store.path';
118
119
    const REPOSITORY_STORE_HOST = 'repository.store.host';
120
121
    const REPOSITORY_STORE_PORT = 'repository.store.port';
122
123
    const REPOSITORY_STORE_BUCKET = 'repository.store.bucket';
124
125
    const REPOSITORY_STORE_CACHE = 'repository.store.cache';
126
127
    const CHANGE_STREAM = 'change-stream';
128
129
    const CHANGE_STREAM_TYPE = 'change-stream.type';
130
131
    const CHANGE_STREAM_PATH = 'change-stream.path';
132
133
    const CHANGE_STREAM_STORE = 'change-stream.store';
134
135
    const CHANGE_STREAM_STORE_TYPE = 'change-stream.store.type';
136
137
    const CHANGE_STREAM_STORE_PATH = 'change-stream.store.path';
138
139
    const CHANGE_STREAM_STORE_HOST = 'change-stream.store.host';
140
141
    const CHANGE_STREAM_STORE_PORT = 'change-stream.store.port';
142
143
    const CHANGE_STREAM_STORE_BUCKET = 'change-stream.store.bucket';
144
145
    const CHANGE_STREAM_STORE_CACHE = 'change-stream.store.cache';
146
147
    const DISCOVERY = 'discovery';
148
149
    const DISCOVERY_TYPE = 'discovery.type';
150
151
    const DISCOVERY_PATH = 'discovery.path';
152
153
    const DISCOVERY_STORE = 'discovery.store';
154
155
    const DISCOVERY_STORE_TYPE = 'discovery.store.type';
156
157
    const DISCOVERY_STORE_PATH = 'discovery.store.path';
158
159
    const DISCOVERY_STORE_HOST = 'discovery.store.host';
160
161
    const DISCOVERY_STORE_PORT = 'discovery.store.port';
162
163
    const DISCOVERY_STORE_BUCKET = 'discovery.store.bucket';
164
165
    const DISCOVERY_STORE_CACHE = 'discovery.store.cache';
166
167
    /**
168
     * The accepted config keys.
169
     *
170
     * @var bool[]
171
     */
172
    private static $keys = array(
173
        self::PULI_DIR => true,
174
        self::BOOTSTRAP_FILE => true,
175
        self::FACTORY_AUTO_GENERATE => true,
176
        self::FACTORY_IN_CLASS => true,
177
        self::FACTORY_IN_FILE => true,
178
        self::FACTORY_OUT_CLASS => true,
179
        self::FACTORY_OUT_FILE => true,
180
        self::REPOSITORY_TYPE => true,
181
        self::REPOSITORY_PATH => true,
182
        self::REPOSITORY_SYMLINK => true,
183
        self::REPOSITORY_OPTIMIZE => true,
184
        self::REPOSITORY_STORE_TYPE => true,
185
        self::REPOSITORY_STORE_PATH => true,
186
        self::REPOSITORY_STORE_HOST => true,
187
        self::REPOSITORY_STORE_PORT => true,
188
        self::REPOSITORY_STORE_BUCKET => true,
189
        self::REPOSITORY_STORE_CACHE => true,
190
        self::CHANGE_STREAM_TYPE => true,
191
        self::CHANGE_STREAM_PATH => true,
192
        self::CHANGE_STREAM_STORE_TYPE => true,
193
        self::CHANGE_STREAM_STORE_PATH => true,
194
        self::CHANGE_STREAM_STORE_HOST => true,
195
        self::CHANGE_STREAM_STORE_PORT => true,
196
        self::CHANGE_STREAM_STORE_BUCKET => true,
197
        self::CHANGE_STREAM_STORE_CACHE => true,
198
        self::DISCOVERY_TYPE => true,
199
        self::DISCOVERY_PATH => true,
200
        self::DISCOVERY_STORE_TYPE => true,
201
        self::DISCOVERY_STORE_PATH => true,
202
        self::DISCOVERY_STORE_HOST => true,
203
        self::DISCOVERY_STORE_PORT => true,
204
        self::DISCOVERY_STORE_BUCKET => true,
205
        self::DISCOVERY_STORE_CACHE => true,
206
    );
207
208
    private static $compositeKeys = array(
209
        self::FACTORY => true,
210
        self::FACTORY_IN => true,
211
        self::FACTORY_OUT => true,
212
        self::REPOSITORY => true,
213
        self::REPOSITORY_STORE => true,
214
        self::CHANGE_STREAM => true,
215
        self::CHANGE_STREAM_STORE => true,
216
        self::DISCOVERY => true,
217
        self::DISCOVERY_STORE => true,
218
    );
219
220
    /**
221
     * The configuration values.
222
     *
223
     * @var array
224
     */
225
    private $values = array();
226
227
    /**
228
     * The configuration to fall back to.
229
     *
230
     * @var Config
231
     */
232
    private $baseConfig;
233
234
    /**
235
     * Returns all valid configuration keys.
236
     *
237
     * @return string[] The config keys.
0 ignored issues
show
Should the return type not be integer[]?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
238
     */
239 20
    public static function getKeys()
240
    {
241 20
        return array_keys(self::$keys);
242
    }
243
244
    /**
245
     * Creates a new configuration.
246
     *
247
     * @param Config|null $baseConfig The configuration to fall back to if a value is
248
     *                                not set in here.
249
     * @param array       $values     The values to initially set in the configuration.
250
     */
251 713
    public function __construct(Config $baseConfig = null, array $values = array())
252
    {
253 713
        $this->baseConfig = $baseConfig;
254
255 713
        $this->merge($values);
256 713
    }
257
258
    /**
259
     * Returns the base configuration.
260
     *
261
     * @return Config The base configuration or `null` if none is set.
262
     */
263
    public function getBaseConfig()
264
    {
265
        return $this->baseConfig;
266
    }
267
268
    /**
269
     * Returns the value of a configuration key.
270
     *
271
     * If fallback is enabled, the value of the base configuration is returned
272
     * if the key was not set.
273
     *
274
     * You can also pass a default value in the second parameter. This default
275
     * value is returned if the configuration key was neither found in this nor
276
     * in its fallback configuration.
277
     *
278
     * @param string $key      The configuration key.
279
     * @param mixed  $default  The value to return if the key was not set.
280
     * @param bool   $fallback Whether to return the value of the base
281
     *                         configuration if the key was not set.
282
     *
283
     * @return mixed The value of the configuration key.
0 ignored issues
show
Consider making the return type a bit more specific; maybe use array|object|integer|double|null|boolean|string.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
284
     *
285
     * @throws NoSuchConfigKeyException If the configuration key is invalid.
286
     */
287 134
    public function get($key, $default = null, $fallback = true)
288
    {
289 134
        return $this->replacePlaceholders($this->getRaw($key, $default, $fallback), $fallback);
290
    }
291
292
    /**
293
     * Returns the raw value of a configuration key.
294
     *
295
     * Unlike {@link get()}, this method does not resolve placeholders:
296
     *
297
     * ```php
298
     * $config = new Config();
299
     * $config->set(Config::PULI_DIR, '.puli');
300
     * $config->set(Config::INSTALL_FILE, '{$puli-dir}/install-file.json');
301
     *
302
     * echo $config->get(Config::PULI_DIR);
303
     * // => .puli/install-file.json
304
     *
305
     * echo $config->getRaw(Config::PULI_DIR);
306
     * // => {$puli-dir}/install-file.json
307
     * ```
308
     *
309
     * @param string $key      The configuration key.
310
     * @param mixed  $default  The value to return if the key was not set.
311
     * @param bool   $fallback Whether to return the value of the base
312
     *                         configuration if the key was not set.
313
     *
314
     * @return mixed The value of the configuration key.
315
     *
316
     * @throws NoSuchConfigKeyException If the configuration key is invalid.
317
     */
318 160
    public function getRaw($key, $default = null, $fallback = true)
319
    {
320 160
        if (isset(self::$compositeKeys[$key])) {
321 47
            return array_replace_recursive(
322 47
                is_array($default) ? $default : array(),
323 47
                $fallback && $this->baseConfig ? $this->baseConfig->getRaw($key) : array(),
324 47
                $this->filterByKeyPrefix($key.'.')
325
            );
326
        }
327
328 146
        if (!isset(self::$keys[$key])) {
329 2
            throw NoSuchConfigKeyException::forKey($key);
330
        }
331
332 144
        if (!array_key_exists($key, $this->values) && $fallback && $this->baseConfig) {
333 72
            return $this->baseConfig->getRaw($key, $default);
334
        }
335
336 144
        return isset($this->values[$key]) ? $this->values[$key] : $default;
337
    }
338
339
    /**
340
     * Returns whether a configuration key is set.
341
     *
342
     * @param string $key      The configuration key to search.
343
     * @param bool   $fallback Whether to check the base configuration if the
344
     *                         key is not found.
345
     *
346
     * @return bool Returns `true` if the configuration key is set.
347
     *
348
     * @throws NoSuchConfigKeyException If the configuration key is invalid.
349
     */
350 22
    public function contains($key, $fallback = true)
351
    {
352 22
        if (!isset(self::$compositeKeys[$key]) && !isset(self::$keys[$key])) {
353 1
            throw NoSuchConfigKeyException::forKey($key);
354
        }
355
356 21
        if (array_key_exists($key, $this->values)) {
357 13
            return true;
358
        }
359
360 16
        if (isset(self::$compositeKeys[$key]) && $this->containsKeyPrefix($key.'.')) {
361 2
            return true;
362
        }
363
364 16
        if ($fallback && $this->baseConfig) {
365 12
            return $this->baseConfig->contains($key);
366
        }
367
368 16
        return false;
369
    }
370
371
    /**
372
     * Sets the value of a configuration key.
373
     *
374
     * @param string $key   The configuration key.
375
     * @param mixed  $value The value to set.
376
     *
377
     * @throws NoSuchConfigKeyException If the configuration key is invalid.
378
     * @throws InvalidConfigException   If the value is invalid.
379
     */
380 446
    public function set($key, $value)
381
    {
382 446
        if (isset(self::$compositeKeys[$key])) {
383 7
            $this->assertArray($key, $value);
384 7
            $this->removeByKeyPrefix($key.'.');
385
386 7
            foreach ($value as $k => $v) {
387 7
                $this->set($key.'.'.$k, $v);
388
            }
389
390 7
            return;
391
        }
392
393 446
        if (!isset(self::$keys[$key])) {
394 1
            throw NoSuchConfigKeyException::forKey($key);
395
        }
396
397 445
        $this->validate($key, $value);
398
399 420
        $this->values[$key] = $value;
400 420
    }
401
402
    /**
403
     * Sets a list of configuration values.
404
     *
405
     * @param array $values The values to set.
406
     *
407
     * @throws NoSuchConfigKeyException If a configuration key is invalid.
408
     * @throws InvalidConfigException   If a value is invalid.
409
     */
410 713
    public function merge(array $values)
411
    {
412 713
        foreach ($values as $key => $value) {
413 297
            $this->set($key, $value);
414
        }
415 713
    }
416
417
    /**
418
     * Replaces the configuration with a list of configuration values.
419
     *
420
     * @param array $values The values to set.
421
     *
422
     * @throws NoSuchConfigKeyException If a configuration key is invalid.
423
     * @throws InvalidConfigException   If a value is invalid.
424
     */
425 4
    public function replace(array $values)
426
    {
427 4
        $this->clear();
428 4
        $this->merge($values);
429 4
    }
430
431
    /**
432
     * Removes a configuration key.
433
     *
434
     * If the configuration has a base configuration, the default value will
435
     * be returned by {@link get()} after removing the key.
436
     *
437
     * @param string $key The configuration key to remove.
438
     *
439
     * @throws NoSuchConfigKeyException If the configuration key is invalid.
440
     */
441 13
    public function remove($key)
442
    {
443 13
        if (isset(self::$compositeKeys[$key])) {
444 1
            $this->removeByKeyPrefix($key.'.');
445
446 1
            return;
447
        }
448
449 12
        if (!isset(self::$keys[$key])) {
450 1
            throw NoSuchConfigKeyException::forKey($key);
451
        }
452
453 11
        unset($this->values[$key]);
454 11
    }
455
456
    /**
457
     * Removes all configuration keys.
458
     *
459
     * If the configuration has a base configuration, the default values will
460
     * be returned by {@link get()} after removing the keys.
461
     */
462 5
    public function clear()
463
    {
464 5
        $this->values = array();
465 5
    }
466
467
    /**
468
     * Returns all configuration values as flat array.
469
     *
470
     * @param bool $includeFallback Whether to include values set in the base
471
     *                              configuration passed to {@link __construct()}.
472
     *
473
     * @return array The configuration values.
0 ignored issues
show
Should the return type not be array|object|integer|double|null|boolean|string? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
474
     */
475 5
    public function toFlatArray($includeFallback = true)
476
    {
477 5
        return $this->replacePlaceholders($this->toFlatRawArray($includeFallback), $includeFallback);
478
    }
479
480
    /**
481
     * Returns all raw configuration values as flat array.
482
     *
483
     * Unlike {@link toFlatArray()}, this method does not resolve placeholders:
484
     *
485
     * ```php
486
     * $config = new Config();
487
     * $config->set(Config::PULI_DIR, '.puli');
488
     * $config->set(Config::REGISTRY_FILE, '{$puli-dir}/ServiceRegistry.php');
489
     *
490
     * print_r($config->toFlatArray());
491
     * // Array(
492
     * //   'puli-dir' => '.puli',
493
     * //   'registry-file' => '.puli/ServiceRegistry.php',
494
     * // )
495
     *
496
     * print_r($config->toFlatRawArray());
497
     * // Array(
498
     * //   'puli-dir' => '.puli',
499
     * //   'registry-file' => '{$puli-dir}/ServiceRegistry.php',
500
     * // )
501
     * ```
502
     *
503
     * @param bool $includeFallback Whether to include values set in the base
504
     *                              configuration passed to {@link __construct()}.
505
     *
506
     * @return array The raw configuration values.
507
     */
508 34
    public function toFlatRawArray($includeFallback = true)
509
    {
510 34
        return $includeFallback && $this->baseConfig
511 9
            ? array_replace($this->baseConfig->toFlatRawArray(), $this->values)
512 34
            : $this->values;
513
    }
514
515
    /**
516
     * Returns all configuration values as nested array.
517
     *
518
     * @param bool $includeFallback Whether to include values set in the base
519
     *                              configuration passed to {@link __construct()}.
520
     *
521
     * @return array The configuration values.
0 ignored issues
show
Should the return type not be array|object|integer|double|null|boolean|string? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
522
     */
523 3
    public function toArray($includeFallback = true)
524
    {
525 3
        return $this->replacePlaceholders($this->toRawArray($includeFallback), $includeFallback);
526
    }
527
528
    /**
529
     * Returns all raw configuration values as nested array.
530
     *
531
     * Unlike {@link toArray()}, this method does not resolve placeholders:
532
     *
533
     * ```php
534
     * $config = new Config();
535
     * $config->set(Config::PULI_DIR, '.puli');
536
     * $config->set(Config::REPO_STORAGE_DIR, '{$puli-dir}/repository');
537
     *
538
     * print_r($config->toArray());
539
     * // Array(
540
     * //     'puli-dir' => '.puli',
541
     * //     'repository. => array(
542
     * //         'storage-dir' => '.puli/repository',
543
     * //      ),
544
     * // )
545
     *
546
     * print_r($config->toRawArray());
547
     * // Array(
548
     * //     'puli-dir' => '.puli',
549
     * //     'repository. => array(
550
     * //         'storage-dir' => '{$puli-dir}/repository',
551
     * //      ),
552
     * // )
553
     * ```
554
     *
555
     * @param bool $includeFallback Whether to include values set in the base
556
     *                              configuration passed to {@link __construct()}.
557
     *
558
     * @return array The raw configuration values.
559
     */
560 14
    public function toRawArray($includeFallback = true)
561
    {
562 14
        $values = array();
563
564 14
        foreach ($this->values as $key => $value) {
565 8
            $this->addKeyValue($key, $value, $values);
566
        }
567
568 14
        return $includeFallback && $this->baseConfig
569 2
            ? array_replace_recursive($this->baseConfig->toRawArray(), $values)
570 14
            : $values;
571
    }
572
573
    /**
574
     * Returns whether the configuration is empty.
575
     *
576
     * @param bool $includeFallback Whether to include values set in the base
577
     *                              configuration passed to {@link __construct()}.
578
     *
579
     * @return bool Returns `true` if no key is set and `false` otherwise.
580
     */
581 5
    public function isEmpty($includeFallback = true)
582
    {
583 5
        if (!empty($this->values)) {
584 5
            return false;
585
        }
586
587 5
        return $includeFallback && $this->baseConfig
588 2
            ? $this->baseConfig->isEmpty(true)
589 5
            : true;
590
    }
591
592
    /**
593
     * @param string $key
594
     * @param mixed  $value
595
     */
596 445
    private function validate($key, $value)
597
    {
598
        switch ($key) {
599 445
            case self::FACTORY_AUTO_GENERATE:
600 442
            case self::REPOSITORY_SYMLINK:
601 440
            case self::REPOSITORY_OPTIMIZE:
602 438
            case self::REPOSITORY_STORE_CACHE:
603 436
            case self::CHANGE_STREAM_STORE_CACHE:
604 434
            case self::DISCOVERY_STORE_CACHE:
605 310
                $this->assertNotNull($key, $value);
606 310
                $this->assertBoolean($key, $value);
607 304
                break;
608
609 432
            case self::REPOSITORY_STORE_PORT:
610 432
            case self::CHANGE_STREAM_STORE_PORT:
611 432
            case self::DISCOVERY_STORE_PORT:
612
                $this->assertNotNull($key, $value);
613
                $this->assertInteger($key, $value);
614
                break;
615
616 432
            case self::BOOTSTRAP_FILE:
617 428
            case self::FACTORY_IN_FILE:
618 424
            case self::REPOSITORY_STORE_TYPE:
619 423
            case self::CHANGE_STREAM_STORE_TYPE:
620 422
            case self::DISCOVERY_STORE_TYPE:
621 349
                if (null !== $value) {
622 343
                    $this->assertString($key, $value);
623 341
                    $this->assertNonEmpty($key, $value);
624
                }
625 345
                break;
626
627
            default:
628 417
                $this->assertNotNull($key, $value);
629 412
                $this->assertNonEmpty($key, $value);
630 407
                $this->assertString($key, $value);
631
632 402
                break;
633
        }
634 420
    }
635
636
    /**
637
     * @param string $key
638
     * @param mixed  $value
639
     *
640
     * @throws InvalidConfigException If the config value isn't an array.
641
     */
642 7 View Code Duplication
    private function assertArray($key, $value)
0 ignored issues
show
This method seems to be duplicated in 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...
643
    {
644 7
        if (!is_array($value)) {
645
            throw new InvalidConfigException(sprintf(
646
                'The config key "%s" must be an array. Got: %s',
647
                $key,
648
                is_object($value) ? get_class($value) : gettype($value)
649
            ));
650
        }
651 7
    }
652
653
    /**
654
     * @param string $key
655
     * @param mixed  $value
656
     *
657
     * @throws InvalidConfigException If the config value is null.
658
     */
659 430
    private function assertNotNull($key, $value)
660
    {
661 430
        if (null === $value) {
662 5
            throw new InvalidConfigException(sprintf(
663
                'The config key "%s" must not be null. Use remove() to unset '.
664 5
                'keys.',
665
                $key
666
            ));
667
        }
668 425
    }
669
670
    /**
671
     * @param string $key
672
     * @param mixed  $value
673
     *
674
     * @throws InvalidConfigException If the config value isn't a string.
675
     */
676 416 View Code Duplication
    private function assertString($key, $value)
0 ignored issues
show
This method seems to be duplicated in 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...
677
    {
678 416
        if (!is_string($value) && null !== $value) {
679 7
            throw new InvalidConfigException(sprintf(
680 7
                'The config key "%s" must be a string. Got: %s',
681
                $key,
682 7
                is_object($value) ? get_class($value) : gettype($value)
683
            ));
684
        }
685 409
    }
686
687
    /**
688
     * @param string $key
689
     * @param mixed  $value
690
     *
691
     * @throws InvalidConfigException If the config value isn't an integer.
692
     */
693 View Code Duplication
    private function assertInteger($key, $value)
0 ignored issues
show
This method seems to be duplicated in 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...
694
    {
695
        if (!is_int($value) && null !== $value) {
696
            throw new InvalidConfigException(sprintf(
697
                'The config key "%s" must be an integer. Got: %s',
698
                $key,
699
                is_object($value) ? get_class($value) : gettype($value)
700
            ));
701
        }
702
    }
703
704
    /**
705
     * @param string $key
706
     * @param mixed  $value
707
     *
708
     * @throws InvalidConfigException If the config value isn't a boolean.
709
     */
710 310 View Code Duplication
    private function assertBoolean($key, $value)
0 ignored issues
show
This method seems to be duplicated in 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...
711
    {
712 310
        if (!is_bool($value) && null !== $value) {
713 6
            throw new InvalidConfigException(sprintf(
714 6
                'The config key "%s" must be a bool. Got: %s',
715
                $key,
716 6
                is_object($value) ? get_class($value) : gettype($value)
717
            ));
718
        }
719 304
    }
720
721
    /**
722
     * @param string $key
723
     * @param mixed  $value
724
     *
725
     * @throws InvalidConfigException If the config value is an empty string.
726
     */
727 419
    private function assertNonEmpty($key, $value)
728
    {
729 419
        if ('' === $value) {
730 7
            throw new InvalidConfigException(sprintf(
731 7
                'The value of the config key "%s" must not be empty.',
732
                $key
733
            ));
734
        }
735 412
    }
736
737
    /**
738
     * @param mixed $raw
739
     * @param bool  $fallback
740
     *
741
     * @return mixed
0 ignored issues
show
Consider making the return type a bit more specific; maybe use array|object|integer|double|null|boolean|string.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
742
     */
743 133
    private function replacePlaceholders($raw, $fallback = true)
744
    {
745 133
        if (is_array($raw)) {
746 47
            foreach ($raw as $key => $value) {
747 45
                $raw[$key] = $this->replacePlaceholders($value, $fallback);
748
            }
749
750 47
            return $raw;
751
        }
752
753 132
        if (!is_string($raw)) {
754 86
            return $raw;
755
        }
756
757 91
        $config = $this;
758
759 91
        return preg_replace_callback('~\{\$(.+)\}~', function ($matches) use ($config, $fallback) {
760 48
            return $config->get($matches[1], null, $fallback);
761 91
        }, $raw);
762
    }
763
764
    /**
765
     * @param string $keyPrefix
766
     *
767
     * @return array
768
     */
769 47
    private function filterByKeyPrefix($keyPrefix)
770
    {
771 47
        $values = array();
772 47
        $offset = strlen($keyPrefix);
773
774 47
        foreach ($this->values as $k => $v) {
775 45
            if (0 !== strpos($k, $keyPrefix)) {
776 32
                continue;
777
            }
778
779 44
            $this->addKeyValue(substr($k, $offset), $v, $values);
780
        }
781
782 47
        return $values;
783
    }
784
785
    /**
786
     * @param string $keyPrefix
787
     *
788
     * @return bool
789
     */
790 3
    private function containsKeyPrefix($keyPrefix)
791
    {
792 3
        foreach ($this->values as $k => $v) {
793 2
            if (0 === strpos($k, $keyPrefix)) {
794 2
                return true;
795
            }
796
        }
797
798 3
        return false;
799
    }
800
801
    /**
802
     * @param string $keyPrefix
803
     */
804 8
    private function removeByKeyPrefix($keyPrefix)
805
    {
806 8
        foreach ($this->values as $k => $v) {
807 5
            if (0 !== strpos($k, $keyPrefix)) {
808 4
                continue;
809
            }
810
811 2
            unset($this->values[$k]);
812
        }
813 8
    }
814
815
    /**
816
     * @param string $key
817
     * @param mixed  $value
818
     * @param array  $values
819
     */
820 52
    private function addKeyValue($key, $value, array &$values)
821
    {
822 52
        $target = &$values;
823 52
        $keyParts = explode('.', $key);
824
825 52
        for ($i = 0, $l = count($keyParts) - 1; $i < $l; ++$i) {
826 40
            if (!isset($target[$keyParts[$i]])) {
827 40
                $target[$keyParts[$i]] = array();
828
            }
829
830 40
            $target = &$target[$keyParts[$i]];
831
        }
832
833 52
        $target[$keyParts[$l]] = $value;
834 52
    }
835
}
836