Passed
Push — master ( a862c8...fef60d )
by Siad
05:54
created

Variable::loadFile()   A

Complexity

Conditions 3
Paths 6

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.576

Importance

Changes 0
Metric Value
cc 3
eloc 11
c 0
b 0
f 0
nc 6
nop 1
dl 0
loc 16
ccs 6
cts 10
cp 0.6
crap 3.576
rs 9.9
1
<?php
2
/**
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the LGPL. For more information please see
17
 * <http://phing.info>.
18
 */
19
20
/**
21
 * Variable Task.
22
 *
23
 * @author  Siad Ardroumli <[email protected]>
24
 * @package phing.tasks.ext.property
25
 */
26
class Variable extends PropertyTask
27
{
28
    private $remove = false;
29
30
    /**
31
     * Determines whether the property should be removed from the project.
32
     * Default is false. Once  removed, conditions that check for property
33
     * existence will find this property does not exist.
34
     *
35
     * @param boolean $b set to true to remove the property from the project.
36
     */
37
    public function setUnset($b)
38
    {
39
        $this->remove = $b;
40
    }
41
42
    /**
43
     * Execute this task.
44
     *
45
     * @throws BuildException  Description of the Exception
46
     */
47 1
    public function main()
48
    {
49 1
        if ($this->remove) {
50
            if ($this->name === null || $this->name === "") {
51
                throw new BuildException("The 'name' attribute is required with 'unset'.");
52
            }
53
            $this->removeProperty($this->name);
54
            return;
55
        }
56 1
        if ($this->file === null) {
57
            // check for the required name attribute
58 1
            if ($this->name === null || $this->name === '') {
59
                throw new BuildException("The 'name' attribute is required.");
60
            }
61
62
            // adjust the property value if necessary -- is this necessary?
63
            // Doesn't Ant do this automatically?
64 1
            $this->value = $this->getProject()->replaceProperties($this->value);
65
66
            // set the property
67 1
            $this->forceProperty($this->name, $this->value);
68
        } else {
69 1
            if (!$this->file->exists()) {
70
                throw new BuildException($this->file->getAbsolutePath() . " does not exists.");
71
            }
72 1
            $this->loadFile($this->file);
73
        }
74 1
    }
75
76
    /**
77
     * Remove a property from the project's property table and the userProperty table.
78
     * Note that Ant 1.6 uses a helper for this.
79
     */
80
    private function removeProperty($name)
81
    {
82
        $properties = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $properties is dead and can be removed.
Loading history...
83
        try {
84
            $properties = $this->getPropValue($this->getProject(), 'properties');
85
            if ($properties !== null) {
86
                unset($properties[$name]);
87
                $this->setPropValue($properties, $this->getProject(), 'properties');
88
            }
89
        } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
90
        }
91
        try {
92
            $properties = $this->getPropValue($this->getProject(), 'userProperties');
93
            if ($properties !== null) {
94
                unset($properties[$name]);
95
                $this->setPropValue($properties, $this->getProject(), 'userProperties');
96
            }
97
        } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
98
        }
99
    }
100
101 1
    private function forceProperty($name, $value)
102
    {
103
        try {
104 1
            $properties = $this->getPropValue($this->getProject(), 'properties');
105
            if ($properties === null) {
106
                $this->getProject()->setUserProperty($name, $value);
107
            } else {
108
                $properties[$name] = $value;
109
                $this->setPropValue($properties, $this->getProject(), 'properties');
110
            }
111 1
        } catch (Exception $e) {
112 1
            $this->getProject()->setUserProperty($name, $value);
113
        }
114 1
    }
115
116
    /**
117
     * Get a private property of a class
118
     *
119
     * @param  mixed $thisClass The class
120
     * @param  string $fieldName The property to get
121
     * @return ReflectionProperty               The property value
122
     * @throws Exception
123
     */
124 1
    private function getField($thisClass, $fieldName)
125
    {
126 1
        $refClazz = new ReflectionObject($thisClass);
127 1
        if (!$refClazz->hasProperty($fieldName)) {
128 1
            throw new Exception("Invalid field : $fieldName");
129
        }
130
131
        return $refClazz->getProperty($fieldName);
132
    }
133
134
    /**
135
     * Get a private property of an object
136
     *
137
     * @param  mixed $instance the object instance
138
     * @param  string $fieldName the name of the field
139
     * @return mixed an object representing the value of the field
140
     * @throws Exception
141
     */
