Completed
Pull Request — master (#66)
by T
04:57
created

AbstractConfig::has()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 4

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 22
ccs 4
cts 4
cp 1
rs 8.9197
cc 4
eloc 12
nc 4
nop 1
crap 4
1
<?php
2
3
namespace Noodlehaus;
4
5
use ArrayAccess;
6
use Iterator;
7
8
/**
9
 * Abstract Config class
10
 *
11
 * @package    Config
12
 * @author     Jesus A. Domingo <[email protected]>
13
 * @author     Hassan Khan <[email protected]>
14
 * @link       https://github.com/noodlehaus/config
15
 * @license    MIT
16
 */
17
abstract class AbstractConfig implements ArrayAccess, ConfigInterface, Iterator
18
{
19
    /**
20
     * Stores the configuration data
21
     *
22
     * @var array|null
23
     */
24
    protected $data = null;
25
26
    /**
27
     * Caches the configuration data
28
     *
29
     * @var array
30
     */
31
    protected $cache = array();
32
33
    /**
34
     * Constructor method and sets default options, if any
35
     *
36
     * @param array $data
37
     */
38 3
    public function __construct(Array $data)
39
    {
40 3
        $this->data = array_merge($this->getDefaults(), $data);
41 3
    }
42
43
    /**
44
     * Override this method in your own subclass to provide an array of default
45
     * options and values
46
     *
47
     * @return array
48
     *
49
     * @codeCoverageIgnore
50
     */
51
    protected function getDefaults()
52
    {
53
        return array();
54
    }
55
56
    /**
57
     * ConfigInterface Methods
58
     */
59
60
    /**
61
     * {@inheritDoc}
62
     */
63 24
    public function get($key, $default = null)
64
    {
65
        // Check if already cached
66 24
        if (isset($this->cache[$key])) {
67 3
            return $this->cache[$key];
68
        }
69
70 24
        $segs = explode('.', $key);
71 24
        $root = $this->data;
72
73
        // nested case
74 24
        foreach ($segs as $part) {
75 24
            if (isset($root[$part])) {
76 15
                $root = $root[$part];
77 15
                continue;
78
            } else {
79 12
                $root = $default;
80 12
                break;
81
            }
82 24
        }
83
84
        // whatever we have is what we needed
85 24
        return ($this->cache[$key] = $root);
86
    }
87
88
    /**
89
     * {@inheritDoc}
90
     */
91 18
    public function set($key, $value)
92
    {
93 18
        $segs = explode('.', $key);
94 18
        $root = &$this->data;
95 18
        $cacheKey = '';
96
97
        // Look for the key, creating nested keys if needed
98 18
        while ($part = array_shift($segs)) {
99 18
            if($cacheKey != ''){
100 12
                $cacheKey .= '.';
101 12
            }
102 18
            $cacheKey .= $part;
103 18
            if (!isset($root[$part]) && count($segs)) {
104 3
                $root[$part] = array();
105 3
            }
106 18
            $root = &$root[$part];
107
108
            //Unset all old nested cache
109 18
            if(isset($this->cache[$cacheKey])){
110 9
                unset($this->cache[$cacheKey]);
111 9
            }
112
113
            //Unset all old nested cache in case of array
114 18
            if(count($segs) == 0){
115 18
                foreach ($this->cache as $cacheLocalKey => $cacheValue) {
116 9
                    if(substr($cacheLocalKey, 0, strlen($cacheKey)) === $cacheKey){
117 6
                        unset($this->cache[$cacheLocalKey]);
118 6
                    }
119 18
                }
120 18
            }
121 18
        }
122
123
        // Assign value at target node
124 18
        $this->cache[$key] = $root = $value;
125 18
    }
126
127
    /**
128
     * {@inheritDoc}
129
     */
130 6
    public function has($key)
131
    {
132 6
        // Check if already cached
133
        if (isset($this->cache[$key])) {
134
            return true;
135
        }
136
137
        $segments = explode('.', $key);
138 3
        $root = $this->data;
139
140 3
        // nested case
141
        foreach ($segments as $segment) {
142
            if (array_key_exists($segment, $root)) {
143
                $root = $root[$segment];
144
                continue;
145
            } else {
146
                return false;
147
            }
148
        }
149
150
        return true;
151
    }
152
153
    /**
154
     * {@inheritDoc}
155
     */
156 6
    public function all()
157
    {
158 6
        return $this->data;
159
    }
160
161
    /**
162
     * ArrayAccess Methods
163
     */
164
165
    /**
166
     * Gets a value using the offset as a key
167
     *
168 6
     * @param  string $offset
169
     *
170 6
     * @return mixed
171
     */
172
    public function offsetGet($offset)
173
    {
174
        return $this->get($offset);
175
    }
176
177
    /**
178
     * Checks if a key exists
179
     *
180
     * @param  string $offset
181 3
     *
182
     * @return bool
183 3
     */
184 3
    public function offsetExists($offset)
185
    {
186
        return $this->has($offset);
187
    }
188
189
    /**
190
     * Sets a value using the offset as a key
191
     *
192
     * @param  string $offset
193 3
     * @param  mixed  $value
194
     *
195 3
     * @return void
196 3
     */
197
    public function offsetSet($offset, $value)
198
    {
199
        $this->set($offset, $value);
200
    }
201
202
    /**
203
     * Deletes a key and its value
204
     *
205
     * @param  string $offset
206
     *
207
     * @return void
208
     */
209
    public function offsetUnset($offset)
210 3
    {
211
        $this->set($offset, null);
212 3
    }
213
214
    /**
215
     * Iterator Methods
216
     */
217
218
    /**
219
     * Returns the data array element referenced by its internal cursor
220
     *
221
     * @return mixed The element referenced by the data array's internal cursor.
222 3
     *     If the array is empty or there is no element at the cursor, the
223
     *     function returns false. If the array is undefined, the function
224 3
     *     returns null
225
     */
226
    public function current()
227
    {
228
        return (is_array($this->data) ? current($this->data) : null);
229
    }
230
231
    /**
232
     * Returns the data array index referenced by its internal cursor
233
     *
234
     * @return mixed The index referenced by the data array's internal cursor.
235 3
     *     If the array is empty or undefined or there is no element at the
236
     *     cursor, the function returns null
237 3
     */
238
    public function key()
239
    {
240
        return (is_array($this->data) ? key($this->data) : null);
241
    }
242
243
    /**
244
     * Moves the data array's internal cursor forward one element
245
     *
246
     * @return mixed The element referenced by the data array's internal cursor
247
     *     after the move is completed. If there are no more elements in the
248 3
     *     array after the move, the function returns false. If the data array
249
     *     is undefined, the function returns null
250 3
     */
251
    public function next()
252
    {
253
        return (is_array($this->data) ? next($this->data) : null);
254
    }
255
256
    /**
257
     * Moves the data array's internal cursor to the first element
258 3
     *
259
     * @return mixed The element referenced by the data array's internal cursor
260 3
     *     after the move is completed. If the data array is empty, the function
261
     *     returns false. If the data array is undefined, the function returns
262
     *     null
263
     */
264
    public function rewind()
265
    {
266
        return (is_array($this->data) ? reset($this->data) : null);
267
    }
268
269
    /**
270
     * Tests whether the iterator's current index is valid
271
     *
272
     * @return bool True if the current index is valid; false otherwise
273
     */
274
    public function valid()
275
    {
276
        return (is_array($this->data) ? key($this->data) !== null : false);
277
    }
278
}
279