ZendConfig   C
last analyzed

Complexity

Total Complexity 55

Size/Duplication

Total Lines 449
Duplicated Lines 7.8 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 35
loc 449
rs 6.8
c 0
b 0
f 0
wmc 55
lcom 1
cbo 1

24 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 5 15 3
A get() 0 8 2
A __get() 0 4 1
A __set() 5 13 3
A __clone() 12 12 3
A toArray() 13 13 3
A __isset() 0 4 1
A __unset() 0 10 2
A count() 0 4 1
A current() 0 5 1
A key() 0 4 1
A next() 0 9 2
A rewind() 0 6 1
A valid() 0 4 1
A getSectionName() 0 7 3
A areAllSectionsLoaded() 0 4 1
B merge() 0 20 6
A setReadOnly() 0 9 3
A readOnly() 0 4 1
A getExtends() 0 4 1
A setExtend() 0 8 4
A assertValidExtend() 0 13 3
A loadFileErrorHandler() 0 8 2
B arrayMergeRecursive() 0 20 6

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ZendConfig often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ZendConfig, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace FMUP\Config\Ini\Extended;
3
4
/**
5
 * Zend Framework
6
 *
7
 * LICENSE
8
 *
9
 * This source file is subject to the new BSD license that is bundled
10
 * with this package in the file LICENSE.txt.
11
 * It is also available through the world-wide-web at this URL:
12
 * http://framework.zend.com/license/new-bsd
13
 * If you did not receive a copy of the license and are unable to
14
 * obtain it through the world-wide-web, please send an email
15
 * to [email protected] so we can send you a copy immediately.
16
 *
17
 * @category   Zend
18
 * @package    Zend_Config
19
 * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
20
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
21
 * @version    $Id$
22
 */
23
24
use FMUP\Config\Exception;
25
26
/**
27
 * @category   Zend
28
 * @package    Zend_Config
29
 * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
30
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
31
 * @codeCoverageIgnore since its an open source component
32
 * @SuppressWarnings(PHPMD)
33
 */
