Completed
Pull Request — 2.0 (#8)
by Tim
05:06
created

Properties::mergeProperties()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 7
cts 7
cp 1
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 4
nc 3
nop 2
crap 5
1
<?php
2
3
/**
4
 * \AppserverIo\Properties\Properties
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Tim Wagner <[email protected]>
15
 * @copyright 2015 TechDivision GmbH <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      http://github.com/appserver-io/properties
18
 * @link      http://www.appserver.io
19
 */
20
21
namespace AppserverIo\Properties;
22
23
use AppserverIo\Lang\String;
24
use AppserverIo\Lang\NullPointerException;
25
use AppserverIo\Collections\HashMap;
26
use Metadata\MergeableClassMetadata;
27
28
/**
29
 * The Properties class represents a persistent set of properties.
30
 * The Properties can be saved to a stream or loaded from a stream.
31
 * Each key and its corresponding value in the property list is a string.
32
 *
33
 * A property list can contain another property list as its "defaults";
34
 * this second property list is searched if the property key is not
35
 * found in the original property list.
36
 *
37
 * Because Properties inherits from HashMap, the put method can be
38
 * applied to a Properties object. Their use is strongly discouraged
39
 * as they allow the caller to insert entries whose keys or values are
40
 * not Strings. The setProperty method should be used instead. If the
41
 * store or save method is called on a "compromised" Properties object
42
 * that contains a non-String key or value, the call will fail.
43
 *
44
 * @author    Tim Wagner <[email protected]>
45
 * @copyright 2015 TechDivision GmbH <[email protected]>
46
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
47
 * @link      http://github.com/appserver-io/properties
48
 * @link      http://www.appserver.io
49
 */
50
class Properties extends HashMap implements PropertiesInterface
51
{
52
53
    /**
54
     * This member is TRUE if the sections should be parsed, else FALSE
55
     *
56
     * @var boolean
57
     */
58
    protected $sections = false;
59
60
    /**
61
     * The default constructor.
62
     *
63
     * @param \AppserverIo\Properties\Properties $defaults The properties we want want to use for initialization
64
     */
65 11
    public function __construct(Properties $defaults = null)
66
    {
67
        // check if properties are passed
68 11
        if ($defaults != null) {
69
            // if yes set them
70
            parent::__construct($defaults->toArray());
71
        } else {
72 11
            parent::__construct();
73
        }
74 11
    }
75
76
    /**
77
     * Factory method.
78
     *
79
     * @param \AppserverIo\Properties\Properties $defaults Default properties to initialize the new ones with
80
     *
81
     * @return \AppserverIo\Properties\Properties The initialized properties
82
     */
83 8
    public static function create(Properties $defaults = null)
84
    {
85 8
        return new Properties($defaults);
86
    }
87
88
    /**
89
     * Reads a property list (key and element pairs) from the passed file.
90
     *
91
     * @param string  $file        The path and the name of the file to load the properties from
92
     * @param boolean $sections    Has to be TRUE to parse the sections
93
     * @param integer $scannerMode Can either be INI_SCANNER_NORMAL (default) or INI_SCANNER_RAW, if INI_SCANNER_RAW is supplied, then option values will not be parsed.
94
     *
95
     * @return \AppserverIo\Properties\Properties The initialized properties
96
     * @throws \AppserverIo\Properties\PropertyFileParseException Is thrown if an error occurs while parsing the property file
97
     * @throws \AppserverIo\Properties\PropertyFileNotFoundException Is thrown if the property file passed as parameter does not exist in the include path
98
     * @link http://php.net/parse_ini_string
99
     */
100 7
    public function load($file, $sections = false, $scannerMode = INI_SCANNER_RAW)
101
    {
102
        // try to load the file content
103 7
        $content = @file_get_contents($file, FILE_USE_INCLUDE_PATH);
104
        // check if file has successfully been loaded
105 7
        if (! $content) {
106
            // throw an exception if the file can not be found in the include path
107 1
            throw new PropertyFileNotFoundException(sprintf('File %s not found in include path', $file));
108
        }
109
        // parse the file content
110 6
        $properties = parse_ini_string($content, $this->sections = $sections, $scannerMode);
111
        // check if property file was parsed successfully
112 6
        if ($properties == false) {
113
            // throw an exception if an error occurs
114 1
            throw new PropertyFileParseException(sprintf('File %s can not be parsed as property file', $file));
115
        }
116
        // set the found values
117 5
        $this->items = $properties;
118
        // return the initialized properties
119 5
        return $this;
120
    }
121
122
    /**
123
     * Stores the properties in the property file. This method is NOT using the include path for storing the file.
124
     *
125
     * @param string $file The path and the name of the file to store the properties to
126
     *
127
     * @return void
128
     *
129
     * @throws \AppserverIo\Properties\PropertyFileStoreException Is thrown if the file could not be written
130
     * @todo Actually only properties without sections will be stored, if a section is specified, then it will be ignored
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
131
     */
132 2
    public function store($file)
133
    {
134
        // create a new file or replace the old one if it exists
135 2
        if (($handle = @fopen($file, "w+")) === false) {
136 1
            throw new PropertyFileStoreException(sprintf('Can\'t open property file %s for writing', $file));
137
        }
138
        // store the property in the file
139 1
        foreach ($this->items as $name => $value) {
140 1
            $written = @fwrite($handle, $name . " = " . addslashes($value) . PHP_EOL);
141 1
            if ($written === false) {
142
                throw new PropertyFileStoreException(sprintf('Can\'t attach property with name %s to property file %s', $name, $file));
143
            }
144 1
        }
145
        // saves and closes the file and returns TRUE if the file was written successfully
146 1
        if (!@fclose($handle)) {
147
            throw new PropertyFileStoreException(sprintf('Error while closing and writing property file %s', $file));
148
        }
149 1
    }
150
151
    /**
152
     * Searches for the property with the specified key in this property list.
153
     *
154
     * @param string $key     Holds the key of the value to return
155
     * @param string $section Holds a string with the section name to return the key for (only matters if sections is set to TRUE)
156
     *
157
     * @return string Holds the value of the passed key
158
     * @throws \AppserverIo\Lang\NullPointerException Is thrown if the passed key, or, if sections are TRUE, the passed section is NULL
159
     */
160 6
    public function getProperty($key, $section = null)
161
    {
162
        // initialize the property value
163 6
        $property = null;
164
        // check if the sections are included
165 6
        if ($this->sections) {
166
            // if the passed section OR the passed key is NULL throw an exception
167 1
            if ($section == null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $section of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
168
                throw new NullPointerException('Passed section is null');
169
            }
170 1
            if ($key == null) {
171
                throw new NullPointerException('Passed key is null');
172
            }
173
            // if the section exists ...
174 1
            if ($this->exists($section)) {
175
                // get all entries of the section
176 1
                $entries = new HashMap($this->get($section));
177 1
                if ($entries->exists($key)) {
178
                    // if yes set it
179 1
                    $property = $entries->get($key);
180 1
                }
181 1
            }
182 1
        } else {
183
            // if the passed key is NULL throw an exception
184 5
            if ($key == null) {
185
                throw new NullPointerException('Passed key is null');
186
            }
187
            // check if the property exists in the internal list
188 5
            if ($this->exists($key)) {
189
                // if yes set it
190 5
                $property = $this->get($key);
191 5
            }
192
        }
193
        // return the property or null
194 6
        return $property;
195
    }
196
197
    /**
198
     * Calls the HashMap method add.
199
     *
200
     * @param string $key     Holds the key of the value to return
201
     * @param mixed  $value   Holds the value to add to the properties
202
     * @param string $section Holds a string with the section name to return the key for (only matters if sections is set to TRUE)
203
     *
204
     * @return void
205
     * @throws \AppserverIo\Lang\NullPointerException Is thrown if the passed key, or, if sections are TRUE, the passed section is NULL
206
     */
207 5
    public function setProperty($key, $value, $section = null)
208
    {
209
        // check if the sections are included
210 5
        if ($this->sections) {
211
            // if the passed section OR the passed key is NULL throw an exception
212
            if ($section == null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $section of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
213
                throw new NullPointerException('Passed section is null');
214
            }
215
            if ($key == null) {
216
                throw new NullPointerException('Passed key is null');
217
            }
218
            // if the section exists ...
219
            if ($this->exists($section)) {
220
                // get all entries of the section
221
                $entries = new HashMap($this->get($section));
222
                $entries->add($key, $value);
223
            }
224
        } else {
225
            // if the passed key is NULL throw an exception
226 5
            if ($key == null) {
227
                throw new NullPointerException('Passed key is null');
228
            }
229
            // add the value with the passed
230 5
            $this->add($key, $value);
231
        }
232 5
    }
233
234
    /**
235
     * Returns all properties with their keys as a string.
236
     *
237
     * @return string String with all key -> properties pairs
238
     */
239
    public function __toString()
240
    {
241
        // initialize the return value
242
        $return = "";
243
        // iterate over all items and concatenate the values to
244
        // the return string
245
        foreach ($this->items as $key => $value) {
246
            // if sections are set to true there can be subarrays
247
            // with key/value pairs
248
            if (is_array($value)) {
249
                // set the section and add the key/value pairs to the section
250
                $return .= "[" . $key . "]";
251
                foreach ($value as $sectionKey => $sectionValue) {
252
                    $return .= $sectionKey . "=" . $sectionValue . PHP_EOL;
253
                }
254
            }
255
            // add the key/value pair
256
            $return .= $key . "=" . $value . PHP_EOL;
257
        }
258
        // return the string
259
        return $return;
260
    }
261
262
    /**
263
     * Returns all properties with their keys as a String.
264
     *
265
     * @return \AppserverIo\Lang\String String with all key -> properties pairs
266
     */
267
    public function toString()
268
    {
269
        return new String($this->__toString());
270
    }
271
272
    /**
273
     * Merges the passed properties into the actual instance. If override
274
     * flag is set to TRUE, existing properties will be overwritten.
275
     *
276
     * @param \AppserverIo\Properties\PropertiesInterface $properties The properties to merge
277
     * @param boolean                                     $override   TRUE if existing properties have to be overwritten, else FALSE
278
     *
279
     * @return void
280
     */
281 2
    public function mergeProperties(PropertiesInterface $properties, $override = false)
282
    {
283
        // iterate over the keys of the passed properties and add thm, or replace existing ones
284 2
        foreach ($properties as $key => $value) {
0 ignored issues
show
Bug introduced by
The expression $properties of type object<AppserverIo\Prope...es\PropertiesInterface> is not traversable.
Loading history...
285 2
            if ($this->exists($key) === false || ($this->exists($key) === true && $override === true)) {
286 2
                $this->setProperty($key, $value);
287 2
            }
288 2
        }
289 2
    }
290
291
    /**
292
     * Returns all key values as an array.
293
     *
294
     * @return array The keys as array values
295
     */
296 3
    public function getKeys()
297
    {
298
        // check if the property file is sectioned
299 3
        if ($this->sections) {
300
            // initialize the array for the keys
301 1
            $keys = array();
302
            // iterate over the sections and merge all sectioned keys
303 1
            foreach ($this->items as $item) {
304 1
                $keys = array_merge($keys, array_keys($item));
305 1
            }
306
            // return the keys
307 1
            return $keys;
308
        } else {
309 2
            return array_keys($this->items);
310
        }
311
    }
312
}
313