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