Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
81 / 81
100.00% covered (success)
100.00%
15 / 15
CRAP
100.00% covered (success)
100.00%
1 / 1
Config
100.00% covered (success)
100.00%
81 / 81
100.00% covered (success)
100.00%
15 / 15
43
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 createFromData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 parseData
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
6
 merge
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 mergeFromData
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 writeToFile
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
7
 toArrayObject
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toJson
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toIni
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toXml
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 changesAllowed
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 arrayToXml
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
5
 arrayToIni
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
9
 __set
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 __unset
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * Pop PHP Framework (http://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@nolainteractive.com>
7 * @copyright  Copyright (c) 2009-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com)
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Config;
15
16use Pop\Utils;
17
18/**
19 * Config class
20 *
21 * @category   Pop
22 * @package    Pop\Config
23 * @author     Nick Sagona, III <dev@nolainteractive.com>
24 * @copyright  Copyright (c) 2009-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com)
25 * @license    http://www.popphp.org/license     New BSD License
26 * @version    3.4.0
27 */
28class Config extends Utils\ArrayObject
29{
30
31    /**
32     * Flag for whether changes are allowed after object instantiation
33     * @var boolean
34     */
35    protected $allowChanges = false;
36
37    /**
38     * Constructor
39     *
40     * Instantiate a config object
41     *
42     * @param  mixed   $data
43     * @param  boolean $changes
44     */
45    public function __construct($data = [], $changes = false)
46    {
47        $this->allowChanges = (bool)$changes;
48        parent::__construct($data);
49    }
50
51    /**
52     * Method to create a config object from parsed data
53     *
54     * @param  mixed   $data
55     * @param  boolean $changes
56     * @return self
57     */
58    public static function createFromData($data, $changes = false)
59    {
60        return new self(self::parseData($data), $changes);
61    }
62
63    /**
64     * Method to parse data and return config values
65     *
66     * @param  mixed $data
67     * @return array
68     */
69    public static function parseData($data)
70    {
71        // If PHP
72        if ((strtolower((substr($data, -6)) == '.phtml') ||
73            strtolower((substr($data, -4)) == '.php'))) {
74            $data = include $data;
75        // If JSON
76        } else if (strtolower(substr($data, -5)) == '.json') {
77            $data = json_decode(file_get_contents($data), true);
78        // If INI
79        } else if (strtolower(substr($data, -4)) == '.ini') {
80            $data = parse_ini_file($data, true);
81        // If XML
82        } else if (strtolower(substr($data, -4)) == '.xml') {
83            $data = (array)simplexml_load_file($data);
84        } else {
85            $data = [];
86        }
87
88        return $data;
89    }
90
91    /**
92     * Merge the values of another config object into this one.
93     * By default, existing values are overwritten, unless the
94     * $preserve flag is set to true.
95     *
96     * @param  mixed    $data
97     * @param  boolean $preserve
98     * @throws Exception
99     * @return Config
100     */
101    public function merge($data, $preserve = false)
102    {
103        if (!$this->allowChanges) {
104            throw new Exception('Real-time configuration changes are not allowed.');
105        }
106
107        $this->data = ($preserve) ?
108            array_merge_recursive($this->data, $data) : array_replace_recursive($this->data, $data);
109
110        return $this;
111    }
112
113    /**
114     * Merge the values of another config object into this one.
115     * By default, existing values are overwritten, unless the
116     * $preserve flag is set to true.
117     *
118     * @param  mixed   $data
119     * @param  boolean $preserve
120     * @throws Exception
121     * @return Config
122     */
123    public function mergeFromData($data, $preserve = false)
124    {
125        if (!$this->allowChanges) {
126            throw new Exception('Real-time configuration changes are not allowed.');
127        }
128
129        return $this->merge(self::parseData($data), $preserve);
130    }
131
132    /**
133     * Write the config data to file
134     *
135     * @param  string $filename
136     * @throws Exception
137     * @return void
138     */
139    public function writeToFile($filename)
140    {
141        if (strpos($filename, '.') !== false) {
142            $ext = strtolower(substr($filename, (strrpos($filename, '.') + 1)));
143            switch ($ext) {
144                case 'php':
145                    $config  = '<?php' . PHP_EOL . PHP_EOL;
146                    $config .= 'return ' . var_export($this->toArray(), true) . ';';
147                    $config .= PHP_EOL;
148                    file_put_contents($filename, $config);
149                    break;
150                case 'json':
151                    file_put_contents($filename, $this->toJson());
152                    break;
153                case 'ini':
154                    file_put_contents($filename, $this->toIni());
155                    break;
156                case 'xml':
157                    file_put_contents($filename, $this->toXml());
158                    break;
159                default:
160                    throw new Exception("Invalid type '" . $ext . "'. Supported config file types are PHP, JSON, INI or XML.");
161            }
162        }
163    }
164
165    /**
166     * Get the config values as an array
167     *
168     * @return \ArrayObject
169     */
170    public function toArrayObject()
171    {
172        return new \ArrayObject($this->toArray(), \ArrayObject::ARRAY_AS_PROPS);
173    }
174
175    /**
176     * Get the config values as a JSON string
177     *
178     * @return string
179     */
180    public function toJson()
181    {
182        return $this->jsonSerialize(JSON_PRETTY_PRINT);
183    }
184
185    /**
186     * Get the config values as an INI string
187     *
188     * @return string
189     */
190    public function toIni()
191    {
192        return $this->arrayToIni($this->toArray());
193    }
194
195    /**
196     * Get the config values as an XML string
197     *
198     * @return string
199     */
200    public function toXml()
201    {
202        $config = new \SimpleXMLElement('<?xml version="1.0"?><config></config>');
203        $this->arrayToXml($this->toArray(), $config);
204
205        $dom = new \DOMDocument('1.0');
206        $dom->preserveWhiteSpace = false;
207        $dom->formatOutput       = true;
208        $dom->loadXML($config->asXML());
209        return $dom->saveXML();
210    }
211
212    /**
213     * Return if changes to the config are allowed.
214     *
215     * @return boolean
216     */
217    public function changesAllowed()
218    {
219        return $this->allowChanges;
220    }
221
222    /**
223     * Method to convert array to XML
224     *
225     * @param  array             $array
226     * @param  \SimpleXMLElement $config
227     * @return void
228     */
229    protected function arrayToXml($array, \SimpleXMLElement &$config)
230    {
231        foreach($array as $key => $value) {
232            if(is_array($value)) {
233                $subNode = (!is_numeric($key)) ? $config->addChild($key) : $config->addChild('item');
234                $this->arrayToXml($value, $subNode);
235            } else {
236                if (!is_numeric($key)) {
237                    $config->addChild($key, htmlspecialchars($value));
238                } else {
239                    $config->addChild('item', htmlspecialchars($value));
240                }
241            }
242        }
243    }
244
245    /**
246     * Method to convert array to INI
247     *
248     * @param  array $array
249     * @return string
250     */
251    protected function arrayToIni(array $array)
252    {
253        $ini          = '';
254        $lastWasArray = false;
255
256        foreach ($array as $key => $value) {
257            if (is_array($value)) {
258                if (!$lastWasArray) {
259                    $ini .= PHP_EOL;
260                }
261                $ini .= '[' . $key . ']' . PHP_EOL;
262                foreach ($value as $k => $v) {
263                    if (!is_array($v)) {
264                        $ini .= $key .
265                            '[' . ((!is_numeric($k)) ? $k : null) . '] = ' .
266                            ((!is_numeric($v)) ? '"' . $v . '"' : $v) . PHP_EOL;
267                    }
268                }
269                $ini .= PHP_EOL;
270                $lastWasArray = true;
271            } else {
272                $ini .= $key . " = " . ((!is_numeric($value)) ? '"' . $value . '"' : $value) . PHP_EOL;
273                $lastWasArray = false;
274            }
275        }
276
277        return $ini;
278    }
279
280    /**
281     * Set a value
282     *
283     * @param  string $name
284     * @param  mixed $value
285     * @throws Exception
286     * @return void
287     */
288    public function __set($name, $value)
289    {
290        if (!$this->allowChanges) {
291            throw new Exception('Real-time configuration changes are not allowed.');
292        }
293        parent::__set($name, $value);
294    }
295
296    /**
297     * Unset a value
298     *
299     * @param  string $name
300     * @throws Exception
301     * @return void
302     */
303    public function __unset($name)
304    {
305        if (!$this->allowChanges) {
306            throw new Exception('Real-time configuration changes are not allowed.');
307        }
308        parent::__unset($name);
309    }
310
311}