142 1
    private function getPropValue($instance, $fieldName)
143
    {
144 1
        $field = $this->getField($instance, $fieldName);
145
        $field->setAccessible(true);
146
        return $field->getValue($instance);
147
    }
148
149
    private function setPropValue($value, $instance, $fieldName)
150
    {
151
        $field = $this->getField($instance, $fieldName);
152
        $field->setAccessible(true);
153
        $field->setValue($instance, $value);
154
    }
155
156
    /**
157
     * load variables from a file
158
     *
159
     * @param  PhingFile $file file to load
160
     * @throws BuildException
161
     */
162 1
    protected function loadFile(PhingFile $file)
163
    {
164 1
        $props = new Properties();
165
        try {
166 1
            if ($file->exists()) {
167 1
                $props->load($file);
168
169 1
                $this->addProperties($props);
170
            } else {
171
                $this->log(
172
                    'Unable to find property file: ' . $file->getAbsolutePath(),
173 1
                    Project::MSG_VERBOSE
174
                );
175
            }
176
        } catch (IOException $ex) {
177
            throw new BuildException($ex, $this->getLocation());
178
        }
179 1
    }
180
181
    /**
182
     * iterate through a set of properties, resolve them, then assign them
183
     *
184
     * @param Properties $props The feature to be added to the Properties attribute
185
     */
186 1
    protected function addProperties($props)
187
    {
188 1
        $this->resolveAllProperties($props);
189 1
        foreach ($props->keys() as $name) {
190 1
            $this->forceProperty($name, $props->getProperty($name));
191
        }
192 1
    }
193
194
    /**
195
     * resolve properties inside a properties hashtable
196
     *
197
     * @param  Properties $props properties object to resolve
198
     * @throws BuildException  Description of the Exception
199
     */
200 1
    protected function resolveAllProperties(Properties $props)
201
    {
202 1
        foreach ($props->keys() as $name) {
203
            // There may be a nice regex/callback way to handle this
204
            // replacement, but at the moment it is pretty complex, and
205
            // would probably be a lot uglier to work into a preg_replace_callback()
206
            // system.  The biggest problem is the fact that a resolution may require
207
            // multiple passes.
208
209 1
            $value = $props->getProperty($name);
210 1
            $resolved = false;
211 1
            $resolveStack = array();
212
213 1
            $ih = PropertyHelper::getPropertyHelper($this->project);
214
215 1
            while (!$resolved) {
216 1
                $fragments = array();
217 1
                $propertyRefs = array();
218
219
                // [HL] this was ::parsePropertyString($this->value ...) ... this seems wrong
220 1
                $ih->parsePropertyString($value, $fragments, $propertyRefs);
221
222 1
                $resolved = true;
223 1
                if (count($propertyRefs) == 0) {
224 1
                    continue;
225
                }
226
227 1
                $sb = "";
228
229 1
                $j = $propertyRefs;
230
231 1
                foreach ($fragments as $fragment) {
232 1
                    if ($fragment !== null) {
233 1
                        $sb .= $fragment;
234 1
                        continue;
235
                    }
236
237 1
                    $propertyName = array_shift($j);
238 1
                    if (in_array($propertyName, $resolveStack)) {
239
                        // Should we maybe just log this as an error & move on?
240
                        // $this->log("Property ".$name." was circularly defined.", Project::MSG_ERR);
241
                        throw new BuildException("Property " . $propertyName . " was circularly defined.");
242
                    }
243
244 1
                    $fragment = $this->getProject()->getProperty($propertyName);
245 1
                    if ($fragment !== null) {
246 1
                        $sb .= $fragment;
247 1
                        continue;
248
                    }
249
250 1
                    if ($props->containsKey($propertyName)) {
251 1
                        $fragment = $props->getProperty($propertyName);
252 1
                        if (strpos($fragment, '${') !== false) {
253
                            $resolveStack[] = $propertyName;
254 1
                            $resolved = false; // parse again (could have been replaced w/ another var)
255
                        }
256
                    } else {
257
                        $fragment = "\${" . $propertyName . "}";
258
                    }
259
260 1
                    $sb .= $fragment;
261
                }
262
263 1
                $this->log("Resolved Property \"$value\" to \"$sb\"", Project::MSG_DEBUG);
264 1
                $value = $sb;
265 1
                $props->setProperty($name, $value);
266
267 1
                $this->getProject()->setProperty($name, $value);
268
            }
269
        }
270 1
    }
271
}
272