Passed
Push — main ( 28d658...6dddcc )
by Michiel
06:12
created

Variable::resolveAllProperties()   B

Complexity

Conditions 10
Paths 10

Size

Total Lines 70
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 33
CRAP Score 10.1263

Importance

Changes 0
Metric Value
eloc 37
c 0
b 0
f 0
dl 0
loc 70
ccs 33
cts 37
cp 0.8919
rs 7.6666
cc 10
nc 10
nop 1
crap 10.1263

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
5
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
6
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
7
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
8
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
9
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
10
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
11
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
12
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
13
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
14
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15
 *
16
 * This software consists of voluntary contributions made by many individuals
17
 * and is licensed under the LGPL. For more information please see
18
 * <http://phing.info>.
19
 */
20
21
namespace Phing\Task\System\Property;
22
23
use Exception;
24
use Phing\Exception\BuildException;
25
use Phing\Io\File;
26
use Phing\Io\IOException;
27
use Phing\Project;
28
use Phing\PropertyHelper;
29
use Phing\Task\System\PropertyTask;
30
use Phing\Util\Properties;
31
use ReflectionObject;
32
use ReflectionProperty;
33
34
/**
35
 * Variable Task.
36
 *
37
 * @author  Siad Ardroumli <[email protected]>
38
 */
39
class Variable extends PropertyTask
40
{
41
    private $remove = false;
42
43
    /**
44
     * Determines whether the property should be removed from the project.
45
     * Default is false. Once  removed, conditions that check for property
46
     * existence will find this property does not exist.
47
     *
48
     * @param bool $b set to true to remove the property from the project
49
     */
50
    public function setUnset($b)
51
    {
52
        $this->remove = $b;
53
    }
54
55
    /**
56
     * Execute this task.
57
     *
58
     * @throws BuildException Description of the Exception
59
     */
60 1
    public function main()
61
    {
62 1
        if ($this->remove) {
63
            if (null === $this->name || '' === $this->name) {
64
                throw new BuildException("The 'name' attribute is required with 'unset'.");
65
            }
66
            $this->removeProperty($this->name);
67
68
            return;
69
        }
70 1
        if (null === $this->file) {
71
            // check for the required name attribute
72 1
            if (null === $this->name || '' === $this->name) {
73
                throw new BuildException("The 'name' attribute is required.");
74
            }
75
76
            // adjust the property value if necessary -- is this necessary?
77
            // Doesn't Ant do this automatically?
78 1
            $this->value = $this->getProject()->replaceProperties($this->value);
79
80
            // set the property
81 1
            $this->forceProperty($this->name, $this->value);
82
        } else {
83 1
            if (!$this->file->exists()) {
84
                throw new BuildException($this->file->getAbsolutePath() . ' does not exists.');
85
            }
86 1
            $this->loadFile($this->file);
87
        }
88
    }
89
90
    /**
91
     * load variables from a file.
92
     *
93
     * @param File $file file to load
94
     *
95
     * @throws BuildException
96
     */
97 1
    protected function loadFile(File $file)
98
    {
99 1
        $props = new Properties();
100
101
        try {
102 1
            if ($file->exists()) {
103 1
                $props->load($file);
104
105 1
                $this->addProperties($props);
106
            } else {
107 1
                $this->log(
108 1
                    'Unable to find property file: ' . $file->getAbsolutePath(),
109 1
                    Project::MSG_VERBOSE
110 1
                );
111
            }
112
        } catch (IOException $ex) {
113
            throw new BuildException($ex, $this->getLocation());
114
        }
115
    }
116
117
    /**
118
     * iterate through a set of properties, resolve them, then assign them.
119
     *
120
     * @param Properties $props The feature to be added to the Properties attribute
121
     */
122 1
    protected function addProperties($props)
123
    {
124 1
        $this->resolveAllProperties($props);
125 1
        foreach ($props->keys() as $name) {
126 1
            $this->forceProperty($name, $props->getProperty($name));
127
        }
128
    }
129
130
    /**
131
     * resolve properties inside a properties hashtable.
132
     *
133
     * @param Properties $props properties object to resolve
134
     *
135
     * @throws BuildException Description of the Exception
136
     */
137 1
    protected function resolveAllProperties(Properties $props)
138
    {
139 1
        foreach ($props->keys() as $name) {
140
            // There may be a nice regex/callback way to handle this
141
            // replacement, but at the moment it is pretty complex, and
142
            // would probably be a lot uglier to work into a preg_replace_callback()
143
            // system.  The biggest problem is the fact that a resolution may require
144
            // multiple passes.
145
146 1
            $value = $props->getProperty($name);
147 1
            $resolved = false;
148 1
            $resolveStack = [];
149
150 1
            $ih = PropertyHelper::getPropertyHelper($this->project);
151
152 1
            while (!$resolved) {
153 1
                $fragments = [];
154 1
                $propertyRefs = [];
155
156
                // [HL] this was ::parsePropertyString($this->value ...) ... this seems wrong
157 1
                $ih->parsePropertyString($value, $fragments, $propertyRefs);
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type null; however, parameter $value of Phing\PropertyHelper::parsePropertyString() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

157
                $ih->parsePropertyString(/** @scrutinizer ignore-type */ $value, $fragments, $propertyRefs);
Loading history...
158
159 1
                $resolved = true;
160 1
                if (0 == count($propertyRefs)) {
161 1
                    continue;
162
                }
163
164 1
                $sb = '';
165
166 1
                $j = $propertyRefs;
167
168 1
                foreach ($fragments as $fragment) {
169 1
                    if (null !== $fragment) {
170 1
                        $sb .= $fragment;
171
172 1
                        continue;
173
                    }
174
175 1
                    $propertyName = array_shift($j);
176 1
                    if (in_array($propertyName, $resolveStack)) {
177
                        // Should we maybe just log this as an error & move on?
178
                        // $this->log("Property ".$name." was circularly defined.", Project::MSG_ERR);
179
                        throw new BuildException('Property ' . $propertyName . ' was circularly defined.');
180
                    }
181
182 1
                    $fragment = $this->getProject()->getProperty($propertyName);
183 1
                    if (null !== $fragment) {
184 1
                        $sb .= $fragment;
185
186 1
                        continue;
187
                    }
188
189 1
                    if ($props->containsKey($propertyName)) {
190 1
                        $fragment = $props->getProperty($propertyName);
191 1
                        if (false !== strpos($fragment, '${')) {
0 ignored issues
show
Bug introduced by
It seems like $fragment can also be of type null; however, parameter $haystack of strpos() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

191
                        if (false !== strpos(/** @scrutinizer ignore-type */ $fragment, '${')) {
Loading history...
192
                            $resolveStack[] = $propertyName;
193
                            $resolved = false; // parse again (could have been replaced w/ another var)
194
                        }
195
                    } else {
196
                        $fragment = '${' . $propertyName . '}';
197
                    }
198
199 1
                    $sb .= $fragment;
200
                }
201
202 1
                $this->log("Resolved Property \"{$value}\" to \"{$sb}\"", Project::MSG_DEBUG);
203 1
                $value = $sb;
204 1
                $props->setProperty($name, $value);
205
206 1
                $this->getProject()->setProperty($name, $value);
207
            }
208
        }
209
    }
210
211
    /**
212
     * Remove a property from the project's property table and the userProperty table.
213
     * Note that Ant 1.6 uses a helper for this.
214
     *
215
     * @param mixed $name
216
     */
217
    private function removeProperty($name)
218
    {
219
        $properties = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $properties is dead and can be removed.
Loading history...
220
221
        try {
222
            $properties = $this->getPropValue($this->getProject(), 'properties');
223
            if (null !== $properties) {
224
                unset($properties[$name]);
225
                $this->setPropValue($properties, $this->getProject(), 'properties');
226
            }
227
        } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
228
        }
229
230
        try {
231
            $properties = $this->getPropValue($this->getProject(), 'userProperties');
232
            if (null !== $properties) {
233
                unset($properties[$name]);
234
                $this->setPropValue($properties, $this->getProject(), 'userProperties');
235
            }
236
        } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
237
        }
238
    }
