PageRenderTime 33ms CodeModel.GetById 14ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 0ms

/TComponent.php

https://bitbucket.org/freshflow/prado-fork-v3.10
PHP | 841 lines | 389 code | 38 blank | 414 comment | 66 complexity | 0cb7d87983008ccae1056ba61d2e72d6 MD5 | raw file
  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 &copy; 2005-2008 PradoSoft
  8 * @license http://www.pradosoft.com/license/
  9 * @version $Id: TComponent.php 2541 2008-10-21 15:05:13Z qiang.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 2541 2008-10-21 15:05:13Z qiang.xue $
 71 * @package System
 72 * @since 3.0
 73 */
 74class 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		$getter='get'.$name;
 99		if(method_exists($this,$getter))
100		{
101			// getting a property
102			return $this->$getter();
103		}
104		else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
105		{
106			// getting an event (handler list)
107			$name=strtolower($name);
108			if(!isset($this->_e[$name]))
109				$this->_e[$name]=new TList;
110			return $this->_e[$name];
111		}
112		else
113		{
114			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		$setter='set'.$name;
133		if(method_exists($this,$setter))
134		{
135			$this->$setter($value);
136		}
137		else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
138		{
139			$this->attachEventHandler($name,$value);
140		}
141		else if(method_exists($this,'get'.$name))
142		{
143			throw new TInvalidOperationException('component_property_readonly',get_class($this),$name);
144		}
145		else
146		{
147			throw new TInvalidOperationException('component_property_undefined',get_class($this),$name);
148		}
149	}
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		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		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		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		$object=$this;
198		foreach(explode('.',$path) as $property)
199			$object=$object->$property;
200		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		$object=$this;
214		if(($pos=strrpos($path,'.'))===false)
215			$property=$path;
216		else
217		{
218			$object=$this->getSubProperty(substr($path,0,$pos));
219			$property=substr($path,$pos+1);
220		}
221		$object->$property=$value;
222	}
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		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		$name=strtolower($name);
242		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		if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
253		{
254			$name=strtolower($name);
255			if(!isset($this->_e[$name]))
256				$this->_e[$name]=new TList;
257			return $this->_e[$name];
258		}
259		else
260			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		$this->getEventHandlers($name)->add($handler);
304	}
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		if($this->hasEventHandler($name))
316		{
317			try
318			{
319				$this->getEventHandlers($name)->remove($handler);
320				return true;
321			}
322			catch(Exception $e)
323			{
324			}
325		}
326		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		$name=strtolower($name);
342		if(isset($this->_e[$name]))
343		{
344			foreach($this->_e[$name] as $handler)
345			{
346				if(is_string($handler))
347				{
348					if(($pos=strrpos($handler,'.'))!==false)
349					{
350						$object=$this->getSubProperty(substr($handler,0,$pos));
351						$method=substr($handler,$pos+1);
352						if(method_exists($object,$method))
353							$object->$method($sender,$param);
354						else
355							throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,$handler);
356					}
357					else
358						call_user_func($handler,$sender,$param);
359				}
360				else if(is_callable($handler,true))
361				{
362					// an array: 0 - object, 1 - method name/path
363					list($object,$method)=$handler;
364					if(is_string($object))	// static method call
365						call_user_func($handler,$sender,$param);
366					else
367					{
368						if(($pos=strrpos($method,'.'))!==false)
369						{
370							$object=$this->getSubProperty(substr($method,0,$pos));
371							$method=substr($method,$pos+1);
372						}
373						if(method_exists($object,$method))
374							$object->$method($sender,$param);
375						else
376							throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,$handler[1]);
377					}
378				}
379				else
380					throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,gettype($handler));
381			}
382		}
383		else if(!$this->hasEvent($name))
384			throw new TInvalidOperationException('component_event_undefined',get_class($this),$name);
385	}
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			if(eval("\$result=$expression;")===false)
397				throw new Exception('');
398			return $result;
399		}
400		catch(Exception $e)
401		{
402			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			ob_start();
417			if(eval($statements)===false)
418				throw new Exception('');
419			$content=ob_get_contents();
420			ob_end_clean();
421			return $content;
422		}
423		catch(Exception $e)
424		{
425			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		$parent->addParsedObject($this);
441	}
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	}
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 2541 2008-10-21 15:05:13Z qiang.xue $
475 * @package System
476 * @since 3.0
477 */
478class 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 2541 2008-10-21 15:05:13Z qiang.xue $
512 * @package System
513 * @since 3.0
514 */
515class 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		if (is_string($value))
528			return strcasecmp($value,'true')==0 || $value!=0;
529		else
530			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		if (is_bool($value))
543			return $value?'true':'false';
544		else
545			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		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		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		if(is_string($value))
580		{
581			$value = trim($value);
582			$len = strlen($value);
583			if ($len >= 2 && $value[0] == '(' && $value[$len-1] == ')')
584			{
585				eval('$array=array'.$value.';');
586				return $array;
587			}
588			else
589				return $len>0?array($value):array();
590		}
591		else
592			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		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		static $types=array();
624		if(func_num_args()===2 && is_string($enums))
625		{
626			if(!isset($types[$enums]))
627				$types[$enums]=new ReflectionClass($enums);
628			if($types[$enums]->hasConstant($value))
629				return $value;
630			else
631				throw new TInvalidDataValueException(
632					'propertyvalue_enumvalue_invalid',$value,
633						implode(' | ',$types[$enums]->getConstants()));
634		}
635		else if(!is_array($enums))
636		{
637			$enums=func_get_args();
638			array_shift($enums);
639		}
640		if(in_array($value,$enums,true))
641			return $value;
642		else
643			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 2541 2008-10-21 15:05:13Z qiang.xue $
653 * @package System
654 * @since 3.0
655 */
656class 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 2541 2008-10-21 15:05:13Z qiang.xue $
675 * @package System
676 * @since 3.0
677 */
678class 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		if(is_string($component) && class_exists($component,false))
693			$this->_className=$component;
694		else if(is_object($component))
695			$this->_className=get_class($component);
696		else
697			throw new TInvalidDataTypeException('componentreflection_class_invalid');
698		$this->reflect();
699	}
700
701	private function isPropertyMethod($method)
702	{
703		$methodName=$method->getName();
704		return $method->getNumberOfRequiredParameters()===0
705				&& strncasecmp($methodName,'get',3)===0
706				&& isset($methodName[3]);
707	}
708
709	private function isEventMethod($method)
710	{
711		$methodName=$method->getName();
712		return strncasecmp($methodName,'on',2)===0
713				&& isset($methodName[2]);
714	}
715
716	private function reflect()
717	{
718		$class=new TReflectionClass($this->_className);
719		$properties=array();
720		$events=array();
721		$methods=array();
722		$isComponent=is_subclass_of($this->_className,'TComponent') || strcasecmp($this->_className,'TComponent')===0;
723		foreach($class->getMethods() as $method)
724		{
725			if($method->isPublic() || $method->isProtected())
726			{
727				$methodName=$method->getName();
728				if(!$method->isStatic() && $isComponent)
729				{
730					if($this->isPropertyMethod($method))
731						$properties[substr($methodName,3)]=$method;
732					else if($this->isEventMethod($method))
733					{
734						$methodName[0]='O';
735						$events[$methodName]=$method;
736					}
737				}
738				if(strncmp($methodName,'__',2)!==0)
739					$methods[$methodName]=$method;
740			}
741		}
742		$reserved=array();
743		ksort($properties);
744		foreach($properties as $name=>$method)
745		{
746			$this->_properties[$name]=array(
747				'type'=>$this->determinePropertyType($method),
748				'readonly'=>!$class->hasMethod('set'.$name),
749				'protected'=>$method->isProtected(),
750				'class'=>$method->getDeclaringClass()->getName(),
751				'comments'=>$method->getDocComment()
752			);
753			$reserved['get'.strtolower($name)]=1;
754			$reserved['set'.strtolower($name)]=1;
755		}
756		ksort($events);
757		foreach($events as $name=>$method)
758		{
759			$this->_events[$name]=array(
760				'class'=>$method->getDeclaringClass()->getName(),
761				'protected'=>$method->isProtected(),
762				'comments'=>$method->getDocComment()
763			);
764			$reserved[strtolower($name)]=1;
765		}
766		ksort($methods);
767		foreach($methods as $name=>$method)
768		{
769			if(!isset($reserved[strtolower($name)]))
770				$this->_methods[$name]=array(
771					'class'=>$method->getDeclaringClass()->getName(),
772					'protected'=>$method->isProtected(),
773					'static'=>$method->isStatic(),
774					'comments'=>$method->getDocComment()
775				);
776		}
777	}
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		$comment=$method->getDocComment();
788		if(preg_match('/@return\\s+(.*?)\\s+/',$comment,$matches))
789			return $matches[1];
790		else
791			return '{unknown}';
792	}
793
794	/**
795	 * @return string class name of the component
796	 */
797	public function getClassName()
798	{
799		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		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		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		return $this->_methods;
839	}
840}
841