34
class ZendConfig implements \Countable, \Iterator
35
{
36
    /**
37
     * Whether in-memory modifications to configuration data are allowed
38
     *
39
     * @var boolean
40
     */
41
    protected $allowModifications;
42
43
    /**
44
     * Iteration index
45
     *
46
     * @var integer
47
     */
48
    protected $index;
49
50
    /**
51
     * Number of elements in configuration data
52
     *
53
     * @var integer
54
     */
55
    protected $count;
56
57
    /**
58
     * Contains array of configuration data
59
     *
60
     * @var array
61
     */
62
    protected $data;
63
64
    /**
65
     * Used when unsetting values during iteration to ensure we do not skip
66
     * the next element
67
     *
68
     * @var boolean
69
     */
70
    protected $skipNextIteration;
71
72
    /**
73
     * Contains which config file sections were loaded. This is null
74
     * if all sections were loaded, a string name if one section is loaded
75
     * and an array of string names if multiple sections were loaded.
76
     *
77
     * @var mixed
78
     */
79
    protected $loadedSection;
80
81
    /**
82
     * This is used to track section inheritance. The keys are names of sections that
83
     * extend other sections, and the values are the extended sections.
84
     *
85
     * @var array
86
     */
87
    protected $extends = array();
88
89
    /**
90
     * Load file error string.
91
     *
92
     * Is null if there was no error while file loading
93
     *
94
     * @var string
95
     */
96
    protected $loadFileErrorStr = null;
97
98
    /**
99
     * Zend_Config provides a property based interface to
100
     * an array. The data are read-only unless $allowModifications
101
     * is set to true on construction.
102
     *
103
     * Zend_Config also implements Countable and Iterator to
104
     * facilitate easy access to the data.
105
     *
106
     * @param  array $array
107
     * @param  boolean $allowModifications
108
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
109
     */
110
    public function __construct(array $array, $allowModifications = false)
111
    {
112
        $this->allowModifications = (boolean)$allowModifications;
113
        $this->loadedSection = null;
114
        $this->index = 0;
115
        $this->data = array();
116
        foreach ($array as $key => $value) {
117 View Code Duplication
            if (is_array($value)) {
0 ignored issues
show
Duplication introduced by
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...
118
                $this->data[$key] = new self($value, $this->allowModifications);
119
            } else {
120
                $this->data[$key] = $value;
121
            }
122
        }
123
        $this->count = count($this->data);
124
    }
125
126
    /**
127
     * Retrieve a value and return $default if there is no element set.
128
     *
129
     * @param string $name
130
     * @param mixed $default
131
     * @return mixed
132
     */
133
    public function get($name, $default = null)
134
    {
135
        $result = $default;
136
        if (array_key_exists($name, $this->data)) {
137
            $result = $this->data[$name];
138
        }
139
        return $result;
140
    }
141
142
    /**
143
     * Magic function so that $obj->value will work.
144
     *
145
     * @param string $name
146
     * @return mixed
147
     */
148
    public function __get($name)
149
    {
150
        return $this->get($name);
151
    }
152
153
    /**
154
     * Only allow setting of a property if $allowModifications
155
     * was set to true on construction. Otherwise, throw an exception.
156
     *
157
     * @param  string $name
158
     * @param  mixed $value
159
     * @throws Exception
160
     * @return void
161
     */
162
    public function __set($name, $value)
163
    {
164
        if ($this->allowModifications) {
165 View Code Duplication
            if (is_array($value)) {
0 ignored issues
show
Duplication introduced by
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...
166
                $this->data[$name] = new self($value, true);
167
            } else {
168
                $this->data[$name] = $value;
169
            }
170
            $this->count = count($this->data);
171
        } else {
172
            throw new Exception('ZendConfig is read only');
173
        }
174
    }
175
176
    /**
177
     * Deep clone of this instance to ensure that nested Zend_Configs
178
     * are also cloned.
179
     *
180
     * @return void
181
     */
182 View Code Duplication
    public function __clone()
0 ignored issues
show
Duplication introduced by
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...
183
    {
184
        $array = array();
185
        foreach ($this->data as $key => $value) {
186
            if ($value instanceof ZendConfig) {
187
                $array[$key] = clone $value;
188
            } else {
189
                $array[$key] = $value;
190
            }
191
        }
192
        $this->data = $array;
193
    }
194
195
    /**
196
     * Return an associative array of the stored data.
197
     *
198
     * @return array
199
     */
200 View Code Duplication
    public function toArray()
0 ignored issues
show
Duplication introduced by
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...
201
    {
202
        $array = array();
203
        $data = $this->data;
204
        foreach ($data as $key => $value) {
205
            if ($value instanceof ZendConfig) {
206
                $array[$key] = $value->toArray();
207
            } else {
208
                $array[$key] = $value;
209
            }
210
        }
211
        return $array;
212
    }
213
214
    /**
215
     * Support isset() overloading on PHP 5.1
216
     *
217
     * @param string $name
218
     * @return boolean
219
     */
220
    public function __isset($name)
221
    {
222
        return isset($this->data[$name]);
223
    }
224
225
    /**
226
     * Support unset() overloading on PHP 5.1
227
     *
228
     * @param  string $name
229
     * @throws Exception
230
     * @return void
231
     */
232
    public function __unset($name)
233
    {
234
        if ($this->allowModifications) {
235
            unset($this->data[$name]);
236
            $this->count = count($this->data);
237
            $this->skipNextIteration = true;
238
        } else {
239
            throw new Exception('Zend_Config is read only');
240
        }
241
    }
242
243
    /**
244
     * Defined by Countable interface
245
     *
246
     * @return int
247
     */
248
    public function count()
249
    {
250
        return $this->count;
251
    }
252
253
    /**
254
     * Defined by Iterator interface
255
     *
256
     * @return mixed
257
     */
258
    public function current()
259
    {
260
        $this->skipNextIteration = false;
261
        return current($this->data);
262
    }
263
264
    /**
265
     * Defined by Iterator interface
266
     *
267
     * @return mixed
268
     */
269
    public function key()
270
    {
271
        return key($this->data);
272
    }
273
274
    /**
275
     * Defined by Iterator interface
276
     *
277
     */
278
    public function next()
279
    {
280
        if ($this->skipNextIteration) {
281
            $this->skipNextIteration = false;
282
            return;
283
        }
284
        next($this->data);
285
        $this->index++;
286
    }
287
288
    /**
289
     * Defined by Iterator interface
290
     *
291
     */
292
    public function rewind()
293
    {
294
        $this->skipNextIteration = false;
295
        reset($this->data);
296
        $this->index = 0;
297
    }
298
299
    /**
300
     * Defined by Iterator interface
301
     *
302
     * @return boolean
303
     */
304
    public function valid()
305
    {
306
        return $this->index < $this->count;
307
    }
308
309
    /**
310
     * Returns the section name(s) loaded.
311
     *
312
     * @return mixed
313
     */
314
    public function getSectionName()
315
    {
316
        if (is_array($this->loadedSection) && count($this->loadedSection) == 1) {
317
            $this->loadedSection = $this->loadedSection[0];
318
        }
319
        return $this->loadedSection;
320
    }
321
322
    /**
323
     * Returns true if all sections were loaded
324
     *
325
     * @return boolean
326
     */
327
    public function areAllSectionsLoaded()
328
    {
329
        return $this->loadedSection === null;
330
    }
331
332
333
    /**
334
     * Merge another Zend_Config with this one. The items
335
     * in $merge will override the same named items in
336
     * the current config.
337
     *
338
     * @param ZendConfig $merge
339
     * @return ZendConfig
340
     */
341
    public function merge(ZendConfig $merge)
342
    {
343
        foreach ($merge as $key => $item) {
344
            if (array_key_exists($key, $this->data)) {
345
                if ($item instanceof ZendConfig && $this->$key instanceof ZendConfig) {
346
                    $this->$key = $this->$key->merge(new ZendConfig($item->toArray(), !$this->readOnly()));
347
                } else {
348
                    $this->$key = $item;
349
                }
350
            } else {
351
                if ($item instanceof ZendConfig) {
352
                    $this->$key = new ZendConfig($item->toArray(), !$this->readOnly());
353
                } else {
354
                    $this->$key = $item;
355
                }
356
            }
357
        }
358
359
        return $this;
360
    }
361
362
    /**
363
     * Prevent any more modifications being made to this instance. Useful
364
     * after merge() has been used to merge multiple Zend_Config objects
365
     * into one object which should then not be modified again.
366
     *
367
     */
368
    public function setReadOnly()
369
    {
370
        $this->allowModifications = false;
371
        foreach ($this->data as $value) {
372
            if ($value instanceof ZendConfig) {
373
                $value->setReadOnly();
374
            }
375
        }
376
    }
377
378
    /**
379
     * Returns if this Zend_Config object is read only or not.
380
     *
381
     * @return boolean
382
     */
383
    public function readOnly()
384
    {
385
        return !$this->allowModifications;
386
    }
387
388
    /**
389
     * Get the current extends
390
     *
391
     * @return array
392
     */
393
    public function getExtends()
394
    {
395
        return $this->extends;
396
    }
397
398
    /**
399
     * Set an extend for Zend_Config_Writer
400
     *
401
     * @param  string $extendingSection
402
     * @param  string $extendedSection
403
     * @return void
404
     */
405
    public function setExtend($extendingSection, $extendedSection = null)
406
    {
407
        if ($extendedSection === null && isset($this->extends[$extendingSection])) {
408
            unset($this->extends[$extendingSection]);
409
        } elseif ($extendedSection !== null) {
410
            $this->extends[$extendingSection] = $extendedSection;
411
        }
412
    }
413
414
    /**
415
     * Throws an exception if $extendingSection may not extend $extendedSection,
416
     * and tracks the section extension if it is valid.
417
     *
418
     * @param  string $extendingSection
419
     * @param  string $extendedSection
420
     * @throws Exception
421
     * @return void
422
     */
423
    protected function assertValidExtend($extendingSection, $extendedSection)
424
    {
425
        // detect circular section inheritance
426
        $extendedSectionCurrent = $extendedSection;
427
        while (array_key_exists($extendedSectionCurrent, $this->extends)) {
428
            if ($this->extends[$extendedSectionCurrent] == $extendingSection) {
429
                throw new Exception('Illegal circular inheritance detected');
430
            }
431
            $extendedSectionCurrent = $this->extends[$extendedSectionCurrent];
432
        }
433
        // remember that this section extends another section
434
        $this->extends[$extendingSection] = $extendedSection;
435
    }
436
437
    /**
438
     * Handle any errors from simplexml_load_file or parse_ini_file
439
     *
440
     * @param integer $errno
441
     * @param string $errstr
442
     * @param string $errfile
443
     * @param integer $errline
444
     */
445
    public function loadFileErrorHandler($errno, $errstr, $errfile, $errline)
446
    {
447
        if ($this->loadFileErrorStr === null) {
448
            $this->loadFileErrorStr = $errstr . "([$errno] @ $errfile:$errline)";
449
        } else {
450
            $this->loadFileErrorStr .= (PHP_EOL . $errstr . "([$errno] @ $errfile:$errline)");
451
        }
452
    }
453
454
    /**
455
     * Merge two arrays recursively, overwriting keys of the same name
456
     * in $firstArray with the value in $secondArray.
457
     *
458
     * @param  mixed $firstArray First array
459
     * @param  mixed $secondArray Second array to merge into first array
460
     * @return array
461
     */
462
    protected function arrayMergeRecursive($firstArray, $secondArray)
463
    {
464
        if (is_array($firstArray) && is_array($secondArray)) {
465
            foreach ($secondArray as $key => $value) {
466
                if (isset($firstArray[$key])) {
467
                    $firstArray[$key] = $this->arrayMergeRecursive($firstArray[$key], $value);
468
                } else {
469
                    if ($key === 0) {
470
                        $firstArray = array(0 => $this->arrayMergeRecursive($firstArray, $value));
471
                    } else {
472
                        $firstArray[$key] = $value;
473
                    }
474
                }
475
            }
476
        } else {
477
            $firstArray = $secondArray;
478
        }
479
480
        return $firstArray;
481
    }
482
}
483