Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
 
3
/*
4
 *  $Id: PropertyTask.php 144 2007-02-05 15:19:00Z hans $
5
 *
6
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
7
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
8
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
9
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
10
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
11
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
12
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
13
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
14
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
15
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
16
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17
 *
18
 * This software consists of voluntary contributions made by many individuals
19
 * and is licensed under the LGPL. For more information please see
20
 * <http://phing.info>.
21
 */
22
 
23
include_once 'phing/Task.php';
24
include_once 'phing/system/util/Properties.php';
25
 
26
/**
27
 * Task for setting properties in buildfiles.
28
 *
29
 * @author    Andreas Aderhold <andi@binarycloud.com>
30
 * @author    Hans Lellelid <hans@xmpl.org>
31
 * @version   $Revision$
32
 * @package   phing.tasks.system
33
 */
34
class PropertyTask extends Task {
35
 
36
    /** name of the property */
37
    protected $name;
38
 
39
    /** value of the property */
40
    protected $value;
41
 
42
    protected $reference;
43
    protected $env;     // Environment
44
    protected $file;
45
    protected $ref;
46
    protected $prefix;
47
    protected $fallback;
48
 
49
    /** Whether to force overwrite of existing property. */
50
    protected $override = false;
51
 
52
    /** Whether property should be treated as "user" property. */
53
    protected $userProperty = false;
54
 
55
    /**
56
     * Sets a the name of current property component
57
     */
58
    function setName($name) {
59
        $this->name = (string) $name;
60
    }
61
 
62
    /** Get property component name. */
63
    function getName() {
64
        return $this->name;
65
    }
66
 
67
    /**
68
     * Sets a the value of current property component.
69
     * @param    mixed      Value of name, all scalars allowed
70
     */
71
    function setValue($value) {
72
        $this->value = (string) $value;
73
    }
74
 
75
	/**
76
	 * Sets value of property to CDATA tag contents.
77
	 * @param string $values
78
	 * @since 2.2.0
79
	 */
80
	public function addText($value) {
81
		$this->setValue($value);
82
	}
83
 
84
    /** Get the value of current property component. */
85
    function getValue() {
86
        return $this->value;
87
    }
88
 
89
    /** Set a file to use as the source for properties. */
90
    function setFile($file) {
91
        if (is_string($file)) {
92
            $file = new PhingFile($file);
93
        }
94
        $this->file = $file;
95
    }
96
 
97
    /** Get the PhingFile that is being used as property source. */
98
    function getFile() {
99
        return $this->file;
100
    }
101
 
102
    function setRefid(Reference $ref) {
103
        $this->reference = $ref;
104
    }
105
 
106
    function getRefid() {
107
        return $this->reference;
108
    }
109
 
110
    /**
111
     * Prefix to apply to properties loaded using <code>file</code>.
112
     * A "." is appended to the prefix if not specified.
113
     * @param string $prefix prefix string
114
     * @return void
115
     * @since 2.0
116
     */
117
    public function setPrefix($prefix) {
118
        $this->prefix = $prefix;
119
        if (!StringHelper::endsWith(".", $prefix)) {
120
            $this->prefix .= ".";
121
        }
122
    }
123
 
124
    /**
125
     * @return string
126
     * @since 2.0
127
     */
128
    public function getPrefix() {
129
        return $this->prefix;
130
    }
131
 
132
    /**
133
    * the prefix to use when retrieving environment variables.
134
    * Thus if you specify environment="myenv"
135
    * you will be able to access OS-specific
136
    * environment variables via property names "myenv.PATH" or
137
    * "myenv.TERM".
138
    * <p>
139
    * Note that if you supply a property name with a final
140
    * "." it will not be doubled. ie environment="myenv." will still
141
    * allow access of environment variables through "myenv.PATH" and
142
    * "myenv.TERM". This functionality is currently only implemented
143
    * on select platforms. Feel free to send patches to increase the number of platforms
144
    * this functionality is supported on ;).<br>
145
    * Note also that properties are case sensitive, even if the
146
    * environment variables on your operating system are not, e.g. it
147
    * will be ${env.Path} not ${env.PATH} on Windows 2000.
148
    * @param env prefix
149
    */
150
    function setEnvironment($env) {
151
        $this->env = (string) $env;
152
    }
153
 
154
    function getEnvironment() {
155
        return $this->env;
156
    }
157
 
158
    /**
159
     * Set whether this is a user property (ro).
160
     * This is deprecated in Ant 1.5, but the userProperty attribute
161
     * of the class is still being set via constructor, so Phing will
162
     * allow this method to function.
163
     * @param boolean $v
164
     */
165
    function setUserProperty($v) {
166
        $this->userProperty = (boolean) $v;
167
    }
168
 
169
    function getUserProperty() {
170
        return $this->userProperty;
171
    }
172
 
173
    function setOverride($v) {
174
        $this->override = (boolean) $v;
175
    }
176
 
177
    function getOverride() {
178
        return $this->override;
179
    }
180
 
181
    function toString() {
182
        return (string) $this->value;
183
    }
184
 
185
	/**
186
	 * @param Project $p
187
	 */
188
    function setFallback($p) {
189
        $this->fallback = $p;
190
    }
191
 
192
    function getFallback() {
193
        return $this->fallback;
194
    }
195
    /**
196
     * set the property in the project to the value.
197
     * if the task was give a file or env attribute
198
     * here is where it is loaded
199
     */
200
    function main() {
201
        if ($this->name !== null) {
202
            if ($this->value === null && $this->ref === null) {
203
                throw new BuildException("You must specify value or refid with the name attribute", $this->getLocation());
204
            }
205
        } else {
206
            if ($this->file === null && $this->env === null ) {
207
                throw new BuildException("You must specify file or environment when not using the name attribute", $this->getLocation());
208
            }
209
        }
210
 
211
        if ($this->file === null && $this->prefix !== null) {
212
            throw new BuildException("Prefix is only valid when loading from a file.", $this->getLocation());
213
        }
214
 
215
        if (($this->name !== null) && ($this->value !== null)) {
216
            $this->addProperty($this->name, $this->value);
217
        }
218
 
219
        if ($this->file !== null) {
220
            $this->loadFile($this->file);
221
        }
222
 
223
        if ( $this->env !== null ) {
224
            $this->loadEnvironment($this->env);
225
        }
226
 
227
        if (($this->name !== null) && ($this->ref !== null)) {
228
            // get the refereced property
229
            try {
230
            $this->addProperty($this->name, $this->reference->getReferencedObject($this->project)->toString());
231
            } catch (BuildException $be) {
232
                if ($this->fallback !== null) {
233
                     $this->addProperty($this->name, $this->reference->getReferencedObject($this->fallback)->toString());
234
                } else {
235
                    throw $be;
236
                }
237
            }
238
        }
239
    }
240
 
241
    /**
242
     * load the environment values
243
     * @param string $prefix prefix to place before them
244
     */
245
    protected function loadEnvironment($prefix) {
246
 
247
        $props = new Properties();
248
        if ( substr($prefix, strlen($prefix)-1) == '.' ) {
249
            $prefix .= ".";
250
        }
251
        $this->log("Loading Environment $prefix", Project::MSG_VERBOSE);
252
        foreach($_ENV as $key => $value) {
253
            $props->setProperty($prefix . '.' . $key, $value);
254
        }
255
        $this->addProperties($props);
256
    }
257
 
258
    /**
259
     * iterate through a set of properties,
260
     * resolve them then assign them
261
     */
262
    protected function addProperties($props) {
263
        $this->resolveAllProperties($props);
264
        foreach($props->keys() as $name) {
265
            $value = $props->getProperty($name);
266
            $v = $this->project->replaceProperties($value);
267
            if ($this->prefix !== null) {
268
                $name = $this->prefix . $name;
269
            }
270
            $this->addProperty($name, $v);
271
        }
272
    }
273
 
274
    /**
275
     * add a name value pair to the project property set
276
     * @param string $name name of property
277
     * @param string $value value to set
278
     */
279
    protected function addProperty($name, $value) {
280
        if ($this->userProperty) {
281
            if ($this->project->getUserProperty($name) === null || $this->override) {
282
                $this->project->setInheritedProperty($name, $value);
283
            } else {
284
                $this->log("Override ignored for " . $name, Project::MSG_VERBOSE);
285
            }
286
        } else {
287
            if ($this->override) {
288
                $this->project->setProperty($name, $value);
289
            } else {
290
                $this->project->setNewProperty($name, $value);
291
            }
292
        }
293
    }
294
 
295
    /**
296
     * load properties from a file.
297
     * @param PhingFile $file
298
     */
299
    protected function loadFile(PhingFile $file) {
300
        $props = new Properties();
301
        $this->log("Loading ". $file->getAbsolutePath(), Project::MSG_INFO);
302
        try { // try to load file
303
            if ($file->exists()) {
304
                $props->load($file);
305
                $this->addProperties($props);
306
            } else {
307
                $this->log("Unable to find property file: ". $file->getAbsolutePath() ."... skipped", Project::MSG_WARN);
308
            }
309
        } catch (IOException $ioe) {
310
            throw new BuildException("Could not load properties from file.", $ioe);
311
        }
312
    }
313
 
314
    /**
315
     * Given a Properties object, this method goes through and resolves
316
     * any references to properties within the object.
317
     *
318
     * @param Properties $props The collection of Properties that need to be resolved.
319
     * @return void
320
     */
321
    protected function resolveAllProperties(Properties $props) {
322
 
323
        $keys = $props->keys();
324
 
325
        while(count($keys)) {
326
 
327
            // There may be a nice regex/callback way to handle this
328
            // replacement, but at the moment it is pretty complex, and
329
            // would probably be a lot uglier to work into a preg_replace_callback()
330
            // system.  The biggest problem is the fact that a resolution may require
331
            // multiple passes.
332
 
333
            $name     = array_shift($keys);
334
            $value    = $props->getProperty($name);
335
            $resolved = false;
336
 
337
            while(!$resolved) {
338
 
339
                $fragments = array();
340
                $propertyRefs = array();
341
 
342
                // [HL] this was ::parsePropertyString($this->value ...) ... this seems wrong
343
                self::parsePropertyString($value, $fragments, $propertyRefs);
344
 
345
                $resolved = true;
346
                if (count($propertyRefs) !== 0) {
347
 
348
                    $sb = "";
349
 
350
                    $i = $fragments;
351
                    $j = $propertyRefs;
352
                    while(count($i)) {
353
                        $fragment = array_shift($i);
354
                        if ($fragment === null) {
355
                            $propertyName = array_shift($j);
356
 
357
                            if ($propertyName === $name) {
358
                                // Should we maybe just log this as an error & move on?
359
                                // $this->log("Property ".$name." was circularly defined.", Project::MSG_ERR);
360
                                throw new BuildException("Property ".$name." was circularly defined.");
361
                            }
362
 
363
                            $fragment = $this->getProject()->getProperty($propertyName);
364
                            if ($fragment === null) {
365
                                if ($props->containsKey($propertyName)) {
366
                                    $fragment = $props->getProperty($propertyName);
367
                                    $resolved = false; // parse again (could have been replaced w/ another var)
368
                                } else {
369
                                    $fragment = "\${".$propertyName."}";
370
                                }
371
                            }
372
                        }
373
                        $sb .= $fragment;
374
                    }
375
 
376
                    $this->log("Resolved Property \"$value\" to \"$sb\"", Project::MSG_DEBUG);
377
                    $value = $sb;
378
                    $props->setProperty($name, $value);
379
 
380
                } // if (count($propertyRefs))
381
 
382
            } // while (!$resolved)
383
 
384
        } // while (count($keys)
385
    }
386
 
387
 
388
     /**
389
     * This method will parse a string containing ${value} style
390
     * property values into two lists. The first list is a collection
391
     * of text fragments, while the other is a set of string property names
392
     * null entries in the first list indicate a property reference from the
393
     * second list.
394
     *
395
     * This is slower than regex, but useful for this class, which has to handle
396
     * multiple parsing passes for properties.
397
     *
398
     * @param string $value The string to be scanned for property references
399
     * @param array &$fragments The found fragments
400
     * @param  array &$propertyRefs The found refs
401
     */
402
    protected function parsePropertyString($value, &$fragments, &$propertyRefs) {
403
 
404
        $prev = 0;
405
        $pos  = 0;
406
 
407
        while (($pos = strpos($value, '$', $prev)) !== false) {
408
 
409
            if ($pos > $prev) {
410
                array_push($fragments, StringHelper::substring($value, $prev, $pos-1));
411
            }
412
            if ($pos === (strlen($value) - 1)) {
413
                array_push($fragments, '$');
414
                $prev = $pos + 1;
415
            } elseif ($value{$pos+1} !== '{' ) {
416
 
417
                // the string positions were changed to value-1 to correct
418
                // a fatal error coming from function substring()
419
                array_push($fragments, StringHelper::substring($value, $pos, $pos + 1));
420
                $prev = $pos + 2;
421
            } else {
422
                $endName = strpos($value, '}', $pos);
423
                if ($endName === false) {
424
                    throw new BuildException("Syntax error in property: $value");
425
                }
426
                $propertyName = StringHelper::substring($value, $pos + 2, $endName-1);
427
                array_push($fragments, null);
428
                array_push($propertyRefs, $propertyName);
429
                $prev = $endName + 1;
430
            }
431
        }
432
 
433
        if ($prev < strlen($value)) {
434
            array_push($fragments, StringHelper::substring($value, $prev));
435
        }
436
    }
437
 
438
}