Completed
Push — master ( 0c83ff...fee93b )
by Xeriab
04:37
created

AbstractKonfig::offsetSet()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 2
1
<?php
2
/**
3
 * Konfig
4
 *
5
 * Yet another simple configuration file loader library.
6
 *
7
 * @author  Xeriab Nabil (aka KodeBurner) <[email protected]>
8
 * @license https://raw.github.com/xeriab/konfig/master/LICENSE MIT
9
 * @link    https://xeriab.github.io/projects/konfig
10
 */
11
namespace Exen\Konfig;
12
13
use ArrayAccess;
14
use Exen\Konfig\Utils;
15
use Iterator;
16
17
abstract class AbstractKonfig implements ArrayAccess, Iterator, KonfigInterface
18
{
19
    /**
20
     * Stores the configuration items
21
     *
22
     * @var array|null
23
     * @since 0.1
24
     */
25
    protected $data = null;
26
27
    /**
28
     * Caches the configuration data
29
     *
30
     * @var array
31
     * @since 0.1
32
     */
33
    protected $cache = [];
34
35
    /**
36
     * @var string $defaultCheckValue Random value used as a not-found check in get()
37
     * @since 0.1
38
     */
39
    static $defaultCheckValue;
40
41
    /**
42
     * Constructor method and sets default options, if any
43
     *
44
     * @param array $input
45
     * @since 0.1
46
     */
47
    public function __construct($input)
48
    {
49
        // $this->data = array_merge($this->getDefaults(), $input);
50
        $this->data = Utils::arrayMerge($this->getDefaults(), $input);
51
    }
52
53
    /**
54
     * Override this method in your own subclass to provide an array of default
55
     * options and values
56
     *
57
     * @codeCoverageIgnore
58
     * @return array
59
     * @since 0.1
60
     */
61
    protected function getDefaults()
62
    {
63
        return [];
64
    }
65
66
    #: KonfigInterface Methods
67
68
    public function all()
69
    {
70
        return $this->data;
71
    }
72
73
    /**
74
     * {@inheritDoc}
75
     */
76
    public function has($key)
77
    {
78
        // Check if already cached
79
        if (isset($this->cache[$key])) {
80
            return true;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return true; (boolean) is incompatible with the return type declared by the interface Exen\Konfig\KonfigInterface::has of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
81
        }
82
83
        $chunks = explode('.', $key);
84
        $root   = $this->data;
85
86
        // Nested case
87
        foreach ($chunks as $chunk) {
88
            if (array_key_exists($chunk, $root)) {
89
                $root = $root[$chunk];
90
                continue;
91
            } else {
92
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type declared by the interface Exen\Konfig\KonfigInterface::has of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
93
            }
94
        }
95
96
        // Set cache for the given key
97
        $this->cache[$key] = $root;
98
99
        return true;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return true; (boolean) is incompatible with the return type declared by the interface Exen\Konfig\KonfigInterface::has of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
100
    }
101
102
    /**
103
     * {@inheritDoc}
104
     */
105
    public function get($key, $default = null)
106
    {
107
        if ($this->has($key)) {
108
            return $this->cache[$key];
109
        }
110
111
        return $default;
112
    }
113
114
    /**
115
     * {@inheritDoc}
116
     */
117
    public function set($key, $value)
118
    {
119
        $chunks   = explode('.', $key);
120
        $root     = &$this->data;
121
        $cacheKey = '';
122
123
        // Look for the key, creating nested keys if needed
124
        while ($part = array_shift($chunks)) {
125
            if ($cacheKey != '') {
126
                $cacheKey .= '.';
127
            }
128
129
            $cacheKey .= $part;
130
131
            if (!isset($root[$part]) && count($chunks)) {
132
                $root[$part] = [];
133
            }
134
135
            $root = &$root[$part];
136
137
            // Unset all old nested cache
138
            if (isset($this->cache[$cacheKey])) {
139
                unset($this->cache[$cacheKey]);
140
            }
141
142
            // Unset all old nested cache in case of array
143
            if (count($chunks) == 0) {
144
                foreach ($this->cache as $cacheLocalKey => $cacheValue) {
145
                    if (substr($cacheLocalKey, 0, strlen($cacheKey)) === $cacheKey) {
146
                        unset($this->cache[$cacheLocalKey]);
147
                    }
148
                }
149
            }
150
        }
151
152
        // Assign value at target node
153
        $this->cache[$key] = $root = $value;
154
    }
155
156
    /**
157
     * {@inheritDoc}
158
     */
159
    public function delete($key)
160
    {
161
        if (isset($this->cache[$key])) {
162
            unset($this->cache[$key]);
163
        }
164
165
        return Utils::arrDelete($this->data, $key);
0 ignored issues
show
Bug introduced by
The method arrDelete() does not exist on Exen\Konfig\Utils. Did you maybe mean arrayDelete()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
166
    }
167
168
    #: ArrayAccess Methods
169
170
    /**
171
     * Gets a value using the offset as a key
172
     *
173
     * @param  string $offset
174
     * @return mixed
175
     * @since 0.1
176
     */
177
    public function offsetGet($offset)
178
    {
179
        return $this->get($offset);
180
    }
181
182
    /**
183
     * Checks if a key exists
184
     *
185
     * @param  string $offset
186
     * @return bool
187
     * @since 0.1
188
     */
189
    public function offsetExists($offset)
190
    {
191
        return $this->has($offset);
192
    }
193
194
    /**
195
     * Sets a value using the offset as a key
196
     *
197
     * @param string $offset
198
     * @param mixed $value
199
     * @return void
200
     * @since 0.1
201
     */
202
    public function offsetSet($offset, $value)
203
    {
204
        $this->set($offset, $value);
205
    }
206
207
    /**
208
     * Deletes a key and its value
209
     *
210
     * @param  string $offset
211
     * @return void
212
     * @since 0.1
213
     */
214
    public function offsetUnset($offset)
215
    {
216
        $this->set($offset, null);
217
    }
218
219
    #: Iterator Methods
220
221
    /**
222
     * Tests whether the iterator's current index is valid
223
     *
224
     * @return bool True if the current index is valid; false otherwise
225
     * @since 0.1
226
     */
227
    public function valid()
228
    {
229
        return (is_array($this->data) ? key($this->data) !== null : false);
230
    }
231
232
    /**
233
     * Returns the data array index referenced by its internal cursor
234
     *
235
     * @return mixed The index referenced by the data array's internal cursor.
236
     * If the array is empty or undefined or there is no element at the cursor,
237
     * the function returns null
238
     * @since 0.1
239
     */
240
    public function key()
241
    {
242
        return (is_array($this->data) ? key($this->data) : null);
243
    }
244
245
    /**
246
     * Returns the data array element referenced by its internal cursor
247
     *
248
     * @return mixed The element referenced by the data array's internal cursor.
249
     * If the array is empty or there is no element at the cursor,
250
     * the function returns false. If the array is undefined, the function
251
     * returns null
252
     * @since 0.1
253
     */
254
    public function current()
255
    {
256
        return (is_array($this->data) ? current($this->data) : null);
257
    }
258
259
    /**
260
     * Moves the data array's internal cursor forward one element
261
     *
262
     * @return mixed The element referenced by the data array's internal cursor
263
     * after the move is completed. If there are no more elements in the
264
     * array after the move, the function returns false. If the data array
265
     * is undefined, the function returns null
266
     * @since 0.1
267
     */
268
    public function next()
269
    {
270
        return (is_array($this->data) ? next($this->data) : null);
271
    }
272
273
    /**
274
     * Moves the data array's internal cursor to the first element
275
     *
276
     * @return mixed The element referenced by the data array's internal cursor
277
     * after the move is completed. If the data array is empty, the function
278
     * returns false. If the data array is undefined, the function returns null
279
     * @since 0.1
280
     */
281
    public function rewind()
282
    {
283
        return (is_array($this->data) ? reset($this->data) : null);
284
    }
285
}
286
287
#: END OF ./src/AbstractKonfig.php FILE
288