Properties   B
last analyzed

Complexity

Total Complexity 38

Size/Duplication

Total Lines 263
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 66.3%

Importance

Changes 0
Metric Value
wmc 38
lcom 1
cbo 6
dl 0
loc 263
ccs 61
cts 92
cp 0.663
rs 8.3999
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 2
A create() 0 4 1
A load() 0 21 3
B store() 0 18 5
C getProperty() 0 36 8
B setProperty() 0 26 6
B __toString() 0 22 4
A toString() 0 4 1
B mergeProperties() 0 9 5
A getKeys() 0 16 3
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\Strng;
24
use AppserverIo\Lang\NullPointerException;
25
use AppserverIo\Collections\HashMap;
26
27
/**
28
 * The Properties class represents a persistent set of properties.
29
 * The Properties can be saved to a stream or loaded from a stream.
30
 * Each key and its corresponding value in the property list is a string.
31
 *
32
 * A property list can contain another property list as its "defaults";
33
 * this second property list is searched if the property key is not
34
 * found in the original property list.
35
 *
36
 * Because Properties inherits from HashMap, the put method can be
37
 * applied to a Properties object. Their use is strongly discouraged
38
 * as they allow the caller to insert entries whose keys or values are
39
 * not Strings. The setProperty method should be used instead. If the
40
 * store or save method is called on a "compromised" Properties object
41
 * that contains a non-String key or value, the call will fail.
42
 *
43
 * @author    Tim Wagner <[email protected]>
44
 * @copyright 2015 TechDivision GmbH <[email protected]>
45
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
46
 * @link      http://github.com/appserver-io/properties
47
 * @link      http://www.appserver.io
48
 */
49
class Properties extends HashMap implements PropertiesInterface
50
{
51
52
    /**
53
     * This member is TRUE if the sections should be parsed, else FALSE
54
     *
55
     * @var boolean
56
     */
57
    protected $sections = false;
58
59
    /**
60
     * The default constructor.
61
     *
62
     * @param \AppserverIo\Properties\Properties $defaults The properties we want want to use for initialization
63
     */
64 11
    public function __construct(Properties $defaults = null)
65
    {
66
        // check if properties are passed
67 11
        if ($defaults != null) {
68
            // if yes set them
69
            parent::__construct($defaults->toArray());
70
        } else {
71 11
            parent::__construct();
72
        }
73 11
    }
74
75
    /**
76
     * Factory method.
77
     *
78
     * @param \AppserverIo\Properties\Properties $defaults Default properties to initialize the new ones with
79
     *
80
     * @return \AppserverIo\Properties\Properties The initialized properties
81
     */
82 8
    public static function create(Properties $defaults = null)
83
    {
84 8
        return new Properties($defaults);
85
    }
86
87
    /**
88
     * Reads a property list (key and element pairs) from the passed file.
89
     *
90
     * @param string  $file        The path and the name of the file to load the properties from
91
     * @param boolean $sections    Has to be TRUE to parse the sections
92
     * @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.
93
     *
94
     * @return \AppserverIo\Properties\Properties The initialized properties
95
     * @throws \AppserverIo\Properties\PropertyFileParseException Is thrown if an error occurs while parsing the property file
96
     * @throws \AppserverIo\Properties\PropertyFileNotFoundException Is thrown if the property file passed as parameter does not exist in the include path
97
     * @link http://php.net/parse_ini_string
98
     */
99 7
    public function load($file, $sections = false, $scannerMode = INI_SCANNER_RAW)
100
    {
101
        // try to load the file content
102 7
        $content = @file_get_contents($file, FILE_USE_INCLUDE_PATH);
103
        // check if file has successfully been loaded
104 7
        if (! $content) {
105
            // throw an exception if the file can not be found in the include path
106 1
            throw new PropertyFileNotFoundException(sprintf('File %s not found in include path', $file));
107
        }
108
        // parse the file content
109 6
        $properties = parse_ini_string($content, $this->sections = $sections, $scannerMode);
110
        // check if property file was parsed successfully
111 6
        if ($properties == false) {
112
            // throw an exception if an error occurs
113 1
            throw new PropertyFileParseException(sprintf('File %s can not be parsed as property file', $file));
114
        }
115
        // set the found values
116 5
        $this->items = $properties;
117
        // return the initialized properties
118 5
        return $this;
119
    }
120
121
    /**
122
     * Stores the properties in the property file. This method is NOT using the include path for storing the file.
123
     *
124
     * @param string $file The path and the name of the file to store the properties to
125
     *
126
     * @return void
127
     *
128
     * @throws \AppserverIo\Properties\PropertyFileStoreException Is thrown if the file could not be written
129
     * @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...
130
     */
131 2
    public function store($file)
132
    {
133
        // create a new file or replace the old one if it exists
134 2
        if (($handle = @fopen($file, "w+")) === false) {
135 1
            throw new PropertyFileStoreException(sprintf('Can\'t open property file %s for writing', $file));
136
        }
137
        // store the property in the file
138 1
        foreach ($this->items as $name => $value) {
139 1
            $written = @fwrite($handle, $name . " = " . addslashes($value) . PHP_EOL);
140 1
            if ($written === false) {
141
                throw new PropertyFileStoreException(sprintf('Can\'t attach property with name %s to property file %s', $name, $file));
142
            }
143 1
        }
144
        // saves and closes the file and returns TRUE if the file was written successfully
145 1
        if (!@fclose($handle)) {
146
            throw new PropertyFileStoreException(sprintf('Error while closing and writing property file %s', $file));
147
        }
148 1
    }
149
150
    /**
151
     * Searches for the property with the specified key in this property list.
152
     *
153
     * @param string $key     Holds the key of the value to return
154
     * @param string $section Holds a string with the section name to return the key for (only matters if sections is set to TRUE)
155
     *
156
     * @return string Holds the value of the passed key
157
     * @throws \AppserverIo\Lang\NullPointerException Is thrown if the passed key, or, if sections are TRUE, the passed section is NULL
158
     */
159 6
    public function getProperty($key, $section = null)
160
    {
161
        // initialize the property value
162 6
        $property = null;
163
        // check if the sections are included
164 6
        if ($this->sections) {
165
            // if the passed section OR the passed key is NULL throw an exception
166 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...
167
                throw new NullPointerException('Passed section is null');
168
            }
169 1
            if ($key == null) {
170
                throw new NullPointerException('Passed key is null');
171
            }
172
            // if the section exists ...
173 1
            if ($this->exists($section)) {
174
                // get all entries of the section
175 1
                $entries = new HashMap($this->get($section));
176 1
                if ($entries->exists($key)) {
177
                    // if yes set it
178 1
                    $property = $entries->get($key);
179 1
                }
180 1
            }
181 1
        } else {
182
            // if the passed key is NULL throw an exception
183 5
            if ($key == null) {
184
                throw new NullPointerException('Passed key is null');
185
            }
186
            // check if the property exists in the internal list
187 5
            if ($this->exists($key)) {
188
                // if yes set it
189 5
                $property = $this->get($key);
190 5
            }
191
        }
192
        // return the property or null
193 6
        return $property;
194
    }
