Passed
Push — master ( 3415df...79c576 )
by Alain
02:57
created

AbstractConfig::reduceToSubkey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * Abstract Config Object
4
 *
5
 * @package   BrightNucleus\Config
6
 * @author    Alain Schlesser <[email protected]>
7
 * @license   GPL-2.0+
8
 * @link      http://www.brightnucleus.com/
9
 * @copyright 2016 Alain Schlesser, Bright Nucleus
10
 */
11
12
namespace BrightNucleus\Config;
13
14
use ArrayObject;
15
use Assert;
16
use BrightNucleus\Exception\BadMethodCallException;
17
use BrightNucleus\Exception\OutOfRangeException;
18
use Exception;
19
20
/**
21
 * Config loader used to load config PHP files as objects.
22
 *
23
 * @since      0.1.0
24
 *
25
 * @package    BrightNucleus\Config
26
 * @author     Alain Schlesser <[email protected]>
27
 */
28
abstract class AbstractConfig extends ArrayObject implements ConfigInterface
29
{
30
31
    /**
32
     * Array of strings that are used as delimiters to parse configuration keys.
33
     *
34
     * @since 0.1.6
35
     *
36
     * @var array
37
     */
38
    protected $delimiter = ['\\', '/', '.'];
39
40
    /**
41
     * Instantiate the AbstractConfig object.
42
     *
43
     * @since 0.1.0
44
     * @since 0.1.6 Accepts a delimiter to parse configuration keys.
45
     *
46
     * @param array                $config    Array with settings.
47
     * @param string[]|string|null $delimiter A string or array of strings that are used as delimiters to parse
48
     *                                        configuration keys. Defaults to "\", "/" & ".".
49
     */
50 3
    public function __construct(array $config, $delimiter = null)
51
    {
52
        // Make sure the config entries can be accessed as properties.
53 3
        parent::__construct($config, ArrayObject::ARRAY_AS_PROPS);
54
55 3
        if (null !== $delimiter) {
56 1
            $this->delimiter = (array)$delimiter;
57
        }
58 3
    }
59
60
    /**
61
     * Get the value of a specific key.
62
     *
63
     * To get a value several levels deep, add the keys for each level as a comma-separated list.
64
     *
65
     * @since 0.1.0
66
     * @since 0.1.4 Accepts list of keys.
67
     *
68
     * @param string ... List of keys.
69
     * @return mixed
70
     * @throws BadMethodCallException If no argument was provided.
71
     * @throws OutOfRangeException If an unknown key is requested.
72
     */
73 3
    public function getKey()
74
    {
75 3
        $keys = $this->validateKeys(func_get_args());
76
77 3
        $keys  = array_reverse($keys);
78 3
        $array = $this->getArrayCopy();
79 3
        while (count($keys) > 0) {
80 3
            $key   = array_pop($keys);
81 3
            $array = $array[$key];
82
        }
83
84 3
        return $array;
85
    }
86
87
    /**
88
     * Check whether the Config has a specific key.
89
     *
90
     * To check a value several levels deep, add the keys for each level as a comma-separated list.
91
     *
92
     * @since 0.1.0
93
     * @since 0.1.4 Accepts list of keys.
94
     *
95
     * @param string ... List of keys.
96
     * @return bool
97
     */
98 3
    public function hasKey()
99
    {
100
        try {
101 3
            $keys = array_reverse($this->getKeyArguments(func_get_args()));
102
103 3
            $array = $this->getArrayCopy();
104 3
            while (count($keys) > 0) {
105 3
                $key = array_pop($keys);
106 3
                if (! array_key_exists($key, $array)) {
107 3
                    return false;
108
                }
109 3
                $array = $array[$key];
110
            }
111 1
        } catch (Exception $exception) {
112 1
            return false;
113
        }
114
115 3
        return true;
116
    }
117
118
    /**
119
     * Get a (multi-dimensional) array of all the configuration settings.
120
     *
121
     * @since 0.1.4
122
     *
123
     * @return array
124
     */
125 1
    public function getAll()
126
    {
127 1
        return $this->getArrayCopy();
128
    }
129
130
    /**
131
     * Get the an array with all the keys
132
     *
133
     * @since 0.1.0
134
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<integer|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...
135
     */
136 1
    public function getKeys()
137
    {
138 1
        return array_keys((array)$this);
139
    }
140
141
    /**
142
     * Get a new config at a specific sub-level.
143
     *
144
     * @since 0.1.13
145
     *
146
     * @param string ... List of keys.
147
     * @return ConfigInterface
1 ignored issue
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use AbstractConfig.

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...
148
     * @throws BadMethodCallException If no argument was provided.
149
     * @throws OutOfRangeException If an unknown key is requested.
150
     */
151 1
    public function getSubConfig()
152
    {
153 1
        $keys = $this->validateKeys(func_get_args());
154
155 1
        $subConfig = clone $this;
156 1
        $subConfig->reduceToSubKey($keys);
157
158 1
        return $subConfig;
159
    }
160
161
    /**
162
     * Validate a set of keys to make sure they exist.
163
     *
164
     * @since 0.1.13
165
     *
166
     * @param string ... List of keys.
167
     * @return array List of keys.
168
     * @throws BadMethodCallException If no argument was provided.
169
     * @throws OutOfRangeException If an unknown key is requested.
170
     */
171 1
    public function validateKeys()
172
    {
173 1
        $keys = $this->getKeyArguments(func_get_args());
174
175 1
        Assert\that($keys)->all()->string()->notEmpty();
176
177 1
        if (! $this->hasKey($keys)) {
178 1
            throw new OutOfRangeException(
179
                sprintf(
180 1
                    _('The configuration key %1$s does not exist.'),
181 1
                    implode('->', $keys)
182
                )
183
            );
184
        }
185
186 1
        return $keys;
187
    }
188
189
    /**
190
     * Reduce the currently stored config array to a subarray at a specific level.
191
     *
192
     * @since 0.1.13
193
     *
194
     * @param array $keys Array of keys that point to a key down in the hierarchy.
195
     */
196 1
    protected function reduceToSubkey(array $keys)
197
    {
198 1
        $this->exchangeArray($this->getKey($keys));
199 1
    }
200
201
    /**
202
     * Recursively extract the configuration key arguments from an arbitrary array.
203
     *
204
     * @since 0.1.6
205
     *
206
     * @param array $arguments Array as fetched through get_func_args().
207
     * @return array Array of strings.
208
     * @throws BadMethodCallException If no argument was provided.
209
     */
210 5
    protected function getKeyArguments($arguments)
211
    {
212 5
        Assert\that($arguments)->isArray()->notEmpty();
213
214 5
        $keys = [];
215 5
        foreach ($arguments as $argument) {
216 5
            if (is_array($argument)) {
217 5
                $keys = array_merge($keys, $this->getKeyArguments($argument));
218
            }
219 4
            if (is_string($argument)) {
220 4
                $keys = array_merge($keys, $this->parseKeysString($argument));
221
            }
222
        }
223
224 4
        return $keys;
225
    }
226
227
    /**
228
     * Extract individual keys from a delimited string.
229
     *
230
     * @since 0.1.6
231
     *
232
     * @param string $keyString Delimited string of keys.
233
     * @return array Array of key strings.
234
     */
235 2
    protected function parseKeysString($keyString)
236
    {
237 2
        Assert\that($keyString)->string()->notEmpty();
238
239
        // Replace all of the configured delimiters by the first one, so that we can then use explode().
240 2
        $normalizedString = str_replace($this->delimiter, $this->delimiter[0], $keyString);
241
242 2
        return (array)explode($this->delimiter[0], $normalizedString);
243
    }
244
245
    /**
246
     * Validate the Config file.
247
     *
248
     * @since  0.1.0
249
     * @return boolean
250
     */
251
    abstract public function isValid();
252
}
253