1 : <?php
2 : /**
3 : * TComponent, TPropertyValue classes
4 : *
5 : * @author Qiang Xue <qiang.xue@gmail.com>
6 : * @link http://www.pradosoft.com/
7 : * @copyright Copyright © 2005 PradoSoft
8 : * @license http://www.pradosoft.com/license/
9 : * @version $Id: TComponent.php 2108 2007-08-05 18:08:33Z xue $
10 : * @package System
11 : */
12 :
13 : /**
14 : * TComponent class
15 : *
16 : * TComponent is the base class for all PRADO components.
17 : * TComponent implements the protocol of defining, using properties and events.
18 : *
19 : * A property is defined by a getter method, and/or a setter method.
20 : * Properties can be accessed in the way like accessing normal object members.
21 : * Reading or writing a property will cause the invocation of the corresponding
22 : * getter or setter method, e.g.,
23 : * <code>
24 : * $a=$this->Text; // equivalent to $a=$this->getText();
25 : * $this->Text='abc'; // equivalent to $this->setText('abc');
26 : * </code>
27 : * The signatures of getter and setter methods are as follows,
28 : * <code>
29 : * // getter, defines a readable property 'Text'
30 : * function getText() { ... }
31 : * // setter, defines a writable property 'Text', with $value being the value to be set to the property
32 : * function setText($value) { ... }
33 : * </code>
34 : * Property names are case-insensitive. It is recommended that they are written
35 : * in the format of concatenated words, with the first letter of each word
36 : * capitalized (e.g. DisplayMode, ItemStyle).
37 : *
38 : * An event is defined by the presence of a method whose name starts with 'on'.
39 : * The event name is the method name and is thus case-insensitive.
40 : * An event can be attached with one or several methods (called event handlers).
41 : * An event can be raised by calling {@link raiseEvent} method, upon which
42 : * the attached event handlers will be invoked automatically in the order they
43 : * are attached to the event. Event handlers must have the following signature,
44 : * <code>
45 : * function eventHandlerFuncName($sender,$param) { ... }
46 : * </code>
47 : * where $sender refers to the object who is responsible for the raising of the event,
48 : * and $param refers to a structure that may contain event-specific information.
49 : * To raise an event (assuming named as 'Click') of a component, use
50 : * <code>
51 : * $component->raiseEvent('OnClick');
52 : * </code>
53 : * To attach an event handler to an event, use one of the following ways,
54 : * <code>
55 : * $component->OnClick=$callback; // or $component->OnClick->add($callback);
56 : * $$component->attachEventHandler('OnClick',$callback);
57 : * </code>
58 : * The first two ways make use of the fact that $component->OnClick refers to
59 : * the event handler list {@link TList} for the 'OnClick' event.
60 : * The variable $callback contains the definition of the event handler that can
61 : * be either a string referring to a global function name, or an array whose
62 : * first element refers to an object and second element a method name/path that
63 : * is reachable by the object, e.g.
64 : * - 'buttonClicked' : buttonClicked($sender,$param);
65 : * - array($object,'buttonClicked') : $object->buttonClicked($sender,$param);
66 : * - array($object,'MainContent.SubmitButton.buttonClicked') :
67 : * $object->MainContent->SubmitButton->buttonClicked($sender,$param);
68 : *
69 : * @author Qiang Xue <qiang.xue@gmail.com>
70 : * @version $Id: TComponent.php 2108 2007-08-05 18:08:33Z xue $
71 : * @package System
72 : * @since 3.0
73 : */
74 : class TComponent
75 : {
76 : /**
77 : * @var array event handler lists
78 : */
79 : private $_e=array();
80 :
81 : /**
82 : * Returns a property value or an event handler list by property or event name.
83 : * Do not call this method. This is a PHP magic method that we override
84 : * to allow using the following syntax to read a property:
85 : * <code>
86 : * $value=$component->PropertyName;
87 : * </code>
88 : * and to obtain the event handler list for an event,
89 : * <code>
90 : * $eventHandlerList=$component->EventName;
91 : * </code>
92 : * @param string the property name or the event name
93 : * @return mixed the property value or the event handler list
94 : * @throws TInvalidOperationException if the property/event is not defined.
95 : */
96 : public function __get($name)
97 : {
98 32 : $getter='get'.$name;
99 32 : if(method_exists($this,$getter))
100 32 : {
101 : // getting a property
102 29 : return $this->$getter();
103 : }
104 4 : else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
105 4 : {
106 : // getting an event (handler list)
107 0 : $name=strtolower($name);
108 0 : if(!isset($this->_e[$name]))
109 0 : $this->_e[$name]=new TList;
110 0 : return $this->_e[$name];
111 : }
112 : else
113 : {
114 4 : throw new TInvalidOperationException('component_property_undefined',get_class($this),$name);
115 : }
116 : }
117 :
118 : /**
119 : * Sets value of a component property.
120 : * Do not call this method. This is a PHP magic method that we override
121 : * to allow using the following syntax to set a property or attach an event handler.
122 : * <code>
123 : * $this->PropertyName=$value;
124 : * $this->EventName=$handler;
125 : * </code>
126 : * @param string the property name or event name
127 : * @param mixed the property value or event handler
128 : * @throws TInvalidOperationException If the property is not defined or read-only.
129 : */
130 : public function __set($name,$value)
131 : {
132 28 : $setter='set'.$name;
133 28 : if(method_exists($this,$setter))
134 28 : {
135 28 : $this->$setter($value);
136 27 : }
137 1 : else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
138 1 : {
139 0 : $this->attachEventHandler($name,$value);
140 0 : }
141 1 : else if(method_exists($this,'get'.$name))
142 1 : {
143 0 : throw new TInvalidOperationException('component_property_readonly',get_class($this),$name);
144 : }
145 : else
146 : {
147 1 : throw new TInvalidOperationException('component_property_undefined',get_class($this),$name);
148 : }
149 27 : }
150 :
151 : /**
152 : * Determines whether a property is defined.
153 : * A property is defined if there is a getter or setter method
154 : * defined in the class. Note, property names are case-insensitive.
155 : * @param string the property name
156 : * @return boolean whether the property is defined
157 : */
158 : public function hasProperty($name)
159 : {
160 2 : return method_exists($this,'get'.$name) || method_exists($this,'set'.$name);
161 : }
162 :
163 : /**
164 : * Determines whether a property can be read.
165 : * A property can be read if the class has a getter method
166 : * for the property name. Note, property name is case-insensitive.
167 : * @param string the property name
168 : * @return boolean whether the property can be read
169 : */
170 : public function canGetProperty($name)
171 : {
172 2 : return method_exists($this,'get'.$name);
173 : }
174 :
175 : /**
176 : * Determines whether a property can be set.
177 : * A property can be written if the class has a setter method
178 : * for the property name. Note, property name is case-insensitive.
179 : * @param string the property name
180 : * @return boolean whether the property can be written
181 : */
182 : public function canSetProperty($name)
183 : {
184 1 : return method_exists($this,'set'.$name);
185 : }
186 :
187 : /**
188 : * Evaluates a property path.
189 : * A property path is a sequence of property names concatenated by '.' character.
190 : * For example, 'Parent.Page' refers to the 'Page' property of the component's
191 : * 'Parent' property value (which should be a component also).
192 : * @param string property path
193 : * @return mixed the property path value
194 : */
195 : public function getSubProperty($path)
196 : {
197 4 : $object=$this;
198 4 : foreach(explode('.',$path) as $property)
199 4 : $object=$object->$property;
200 4 : return $object;
201 : }
202 :
203 : /**
204 : * Sets a value to a property path.
205 : * A property path is a sequence of property names concatenated by '.' character.
206 : * For example, 'Parent.Page' refers to the 'Page' property of the component's
207 : * 'Parent' property value (which should be a component also).
208 : * @param string property path
209 : * @param mixed the property path value
210 : */
211 : public function setSubProperty($path,$value)
212 : {
213 2 : $object=$this;
214 2 : if(($pos=strrpos($path,'.'))===false)
215 2 : $property=$path;
216 : else
217 : {
218 2 : $object=$this->getSubProperty(substr($path,0,$pos));
219 2 : $property=substr($path,$pos+1);
220 : }
221 2 : $object->$property=$value;
222 2 : }
223 :
224 : /**
225 : * Determines whether an event is defined.
226 : * An event is defined if the class has a method whose name is the event name prefixed with 'on'.
227 : * Note, event name is case-insensitive.
228 : * @param string the event name
229 : * @return boolean
230 : */
231 : public function hasEvent($name)
232 : {
233 11 : return strncasecmp($name,'on',2)===0 && method_exists($this,$name);
234 : }
235 :
236 : /**
237 : * @return boolean whether an event has been attached one or several handlers
238 : */
239 : public function hasEventHandler($name)
240 : {
241 1 : $name=strtolower($name);
242 1 : return isset($this->_e[$name]) && $this->_e[$name]->getCount()>0;
243 : }
244 :
245 : /**
246 : * Returns the list of attached event handlers for an event.
247 : * @return TList list of attached event handlers for an event
248 : * @throws TInvalidOperationException if the event is not defined
249 : */
250 : public function getEventHandlers($name)
251 : {
252 9 : if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
253 9 : {
254 9 : $name=strtolower($name);
255 9 : if(!isset($this->_e[$name]))
256 9 : $this->_e[$name]=new TList;
257 9 : return $this->_e[$name];
258 : }
259 : else
260 2 : throw new TInvalidOperationException('component_event_undefined',get_class($this),$name);
261 : }
262 :
263 : /**
264 : * Attaches an event handler to an event.
265 : *
266 : * The handler must be a valid PHP callback, i.e., a string referring to
267 : * a global function name, or an array containing two elements with
268 : * the first element being an object and the second element a method name
269 : * of the object. In Prado, you can also use method path to refer to
270 : * an event handler. For example, array($object,'Parent.buttonClicked')
271 : * uses a method path that refers to the method $object->Parent->buttonClicked(...).
272 : *
273 : * The event handler must be of the following signature,
274 : * <code>
275 : * function handlerName($sender,$param) {}
276 : * </code>
277 : * where $sender represents the object that raises the event,
278 : * and $param is the event parameter.
279 : *
280 : * This is a convenient method to add an event handler.
281 : * It is equivalent to {@link getEventHandlers}($name)->add($handler).
282 : * For complete management of event handlers, use {@link getEventHandlers}
283 : * to get the event handler list first, and then do various
284 : * {@link TList} operations to append, insert or remove
285 : * event handlers. You may also do these operations like
286 : * getting and setting properties, e.g.,
287 : * <code>
288 : * $component->OnClick[]=array($object,'buttonClicked');
289 : * $component->OnClick->insertAt(0,array($object,'buttonClicked'));
290 : * </code>
291 : * which are equivalent to the following
292 : * <code>
293 : * $component->getEventHandlers('OnClick')->add(array($object,'buttonClicked'));
294 : * $component->getEventHandlers('OnClick')->insertAt(0,array($object,'buttonClicked'));
295 : * </code>
296 : *
297 : * @param string the event name
298 : * @param callback the event handler
299 : * @throws TInvalidOperationException if the event does not exist
300 : */
301 : public function attachEventHandler($name,$handler)
302 : {
303 9 : $this->getEventHandlers($name)->add($handler);
304 9 : }
305 :
306 : /**
307 : * Detaches an existing event handler.
308 : * This method is the opposite of {@link attachEventHandler}.
309 : * @param string event name
310 : * @param callback the event handler to be removed
311 : * @return boolean if the removal is successful
312 : */
313 : public function detachEventHandler($name,$handler)
314 : {
315 0 : if($this->hasEventHandler($name))
316 0 : {
317 : try
318 : {
319 0 : $this->getEventHandlers($name)->remove($handler);
320 0 : return true;
321 : }
322 : catch(Exception $e)
323 : {
324 : }
325 : }
326 0 : return false;
327 : }
328 :
329 : /**
330 : * Raises an event.
331 : * This method represents the happening of an event and will
332 : * invoke all attached event handlers for the event.
333 : * @param string the event name
334 : * @param mixed the event sender object
335 : * @param TEventParameter the event parameter
336 : * @throws TInvalidOperationException if the event is undefined
337 : * @throws TInvalidDataValueException If an event handler is invalid
338 : */
339 : public function raiseEvent($name,$sender,$param)
340 : {
341 11 : $name=strtolower($name);
342 11 : if(isset($this->_e[$name]))
343 11 : {
344 3 : foreach($this->_e[$name] as $handler)
345 : {
346 3 : if(is_string($handler))
347 3 : {
348 0 : if(($pos=strrpos($handler,'.'))!==false)
349 0 : {
350 0 : $object=$this->getSubProperty(substr($handler,0,$pos));
351 0 : $method=substr($handler,$pos+1);
352 0 : if(method_exists($object,$method))
353 0 : $object->$method($sender,$param);
354 : else
355 0 : throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,$handler);
356 0 : }
357 : else
358 0 : call_user_func($handler,$sender,$param);
359 0 : }
360 3 : else if(is_callable($handler,true))
361 3 : {
362 : // an array: 0 - object, 1 - method name/path
363 3 : list($object,$method)=$handler;
364 3 : if(is_string($object)) // static method call
365 3 : call_user_func($handler,$sender,$param);
366 : else
367 : {
368 3 : if(($pos=strrpos($method,'.'))!==false)
369 3 : {
370 1 : $object=$this->getSubProperty(substr($method,0,$pos));
371 1 : $method=substr($method,$pos+1);
372 1 : }
373 3 : if(method_exists($object,$method))
374 3 : $object->$method($sender,$param);
375 : else
376 0 : throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,$handler[1]);
377 : }
378 3 : }
379 : else
380 0 : throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,gettype($handler));
381 3 : }
382 3 : }
383 10 : else if(!$this->hasEvent($name))
384 10 : throw new TInvalidOperationException('component_event_undefined',get_class($this),$name);
385 11 : }
386 :
387 : /**
388 : * Evaluates a PHP expression in the context of this control.
389 : * @return mixed the expression result
390 : * @throws TInvalidOperationException if the expression is invalid
391 : */
392 : public function evaluateExpression($expression)
393 : {
394 : try
395 : {
396 1 : if(eval("\$result=$expression;")===false)
397 1 : throw new Exception('');
398 1 : return $result;
399 : }
400 1 : catch(Exception $e)
401 : {
402 1 : throw new TInvalidOperationException('component_expression_invalid',get_class($this),$expression,$e->getMessage());
403 : }
404 : }
405 :
406 : /**
407 : * Evaluates a list of PHP statements.
408 : * @param string PHP statements
409 : * @return string content echoed or printed by the PHP statements
410 : * @throws TInvalidOperationException if the statements are invalid
411 : */
412 : public function evaluateStatements($statements)
413 : {
414 : try
415 : {
416 1 : ob_start();
417 1 : if(eval($statements)===false)
418 1 : throw new Exception('');
419 1 : $content=ob_get_contents();
420 1 : ob_end_clean();
421 1 : return $content;
422 : }
423 1 : catch(Exception $e)
424 : {
425 1 : throw new TInvalidOperationException('component_statements_invalid',get_class($this),$statements,$e->getMessage());
426 : }
427 : }
428 :
429 : /**
430 : * This method is invoked after the component is instantiated by a template.
431 : * When this method is invoked, the component's properties have been initialized.
432 : * The default implementation of this method will invoke
433 : * the potential parent component's {@link addParsedObject}.
434 : * This method can be overridden.
435 : * @param TComponent potential parent of this control
436 : * @see addParsedObject
437 : */
438 : public function createdOnTemplate($parent)
439 : {
440 0 : $parent->addParsedObject($this);
441 0 : }
442 :
443 : /**
444 : * Processes an object that is created during parsing template.
445 : * The object can be either a component or a static text string.
446 : * This method can be overridden to customize the handling of newly created objects in template.
447 : * Only framework developers and control developers should use this method.
448 : * @param string|TComponent text string or component parsed and instantiated in template
449 : * @see createdOnTemplate
450 : */
451 : public function addParsedObject($object)
452 : {
453 0 : }
454 : }
455 :
456 : /**
457 : * TEnumerable class.
458 : * TEnumerable is the base class for all enumerable types.
459 : * To define an enumerable type, extend TEnumberable and define string constants.
460 : * Each constant represents an enumerable value.
461 : * The constant name must be the same as the constant value.
462 : * For example,
463 : * <code>
464 : * class TTextAlign extends TEnumerable
465 : * {
466 : * const Left='Left';
467 : * const Right='Right';
468 : * }
469 : * </code>
470 : * Then, one can use the enumerable values such as TTextAlign::Left and
471 : * TTextAlign::Right.
472 : *
473 : * @author Qiang Xue <qiang.xue@gmail.com>
474 : * @version $Id: TComponent.php 2108 2007-08-05 18:08:33Z xue $
475 : * @package System
476 : * @since 3.0
477 : */
478 : class TEnumerable
479 : {
480 : }
481 :
482 : /**
483 : * TPropertyValue class
484 : *
485 : * TPropertyValue is a utility class that provides static methods
486 : * to convert component property values to specific types.
487 : *
488 : * TPropertyValue is commonly used in component setter methods to ensure
489 : * the new property value is of specific type.
490 : * For example, a boolean-typed property setter method would be as follows,
491 : * <code>
492 : * function setPropertyName($value) {
493 : * $value=TPropertyValue::ensureBoolean($value);
494 : * // $value is now of boolean type
495 : * }
496 : * </code>
497 : *
498 : * Properties can be of the following types with specific type conversion rules:
499 : * - string: a boolean value will be converted to 'true' or 'false'.
500 : * - boolean: string 'true' (case-insensitive) will be converted to true,
501 : * string 'false' (case-insensitive) will be converted to false.
502 : * - integer
503 : * - float
504 : * - array: string starting with '(' and ending with ')' will be considered as
505 : * as an array expression and will be evaluated. Otherwise, an array
506 : * with the value to be ensured is returned.
507 : * - object
508 : * - enum: enumerable type, represented by an array of strings.
509 : *
510 : * @author Qiang Xue <qiang.xue@gmail.com>
511 : * @version $Id: TComponent.php 2108 2007-08-05 18:08:33Z xue $
512 : * @package System
513 : * @since 3.0
514 : */
515 : class TPropertyValue
516 : {
517 : /**
518 : * Converts a value to boolean type.
519 : * Note, string 'true' (case-insensitive) will be converted to true,
520 : * string 'false' (case-insensitive) will be converted to false.
521 : * If a string represents a non-zero number, it will be treated as true.
522 : * @param mixed the value to be converted.
523 : * @return boolean
524 : */
525 : public static function ensureBoolean($value)
526 : {
527 127 : if (is_string($value))
528 127 : return strcasecmp($value,'true')==0 || $value!=0;
529 : else
530 127 : return (boolean)$value;
531 : }
532 :
533 : /**
534 : * Converts a value to string type.
535 : * Note, a boolean value will be converted to 'true' if it is true
536 : * and 'false' if it is false.
537 : * @param mixed the value to be converted.
538 : * @return string
539 : */
540 : public static function ensureString($value)
541 : {
542 30 : if (is_bool($value))
543 30 : return $value?'true':'false';
544 : else
545 30 : return (string)$value;
546 : }
547 :
548 : /**
549 : * Converts a value to integer type.
550 : * @param mixed the value to be converted.
551 : * @return integer
552 : */
553 : public static function ensureInteger($value)
554 : {
555 18 : return (integer)$value;
556 : }
557 :
558 : /**
559 : * Converts a value to float type.
560 : * @param mixed the value to be converted.
561 : * @return float
562 : */
563 : public static function ensureFloat($value)
564 : {
565 0 : return (float)$value;
566 : }
567 :
568 : /**
569 : * Converts a value to array type. If the value is a string and it is
570 : * in the form (a,b,c) then an array consisting of each of the elements
571 : * will be returned. If the value is a string and it is not in this form
572 : * then an array consisting of just the string will be returned. If the value
573 : * is not a string then
574 : * @param mixed the value to be converted.
575 : * @return array
576 : */
577 : public static function ensureArray($value)
578 : {
579 0 : if(is_string($value))
580 0 : {
581 0 : $trimmed = trim($value);
582 0 : $len = strlen($value);
583 0 : if ($len >= 2 && $trimmed[0] == '(' && $trimmed[$len-1] == ')')
584 0 : {
585 0 : eval('$array=array'.$trimmed.';');
586 0 : return $array;
587 : }
588 : else
589 0 : return $len>0?array($value):array();
590 : }
591 : else
592 0 : return (array)$value;
593 : }
594 :
595 : /**
596 : * Converts a value to object type.
597 : * @param mixed the value to be converted.
598 : * @return object
599 : */
600 : public static function ensureObject($value)
601 : {
602 0 : return (object)$value;
603 : }
604 :
605 : /**
606 : * Converts a value to enum type.
607 : *
608 : * This method checks if the value is of the specified enumerable type.
609 : * A value is a valid enumerable value if it is equal to the name of a constant
610 : * in the specified enumerable type (class).
611 : * For more details about enumerable, see {@link TEnumerable}.
612 : *
613 : * For backward compatibility, this method also supports sanity
614 : * check of a string value to see if it is among the given list of strings.
615 : * @param mixed the value to be converted.
616 : * @param mixed class name of the enumerable type, or array of valid enumeration values. If this is not an array,
617 : * the method considers its parameters are of variable length, and the second till the last parameters are enumeration values.
618 : * @return string the valid enumeration value
619 : * @throws TInvalidDataValueException if the original value is not in the string array.
620 : */
621 : public static function ensureEnum($value,$enums)
622 : {
623 7 : static $types=array();
624 7 : if(func_num_args()===2 && is_string($enums))
625 7 : {
626 6 : if(!isset($types[$enums]))
627 6 : $types[$enums]=new ReflectionClass($enums);
628 6 : if($types[$enums]->hasConstant($value))
629 6 : return $value;
630 : else
631 3 : throw new TInvalidDataValueException(
632 3 : 'propertyvalue_enumvalue_invalid',$value,
633 3 : implode(' | ',$types[$enums]->getConstants()));
634 : }
635 1 : else if(!is_array($enums))
636 1 : {
637 0 : $enums=func_get_args();
638 0 : array_shift($enums);
639 0 : }
640 1 : if(in_array($value,$enums,true))
641 1 : return $value;
642 : else
643 1 : throw new TInvalidDataValueException('propertyvalue_enumvalue_invalid',$value,implode(' | ',$enums));
644 : }
645 : }
646 :
647 : /**
648 : * TEventParameter class.
649 : * TEventParameter is the base class for all event parameter classes.
650 : *
651 : * @author Qiang Xue <qiang.xue@gmail.com>
652 : * @version $Id: TComponent.php 2108 2007-08-05 18:08:33Z xue $
653 : * @package System
654 : * @since 3.0
655 : */
656 : class TEventParameter extends TComponent
657 : {
658 : }
659 :
660 : /**
661 : * TComponentReflection class.
662 : *
663 : * TComponentReflection provides functionalities to inspect the public/protected
664 : * properties, events and methods defined in a class.
665 : *
666 : * The following code displays the properties and events defined in {@link TDataGrid},
667 : * <code>
668 : * $reflection=new TComponentReflection('TDataGrid');
669 : * Prado::varDump($reflection->getProperties());
670 : * Prado::varDump($reflection->getEvents());
671 : * </code>
672 : *
673 : * @author Qiang Xue <qiang.xue@gmail.com>
674 : * @version $Id: TComponent.php 2108 2007-08-05 18:08:33Z xue $
675 : * @package System
676 : * @since 3.0
677 : */
678 : class TComponentReflection extends TComponent
679 : {
680 : private $_className;
681 : private $_properties=array();
682 : private $_events=array();
683 : private $_methods=array();
684 :
685 : /**
686 : * Constructor.
687 : * @param object|string the component instance or the class name
688 : * @throws TInvalidDataTypeException if the object is not a component
689 : */
690 : public function __construct($component)
691 : {
692 0 : if(is_string($component) && class_exists($component,false))
693 0 : $this->_className=$component;
694 0 : else if(is_object($component))
695 0 : $this->_className=get_class($component);
696 : else
697 0 : throw new TInvalidDataTypeException('componentreflection_class_invalid');
698 0 : $this->reflect();
699 0 : }
700 :
701 : private function isPropertyMethod($method)
702 : {
703 0 : $methodName=$method->getName();
704 0 : return $method->getNumberOfRequiredParameters()===0
705 0 : && strncasecmp($methodName,'get',3)===0
706 0 : && isset($methodName[3]);
707 : }
708 :
709 : private function isEventMethod($method)
710 : {
711 0 : $methodName=$method->getName();
712 0 : return strncasecmp($methodName,'on',2)===0
713 0 : && isset($methodName[2]);
714 : }
715 :
716 : private function reflect()
717 : {
718 0 : $class=new TReflectionClass($this->_className);
719 0 : $properties=array();
720 0 : $events=array();
721 0 : $methods=array();
722 0 : $isComponent=is_subclass_of($this->_className,'TComponent') || strcasecmp($this->_className,'TComponent')===0;
723 0 : foreach($class->getMethods() as $method)
724 : {
725 0 : if($method->isPublic() || $method->isProtected())
726 0 : {
727 0 : $methodName=$method->getName();
728 0 : if(!$method->isStatic() && $isComponent)
729 0 : {
730 0 : if($this->isPropertyMethod($method))
731 0 : $properties[substr($methodName,3)]=$method;
732 0 : else if($this->isEventMethod($method))
733 0 : {
734 0 : $methodName[0]='O';
735 0 : $events[$methodName]=$method;
736 0 : }
737 0 : }
738 0 : if(strncmp($methodName,'__',2)!==0)
739 0 : $methods[$methodName]=$method;
740 0 : }
741 0 : }
742 0 : $reserved=array();
743 0 : ksort($properties);
744 0 : foreach($properties as $name=>$method)
745 : {
746 0 : $this->_properties[$name]=array(
747 0 : 'type'=>$this->determinePropertyType($method),
748 0 : 'readonly'=>!$class->hasMethod('set'.$name),
749 0 : 'protected'=>$method->isProtected(),
750 0 : 'class'=>$method->getDeclaringClass()->getName(),
751 0 : 'comments'=>$method->getDocComment()
752 0 : );
753 0 : $reserved['get'.strtolower($name)]=1;
754 0 : $reserved['set'.strtolower($name)]=1;
755 0 : }
756 0 : ksort($events);
757 0 : foreach($events as $name=>$method)
758 : {
759 0 : $this->_events[$name]=array(
760 0 : 'class'=>$method->getDeclaringClass()->getName(),
761 0 : 'protected'=>$method->isProtected(),
762 0 : 'comments'=>$method->getDocComment()
763 0 : );
764 0 : $reserved[strtolower($name)]=1;
765 0 : }
766 0 : ksort($methods);
767 0 : foreach($methods as $name=>$method)
768 : {
769 0 : if(!isset($reserved[strtolower($name)]))
770 0 : $this->_methods[$name]=array(
771 0 : 'class'=>$method->getDeclaringClass()->getName(),
772 0 : 'protected'=>$method->isProtected(),
773 0 : 'static'=>$method->isStatic(),
774 0 : 'comments'=>$method->getDocComment()
775 0 : );
776 0 : }
777 0 : }
778 :
779 : /**
780 : * Determines the property type.
781 : * This method uses the doc comment to determine the property type.
782 : * @param ReflectionMethod
783 : * @return string the property type, '{unknown}' if type cannot be determined from comment
784 : */
785 : protected function determinePropertyType($method)
786 : {
787 0 : $comment=$method->getDocComment();
788 0 : if(preg_match('/@return\\s+(.*?)\\s+/',$comment,$matches))
789 0 : return $matches[1];
790 : else
791 0 : return '{unknown}';
792 : }
793 :
794 : /**
795 : * @return string class name of the component
796 : */
797 : public function getClassName()
798 : {
799 0 : return $this->_className;
800 : }
801 :
802 : /**
803 : * @return array list of component properties. Array keys are property names.
804 : * Each array element is of the following structure:
805 : * [type]=>property type,
806 : * [readonly]=>whether the property is read-only,
807 : * [protected]=>whether the method is protected or not
808 : * [class]=>the class where the property is inherited from,
809 : * [comments]=>comments associated with the property.
810 : */
811 : public function getProperties()
812 : {
813 0 : return $this->_properties;
814 : }
815 :
816 : /**
817 : * @return array list of component events. Array keys are event names.
818 : * Each array element is of the following structure:
819 : * [protected]=>whether the event is protected or not
820 : * [class]=>the class where the event is inherited from.
821 : * [comments]=>comments associated with the event.
822 : */
823 : public function getEvents()
824 : {
825 0 : return $this->_events;
826 : }
827 :
828 : /**
829 : * @return array list of public/protected methods. Array keys are method names.
830 : * Each array element is of the following structure:
831 : * [protected]=>whether the method is protected or not
832 : * [static]=>whether the method is static or not
833 : * [class]=>the class where the property is inherited from,
834 : * [comments]=>comments associated with the event.
835 : */
836 : public function getMethods()
837 : {
838 0 : return $this->_methods;
839 : }
840 : }
841 :
842 : ?>
|