| 1 |
lars |
1 |
<?php
|
|
|
2 |
|
|
|
3 |
/*
|
|
|
4 |
* $Id: IntrospectionHelper.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/types/Reference.php';
|
|
|
24 |
include_once 'phing/types/Path.php';
|
|
|
25 |
include_once 'phing/util/StringHelper.php';
|
|
|
26 |
|
|
|
27 |
/**
|
|
|
28 |
* Helper class that collects the methods that a task or nested element
|
|
|
29 |
* holds to set attributes, create nested elements or hold PCDATA
|
|
|
30 |
* elements.
|
|
|
31 |
*
|
|
|
32 |
*<ul>
|
|
|
33 |
* <li><strong>SMART-UP INLINE DOCS</strong></li>
|
|
|
34 |
* <li><strong>POLISH-UP THIS CLASS</strong></li>
|
|
|
35 |
*</ul>
|
|
|
36 |
*
|
|
|
37 |
* @author Andreas Aderhold <andi@binarycloud.com>
|
|
|
38 |
* @author Hans Lellelid <hans@xmpl.org>
|
|
|
39 |
* @copyright © 2001,2002 THYRELL. All rights reserved
|
|
|
40 |
* @version $Revision: 1.19 $
|
|
|
41 |
* @package phing
|
|
|
42 |
*/
|
|
|
43 |
class IntrospectionHelper {
|
|
|
44 |
|
|
|
45 |
|
|
|
46 |
|
|
|
47 |
/**
|
|
|
48 |
* Holds the attribute setter methods.
|
|
|
49 |
*
|
|
|
50 |
* @var array string[]
|
|
|
51 |
*/
|
|
|
52 |
private $attributeSetters = array();
|
|
|
53 |
|
|
|
54 |
/**
|
|
|
55 |
* Holds methods to create nested elements.
|
|
|
56 |
*
|
|
|
57 |
* @var array string[]
|
|
|
58 |
*/
|
|
|
59 |
private $nestedCreators = array();
|
|
|
60 |
|
|
|
61 |
/**
|
|
|
62 |
* Holds methods to store configured nested elements.
|
|
|
63 |
*
|
|
|
64 |
* @var array string[]
|
|
|
65 |
*/
|
|
|
66 |
private $nestedStorers = array();
|
|
|
67 |
|
|
|
68 |
/**
|
|
|
69 |
* Map from attribute names to nested types.
|
|
|
70 |
*/
|
|
|
71 |
private $nestedTypes = array();
|
|
|
72 |
|
|
|
73 |
/**
|
|
|
74 |
* New idea in phing: any class can register certain
|
|
|
75 |
* keys -- e.g. "task.current_file" -- which can be used in
|
|
|
76 |
* task attributes, if supported. In the build XML these
|
|
|
77 |
* are referred to like this:
|
|
|
78 |
* <regexp pattern="\n" replace="%{task.current_file}"/>
|
|
|
79 |
* In the type/task a listener method must be defined:
|
|
|
80 |
* function setListeningReplace($slot) {}
|
|
|
81 |
* @var array string[]
|
|
|
82 |
*/
|
|
|
83 |
private $slotListeners = array();
|
|
|
84 |
|
|
|
85 |
/**
|
|
|
86 |
* The method to add PCDATA stuff.
|
|
|
87 |
*
|
|
|
88 |
* @var string Method name of the addText (redundant?) method, if class supports it :)
|
|
|
89 |
*/
|
|
|
90 |
private $methodAddText = null;
|
|
|
91 |
|
|
|
92 |
/**
|
|
|
93 |
* The Class that's been introspected.
|
|
|
94 |
*
|
|
|
95 |
* @var object
|
|
|
96 |
* @access private
|
|
|
97 |
*/
|
|
|
98 |
private $bean;
|
|
|
99 |
|
|
|
100 |
/**
|
|
|
101 |
* The cache of IntrospectionHelper classes instantiated by getHelper().
|
|
|
102 |
* @var array IntrospectionHelpers[]
|
|
|
103 |
*/
|
|
|
104 |
private static $helpers = array();
|
|
|
105 |
|
|
|
106 |
/**
|
|
|
107 |
* Factory method for helper objects.
|
|
|
108 |
*
|
|
|
109 |
* @param string $class The class to create a Helper for
|
|
|
110 |
*/
|
|
|
111 |
public static function getHelper($class) {
|
|
|
112 |
if (!isset(self::$helpers[$class])) {
|
|
|
113 |
self::$helpers[$class] = new IntrospectionHelper($class);
|
|
|
114 |
}
|
|
|
115 |
return self::$helpers[$class];
|
|
|
116 |
}
|
|
|
117 |
|
|
|
118 |
/**
|
|
|
119 |
* This function constructs a new introspection helper for a specific class.
|
|
|
120 |
*
|
|
|
121 |
* This method loads all methods for the specified class and categorizes them
|
|
|
122 |
* as setters, creators, slot listeners, etc. This way, the setAttribue() doesn't
|
|
|
123 |
* need to perform any introspection -- either the requested attribute setter/creator
|
|
|
124 |
* exists or it does not & a BuildException is thrown.
|
|
|
125 |
*
|
|
|
126 |
* @param string $bean The classname for this IH.
|
|
|
127 |
*/
|
|
|
128 |
function __construct($class) {
|
|
|
129 |
|
|
|
130 |
$this->bean = new ReflectionClass($class);
|
|
|
131 |
|
|
|
132 |
//$methods = get_class_methods($bean);
|
|
|
133 |
foreach($this->bean->getMethods() as $method) {
|
|
|
134 |
|
|
|
135 |
if ($method->isPublic()) {
|
|
|
136 |
|
|
|
137 |
// We're going to keep case-insensitive method names
|
|
|
138 |
// for as long as we're allowed :) It makes it much
|
|
|
139 |
// easier to map XML attributes to PHP class method names.
|
|
|
140 |
$name = strtolower($method->getName());
|
|
|
141 |
|
|
|
142 |
// There are a few "reserved" names that might look like attribute setters
|
|
|
143 |
// but should actually just be skipped. (Note: this means you can't ever
|
|
|
144 |
// have an attribute named "location" or "tasktype" or a nested element named "task".)
|
|
|
145 |
if ($name === "setlocation" || $name === "settasktype" || $name === "addtask") {
|
|
|
146 |
continue;
|
|
|
147 |
}
|
|
|
148 |
|
|
|
149 |
if ($name === "addtext") {
|
|
|
150 |
|
|
|
151 |
$this->methodAddText = $method;
|
|
|
152 |
|
|
|
153 |
} elseif (strpos($name, "setlistening") === 0) {
|
|
|
154 |
|
|
|
155 |
// Phing supports something unique called "RegisterSlots"
|
|
|
156 |
// These are dynamic values that use a basic slot system so that
|
|
|
157 |
// classes can register to listen to specific slots, and the value
|
|
|
158 |
// will always be grabbed from the slot (and never set in the project
|
|
|
159 |
// component). This is useful for things like tracking the current
|
|
|
160 |
// file being processed by a filter (e.g. AppendTask sets an append.current_file
|
|
|
161 |
// slot, which can be ready by the XSLTParam type.)
|
|
|
162 |
|
|
|
163 |
if (count($method->getParameters()) !== 1) {
|
|
|
164 |
throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() must take exactly one parameter.");
|
|
|
165 |
}
|
|
|
166 |
|
|
|
167 |
$this->slotListeners[$name] = $method;
|
|
|
168 |
|
|
|
169 |
} elseif (strpos($name, "set") === 0) {
|
|
|
170 |
|
|
|
171 |
// A standard attribute setter.
|
|
|
172 |
|
|
|
173 |
if (count($method->getParameters()) !== 1) {
|
|
|
174 |
throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() must take exactly one parameter.");
|
|
|
175 |
}
|
|
|
176 |
|
|
|
177 |
$this->attributeSetters[$name] = $method;
|
|
|
178 |
|
|
|
179 |
} elseif (strpos($name, "create") === 0) {
|
|
|
180 |
|
|
|
181 |
if (count($method->getParameters()) > 0) {
|
|
|
182 |
throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() may not take any parameters.");
|
|
|
183 |
}
|
|
|
184 |
|
|
|
185 |
// Because PHP doesn't support return types, we are going to do
|
|
|
186 |
// two things here to guess return type:
|
|
|
187 |
// 1) parse comments for an explicit value
|
|
|
188 |
// 2) if that fails, assume that the part of the method after "create"
|
|
|
189 |
// is the name of the return type (in many cases it is not)
|
|
|
190 |
|
|
|
191 |
// This isn't super important -- i.e. we're not instantaiting classes
|
|
|
192 |
// based on this information. It's more just so that IntrospectionHelper
|
|
|
193 |
// can keep track of all the nested types -- and provide more helpful
|
|
|
194 |
// exception messages, etc.
|
|
|
195 |
|
|
|
196 |
preg_match('/@return[\s]+([\w]+)/', $method->getDocComment(), $matches);
|
|
|
197 |
if (!empty($matches[1]) && class_exists($matches[1], false)) {
|
|
|
198 |
$this->nestedTypes[$name] = $matches[1];
|
|
|
199 |
} else {
|
|
|
200 |
// assume that method createEquals() creates object of type "Equals"
|
|
|
201 |
// (that example would be false, of course)
|
|
|
202 |
$this->nestedTypes[$name] = $this->getPropertyName($name, "create");
|
|
|
203 |
}
|
|
|
204 |
|
|
|
205 |
$this->nestedCreators[$name] = $method;
|
|
|
206 |
|
|
|
207 |
} elseif (strpos($name, "addconfigured") === 0) {
|
|
|
208 |
|
|
|
209 |
// *must* use class hints if using addConfigured ...
|
|
|
210 |
|
|
|
211 |
// 1 param only
|
|
|
212 |
$params = $method->getParameters();
|
|
|
213 |
|
|
|
214 |
if (count($params) < 1) {
|
|
|
215 |
throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() must take at least one parameter.");
|
|
|
216 |
}
|
|
|
217 |
|
|
|
218 |
if (count($params) > 1) {
|
|
|
219 |
$this->warn($method->getDeclaringClass()->getName()."::".$method->getName()."() takes more than one parameter. (IH only uses the first)");
|
|
|
220 |
}
|
|
|
221 |
|
|
|
222 |
$classname = null;
|
|
|
223 |
|
|
|
224 |
if (($hint = $params[0]->getClass()) !== null) {
|
|
|
225 |
$classname = $hint->getName();
|
|
|
226 |
}
|
|
|
227 |
|
|
|
228 |
if ($classname === null) {
|
|
|
229 |
throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() method MUST use a class hint to indicate the class type of parameter.");
|
|
|
230 |
}
|
|
|
231 |
|
|
|
232 |
$this->nestedTypes[$name] = $classname;
|
|
|
233 |
|
|
|
234 |
$this->nestedStorers[$name] = $method;
|
|
|
235 |
|
|
|
236 |
} elseif (strpos($name, "add") === 0) {
|
|
|
237 |
|
|
|
238 |
// *must* use class hints if using add ...
|
|
|
239 |
|
|
|
240 |
// 1 param only
|
|
|
241 |
$params = $method->getParameters();
|
|
|
242 |
if (count($params) < 1) {
|
|
|
243 |
throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() must take at least one parameter.");
|
|
|
244 |
}
|
|
|
245 |
|
|
|
246 |
if (count($params) > 1) {
|
|
|
247 |
$this->warn($method->getDeclaringClass()->getName()."::".$method->getName()."() takes more than one parameter. (IH only uses the first)");
|
|
|
248 |
}
|
|
|
249 |
|
|
|
250 |
$classname = null;
|
|
|
251 |
|
|
|
252 |
if (($hint = $params[0]->getClass()) !== null) {
|
|
|
253 |
$classname = $hint->getName();
|
|
|
254 |
}
|
|
|
255 |
|
|
|
256 |
// we don't use the classname here, but we need to make sure it exists before
|
|
|
257 |
// we later try to instantiate a non-existant class
|
|
|
258 |
if ($classname === null) {
|
|
|
259 |
throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() method MUST use a class hint to indicate the class type of parameter.");
|
|
|
260 |
}
|
|
|
261 |
|
|
|
262 |
$this->nestedCreators[$name] = $method;
|
|
|
263 |
}
|
|
|
264 |
} // if $method->isPublic()
|
|
|
265 |
} // foreach
|
|
|
266 |
}
|
|
|
267 |
|
|
|
268 |
|
|
|
269 |
/** Sets the named attribute. */
|
|
|
270 |
function setAttribute(Project $project, $element, $attributeName, &$value) {
|
|
|
271 |
|
|
|
272 |
// we want to check whether the value we are setting looks like
|
|
|
273 |
// a slot-listener variable: %{task.current_file}
|
|
|
274 |
//
|
|
|
275 |
// slot-listener variables are not like properties, in that they cannot be mixed with
|
|
|
276 |
// other text values. The reason for this disparity is that properties are only
|
|
|
277 |
// set when first constructing objects from XML, whereas slot-listeners are always dynamic.
|
|
|
278 |
//
|
|
|
279 |
// This is made possible by PHP5 (objects automatically passed by reference) and PHP's loose
|
|
|
280 |
// typing.
|
|
|
281 |
|
|
|
282 |
if (StringHelper::isSlotVar($value)) {
|
|
|
283 |
|
|
|
284 |
$as = "setlistening" . strtolower($attributeName);
|
|
|
285 |
|
|
|
286 |
if (!isset($this->slotListeners[$as])) {
|
|
|
287 |
$msg = $this->getElementName($project, $element) . " doesn't support a slot-listening '$attributeName' attribute.";
|
|
|
288 |
throw new BuildException($msg);
|
|
|
289 |
}
|
|
|
290 |
|
|
|
291 |
$method = $this->slotListeners[$as];
|
|
|
292 |
|
|
|
293 |
$key = StringHelper::slotVar($value);
|
|
|
294 |
$value = Register::getSlot($key); // returns a RegisterSlot object which will hold current value of that register (accessible using getValue())
|
|
|
295 |
|
|
|
296 |
} else {
|
|
|
297 |
|
|
|
298 |
// Traditional value options
|
|
|
299 |
|
|
|
300 |
$as = "set".strtolower($attributeName);
|
|
|
301 |
|
|
|
302 |
if (!isset($this->attributeSetters[$as])) {
|
|
|
303 |
$msg = $this->getElementName($project, $element) . " doesn't support the '$attributeName' attribute.";
|
|
|
304 |
throw new BuildException($msg);
|
|
|
305 |
}
|
|
|
306 |
|
|
|
307 |
$method = $this->attributeSetters[$as];
|
|
|
308 |
|
|
|
309 |
if ($as == "setrefid") {
|
|
|
310 |
$value = new Reference($value);
|
|
|
311 |
} else {
|
|
|
312 |
|
|
|
313 |
// decode any html entities in string
|
|
|
314 |
$value = html_entity_decode($value);
|
|
|
315 |
|
|
|
316 |
// value is a string representation of a boolean type,
|
|
|
317 |
// convert it to primitive
|
|
|
318 |
if (StringHelper::isBoolean($value)) {
|
|
|
319 |
|
|
|
320 |
$value = StringHelper::booleanValue($value);
|
|
|
321 |
}
|
|
|
322 |
|
|
|
323 |
// does method expect a PhingFile object? if so, then
|
|
|
324 |
// pass a project-relative file.
|
|
|
325 |
$params = $method->getParameters();
|
|
|
326 |
|
|
|
327 |
$classname = null;
|
|
|
328 |
|
|
|
329 |
if (($hint = $params[0]->getClass()) !== null) {
|
|
|
330 |
$classname = $hint->getName();
|
|
|
331 |
}
|
|
|
332 |
|
|
|
333 |
// there should only be one param; we'll just assume ....
|
|
|
334 |
if ($classname !== null) {
|
|
|
335 |
switch(strtolower($classname)) {
|
|
|
336 |
case "phingfile":
|
|
|
337 |
$value = $project->resolveFile($value);
|
|
|
338 |
break;
|
|
|
339 |
case "path":
|
|
|
340 |
$value = new Path($project, $value);
|
|
|
341 |
break;
|
|
|
342 |
case "reference":
|
|
|
343 |
$value = new Reference($value);
|
|
|
344 |
break;
|
|
|
345 |
// any other object params we want to support should go here ...
|
|
|
346 |
}
|
|
|
347 |
|
|
|
348 |
} // if hint !== null
|
|
|
349 |
|
|
|
350 |
} // if not setrefid
|
|
|
351 |
|
|
|
352 |
} // if is slot-listener
|
|
|
353 |
|
|
|
354 |
try {
|
|
|
355 |
$project->log(" -calling setter ".$method->getDeclaringClass()->getName()."::".$method->getName()."()", Project::MSG_DEBUG);
|
|
|
356 |
$method->invoke($element, $value);
|
|
|
357 |
} catch(Exception $exc) {
|
|
|
358 |
throw new BuildException($exc);
|
|
|
359 |
}
|
|
|
360 |
|
|
|
361 |
}
|
|
|
362 |
|
|
|
363 |
/** Adds PCDATA areas.*/
|
|
|
364 |
function addText(Project $project, $element, $text) {
|
|
|
365 |
if ($this->methodAddText === null) {
|
|
|
366 |
$msg = $this->getElementName($project, $element)." doesn't support nested text data.";
|
|
|
367 |
throw new BuildException($msg);
|
|
|
368 |
}
|
|
|
369 |
try {
|
|
|
370 |
$method = $this->methodAddText;
|
|
|
371 |
$method->invoke($element, $text);
|
|
|
372 |
} catch (Exception $exc) {
|
|
|
373 |
throw new BuildException($exc);
|
|
|
374 |
}
|
|
|
375 |
}
|
|
|
376 |
|
|
|
377 |
/**
|
|
|
378 |
* Creates a named nested element.
|
|
|
379 |
*
|
|
|
380 |
* Valid creators can be in the form createFoo() or addFoo(Bar).
|
|
|
381 |
* @return object Returns the nested element.
|
|
|
382 |
* @throws BuildException
|
|
|
383 |
*/
|
|
|
384 |
function createElement(Project $project, $element, $elementName) {
|
|
|
385 |
|
|
|
386 |
$addMethod = "add".strtolower($elementName);
|
|
|
387 |
$createMethod = "create".strtolower($elementName);
|
|
|
388 |
$nestedElement = null;
|
|
|
389 |
|
|
|
390 |
if (isset($this->nestedCreators[$createMethod])) {
|
|
|
391 |
|
|
|
392 |
$method = $this->nestedCreators[$createMethod];
|
|
|
393 |
try { // try to invoke the creator method on object
|
|
|
394 |
$project->log(" -calling creator ".$method->getDeclaringClass()->getName()."::".$method->getName()."()", Project::MSG_DEBUG);
|
|
|
395 |
$nestedElement = $method->invoke($element);
|
|
|
396 |
} catch (Exception $exc) {
|
|
|
397 |
throw new BuildException($exc);
|
|
|
398 |
}
|
|
|
399 |
|
|
|
400 |
} elseif (isset($this->nestedCreators[$addMethod])) {
|
|
|
401 |
|
|
|
402 |
$method = $this->nestedCreators[$addMethod];
|
|
|
403 |
|
|
|
404 |
// project components must use class hints to support the add methods
|
|
|
405 |
|
|
|
406 |
try { // try to invoke the adder method on object
|
|
|
407 |
|
|
|
408 |
$project->log(" -calling adder ".$method->getDeclaringClass()->getName()."::".$method->getName()."()", Project::MSG_DEBUG);
|
|
|
409 |
// we've already assured that correct num of params
|
|
|
410 |
// exist and that method is using class hints
|
|
|
411 |
$params = $method->getParameters();
|
|
|
412 |
|
|
|
413 |
$classname = null;
|
|
|
414 |
|
|
|
415 |
if (($hint = $params[0]->getClass()) !== null) {
|
|
|
416 |
$classname = $hint->getName();
|
|
|
417 |
}
|
|
|
418 |
|
|
|
419 |
// create a new instance of the object and add it via $addMethod
|
|
|
420 |
$nestedElement = new $classname();
|
|
|
421 |
|
|
|
422 |
$method->invoke($element, $nestedElement);
|
|
|
423 |
|
|
|
424 |
} catch (Exception $exc) {
|
|
|
425 |
throw new BuildException($exc);
|
|
|
426 |
}
|
|
|
427 |
} else {
|
|
|
428 |
$msg = $this->getElementName($project, $element) . " doesn't support the '$elementName' creator/adder.";
|
|
|
429 |
throw new BuildException($msg);
|
|
|
430 |
}
|
|
|
431 |
|
|
|
432 |
if ($nestedElement instanceof ProjectComponent) {
|
|
|
433 |
$nestedElement->setProject($project);
|
|
|
434 |
}
|
|
|
435 |
|
|
|
436 |
return $nestedElement;
|
|
|
437 |
}
|
|
|
438 |
|
|
|
439 |
/**
|
|
|
440 |
* Creates a named nested element.
|
|
|
441 |
* @return void
|
|
|
442 |
* @throws BuildException
|
|
|
443 |
*/
|
|
|
444 |
function storeElement($project, $element, $child, $elementName = null) {
|
|
|
445 |
|
|
|
446 |
if ($elementName === null) {
|
|
|
447 |
return;
|
|
|
448 |
}
|
|
|
449 |
|
|
|
450 |
$storer = "addconfigured".strtolower($elementName);
|
|
|
451 |
|
|
|
452 |
if (isset($this->nestedStorers[$storer])) {
|
|
|
453 |
|
|
|
454 |
$method = $this->nestedStorers[$storer];
|
|
|
455 |
|
|
|
456 |
try {
|
|
|
457 |
$project->log(" -calling storer ".$method->getDeclaringClass()->getName()."::".$method->getName()."()", Project::MSG_DEBUG);
|
|
|
458 |
$method->invoke($element, $child);
|
|
|
459 |
} catch (Exception $exc) {
|
|
|
460 |
throw new BuildException($exc);
|
|
|
461 |
}
|
|
|
462 |
}
|
|
|
463 |
|
|
|
464 |
}
|
|
|
465 |
|
|
|
466 |
/** Does the introspected class support PCDATA? */
|
|
|
467 |
function supportsCharacters() {
|
|
|
468 |
return ($this->methodAddText !== null);
|
|
|
469 |
}
|
|
|
470 |
|
|
|
471 |
/** Return all attribues supported by the introspected class. */
|
|
|
472 |
function getAttributes() {
|
|
|
473 |
$attribs = array();
|
|
|
474 |
foreach (array_keys($this->attributeSetters) as $setter) {
|
|
|
475 |
$attribs[] =$this->getPropertyName($setter, "set");
|
|
|
476 |
}
|
|
|
477 |
return $attribs;
|
|
|
478 |
}
|
|
|
479 |
|
|
|
480 |
/** Return all nested elements supported by the introspected class. */
|
|
|
481 |
function getNestedElements() {
|
|
|
482 |
return $this->nestedTypes;
|
|
|
483 |
}
|
|
|
484 |
|
|
|
485 |
/**
|
|
|
486 |
* Get the the name for an element.
|
|
|
487 |
* When possible the full classnam (phing.tasks.system.PropertyTask) will
|
|
|
488 |
* be returned. If not available (loaded in taskdefs or typedefs) then the
|
|
|
489 |
* XML element name will be returned.
|
|
|
490 |
*
|
|
|
491 |
* @param Project $project
|
|
|
492 |
* @param object $element The Task or type element.
|
|
|
493 |
* @return string Fully qualified class name of element when possible.
|
|
|
494 |
*/
|
|
|
495 |
function getElementName(Project $project, $element) {
|
|
|
496 |
|
|
|
497 |
$taskdefs = $project->getTaskDefinitions();
|
|
|
498 |
$typedefs = $project->getDataTypeDefinitions();
|
|
|
499 |
|
|
|
500 |
// check if class of element is registered with project (tasks & types)
|
|
|
501 |
// most element types don't have a getTag() method
|
|
|
502 |
$elClass = get_class($element);
|
|
|
503 |
|
|
|
504 |
if (!in_array('getTag', get_class_methods($elClass))) {
|
|
|
505 |
// loop through taskdefs and typesdefs and see if the class name
|
|
|
506 |
// matches (case-insensitive) any of the classes in there
|
|
|
507 |
foreach(array_merge($taskdefs, $typedefs) as $elName => $class) {
|
|
|
508 |
if (0 === strcasecmp($elClass, StringHelper::unqualify($class))) {
|
|
|
509 |
return $class;
|
|
|
510 |
}
|
|
|
511 |
}
|
|
|
512 |
return "$elClass (unknown)";
|
|
|
513 |
} else {
|
|
|
514 |
// ->getTag() method does exist, so use it
|
|
|
515 |
$elName = $element->getTag();
|
|
|
516 |
if (isset($taskdefs[$elName])) {
|
|
|
517 |
return $taskdefs[$elName];
|
|
|
518 |
} elseif (isset($typedefs[$elName])) {
|
|
|
519 |
|
|
|
520 |
return $typedefs[$elName];
|
|
|
521 |
} else {
|
|
|
522 |
return "$elName (unknown)";
|
|
|
523 |
}
|
|
|
524 |
}
|
|
|
525 |
}
|
|
|
526 |
|
|
|
527 |
/** extract the name of a property from a method name - subtracting a given prefix. */
|
|
|
528 |
function getPropertyName($methodName, $prefix) {
|
|
|
529 |
$start = strlen($prefix);
|
|
|
530 |
return strtolower(substr($methodName, $start));
|
|
|
531 |
}
|
|
|
532 |
|
|
|
533 |
/**
|
|
|
534 |
* Prints warning message to screen if -debug was used.
|
|
|
535 |
*/
|
|
|
536 |
function warn($msg) {
|
|
|
537 |
if (Phing::getMsgOutputLevel() === Project::MSG_DEBUG) {
|
|
|
538 |
print("[IntrospectionHelper] " . $msg . "\n");
|
|
|
539 |
}
|
|
|
540 |
}
|
|
|
541 |
|
|
|
542 |
}
|