195
196
    /**
197
     * Calls the HashMap method add.
198
     *
199
     * @param string $key     Holds the key of the value to return
200
     * @param mixed  $value   Holds the value to add to the properties
201
     * @param string $section Holds a string with the section name to return the key for (only matters if sections is set to TRUE)
202
     *
203
     * @return void
204
     * @throws \AppserverIo\Lang\NullPointerException Is thrown if the passed key, or, if sections are TRUE, the passed section is NULL
205
     */
206 5
    public function setProperty($key, $value, $section = null)
207
    {
208
        // check if the sections are included
209 5
        if ($this->sections) {
210
            // if the passed section OR the passed key is NULL throw an exception
211
            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...
212
                throw new NullPointerException('Passed section is null');
213
            }
214
            if ($key == null) {
215
                throw new NullPointerException('Passed key is null');
216
            }
217
            // if the section exists ...
218
            if ($this->exists($section)) {
219
                // get all entries of the section
220
                $entries = new HashMap($this->get($section));
221
                $entries->add($key, $value);
222
            }
223
        } else {
224
            // if the passed key is NULL throw an exception
225 5
            if ($key == null) {
226
                throw new NullPointerException('Passed key is null');
227
            }
228
            // add the value with the passed
229 5
            $this->add($key, $value);
230
        }
231 5
    }
232
233
    /**
234
     * Returns all properties with their keys as a string.
235
     *
236
     * @return string String with all key -> properties pairs
237
     */
238
    public function __toString()
239
    {
240
        // initialize the return value
241
        $return = "";
242
        // iterate over all items and concatenate the values to
243
        // the return string
244
        foreach ($this->items as $key => $value) {
245
            // if sections are set to true there can be subarrays
246
            // with key/value pairs
247
            if (is_array($value)) {
248
                // set the section and add the key/value pairs to the section
249
                $return .= "[" . $key . "]";
250
                foreach ($value as $sectionKey => $sectionValue) {
251
                    $return .= $sectionKey . "=" . $sectionValue . PHP_EOL;
252
                }
253
            }
254
            // add the key/value pair
255
            $return .= $key . "=" . $value . PHP_EOL;
256
        }
257
        // return the string
258
        return $return;
259
    }
260
261
    /**
262
     * Returns all properties with their keys as a String.
263
     *
264
     * @return \AppserverIo\Lang\String String with all key -> properties pairs
265
     */
266
    public function toString()
267
    {
268
        return new Strng($this->__toString());
269
    }
270
271
    /**
272
     * Merges the passed properties into the actual instance. If override
273
     * flag is set to TRUE, existing properties will be overwritten.
274
     *
275
     * @param \AppserverIo\Properties\PropertiesInterface $properties The properties to merge
276
     * @param boolean                                     $override   TRUE if existing properties have to be overwritten, else FALSE
277
     *
278
     * @return void
279
     */
280 2
    public function mergeProperties(PropertiesInterface $properties, $override = false)
281
    {
282
        // iterate over the keys of the passed properties and add thm, or replace existing ones
283 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...
284 2
            if ($this->exists($key) === false || ($this->exists($key) === true && $override === true)) {
285 2
                $this->setProperty($key, $value);
286 2
            }
287 2
        }
288 2
    }
289
290
    /**
291
     * Returns all key values as an array.
292
     *
293
     * @return array The keys as array values
294
     */
295 3
    public function getKeys()
296
    {
297
        // check if the property file is sectioned
298 3
        if ($this->sections) {
299
            // initialize the array for the keys
300 1
            $keys = array();
301
            // iterate over the sections and merge all sectioned keys
302 1
            foreach ($this->items as $item) {
303 1
                $keys = array_merge($keys, array_keys($item));
304 1
            }
305
            // return the keys
306 1
            return $keys;
307
        } else {
308 2
            return array_keys($this->items);
309
        }
310
    }
311
}
312