239
240 1
    private function forceProperty($name, $value)
241
    {
242
        try {
243 1
            $properties = $this->getPropValue($this->getProject(), 'properties');
244
            if (null === $properties) {
245
                $this->getProject()->setUserProperty($name, $value);
246
            } else {
247
                $properties[$name] = $value;
248
                $this->setPropValue($properties, $this->getProject(), 'properties');
249
            }
250 1
        } catch (Exception $e) {
251 1
            $this->getProject()->setUserProperty($name, $value);
252
        }
253
    }
254
255
    /**
256
     * Get a private property of a class.
257
     *
258
     * @param mixed  $thisClass The class
259
     * @param string $fieldName The property to get
260
     *
261
     * @throws Exception
262
     *
263
     * @return ReflectionProperty The property value
264
     */
265 1
    private function getField($thisClass, $fieldName)
266
    {
267 1
        $refClazz = new ReflectionObject($thisClass);
268 1
        if (!$refClazz->hasProperty($fieldName)) {
269 1
            throw new Exception("Invalid field : {$fieldName}");
270
        }
271
272
        return $refClazz->getProperty($fieldName);
273
    }
274
275
    /**
276
     * Get a private property of an object.
277
     *
278
     * @param mixed  $instance  the object instance
279
     * @param string $fieldName the name of the field
280
     *
281
     * @throws Exception
282
     *
283
     * @return mixed an object representing the value of the field
284
     */
285 1
    private function getPropValue($instance, $fieldName)
286
    {
287 1
        $field = $this->getField($instance, $fieldName);
288
        $field->setAccessible(true);
289
290
        return $field->getValue($instance);
291
    }
292
293
    private function setPropValue($value, $instance, $fieldName)
294
    {
295
        $field = $this->getField($instance, $fieldName);
296
        $field->setAccessible(true);
297
        $field->setValue($instance, $value);
298
    }
299
}
300