PageRenderTime 47ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/htdocs/yii/framework/base/CComponent.php

https://bitbucket.org/dfr/tehsad
PHP | 604 lines | 235 code | 29 blank | 340 comment | 55 complexity | d12833cf26ea26ff64e93c60b8143a4a MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, BSD-2-Clause
  1. <?php
  2. /**
  3. * This file contains the foundation classes for component-based and event-driven programming.
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright Copyright &copy; 2008-2009 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * CComponent is the base class for all components.
  12. *
  13. * CComponent implements the protocol of defining, using properties and events.
  14. *
  15. * A property is defined by a getter method, and/or a setter method.
  16. * Properties can be accessed in the way like accessing normal object members.
  17. * Reading or writing a property will cause the invocation of the corresponding
  18. * getter or setter method, e.g.,
  19. * <pre>
  20. * $a=$component->text; // equivalent to $a=$component->getText();
  21. * $component->text='abc'; // equivalent to $component->setText('abc');
  22. * </pre>
  23. * The signatures of getter and setter methods are as follows,
  24. * <pre>
  25. * // getter, defines a readable property 'text'
  26. * public function getText() { ... }
  27. * // setter, defines a writable property 'text' with $value to be set to the property
  28. * public function setText($value) { ... }
  29. * </pre>
  30. *
  31. * An event is defined by the presence of a method whose name starts with 'on'.
  32. * The event name is the method name. When an event is raised, functions
  33. * (called event handlers) attached to the event will be invoked automatically.
  34. *
  35. * An event can be raised by calling {@link raiseEvent} method, upon which
  36. * the attached event handlers will be invoked automatically in the order they
  37. * are attached to the event. Event handlers must have the following signature,
  38. * <pre>
  39. * function eventHandler($event) { ... }
  40. * </pre>
  41. * where $event includes parameters associated with the event.
  42. *
  43. * To attach an event handler to an event, see {@link attachEventHandler}.
  44. * You can also use the following syntax:
  45. * <pre>
  46. * $component->onClick=$callback; // or $component->onClick->add($callback);
  47. * </pre>
  48. * where $callback refers to a valid PHP callback. Below we show some callback examples:
  49. * <pre>
  50. * 'handleOnClick' // handleOnClick() is a global function
  51. * array($object,'handleOnClick') // using $object->handleOnClick()
  52. * array('Page','handleOnClick') // using Page::handleOnClick()
  53. * </pre>
  54. *
  55. * To raise an event, use {@link raiseEvent}. The on-method defining an event is
  56. * commonly written like the following:
  57. * <pre>
  58. * public function onClick($event)
  59. * {
  60. * $this->raiseEvent('onClick',$event);
  61. * }
  62. * </pre>
  63. * where <code>$event</code> is an instance of {@link CEvent} or its child class.
  64. * One can then raise the event by calling the on-method instead of {@link raiseEvent} directly.
  65. *
  66. * Both property names and event names are case-insensitive.
  67. *
  68. * Starting from version 1.0.2, CComponent supports behaviors. A behavior is an
  69. * instance of {@link IBehavior} which is attached to a component. The methods of
  70. * the behavior can be invoked as if they belong to the component. Multiple behaviors
  71. * can be attached to the same component.
  72. *
  73. * To attach a behavior to a component, call {@link attachBehavior}; and to detach the behavior
  74. * from the component, call {@link detachBehavior}.
  75. *
  76. * A behavior can be temporarily enabled or disabled by calling {@link enableBehavior}
  77. * or {@link disableBehavior}, respectively. When disabled, the behavior methods cannot
  78. * be invoked via the component.
  79. *
  80. * @author Qiang Xue <qiang.xue@gmail.com>
  81. * @version $Id: CComponent.php 1465 2009-10-17 11:06:11Z qiang.xue $
  82. * @package system.base
  83. * @since 1.0
  84. */
  85. class CComponent
  86. {
  87. private $_e;
  88. private $_m;
  89. /**
  90. * Returns a property value, an event handler list or a behavior based on its name.
  91. * Do not call this method. This is a PHP magic method that we override
  92. * to allow using the following syntax to read a property or obtain event handlers:
  93. * <pre>
  94. * $value=$component->propertyName;
  95. * $handlers=$component->eventName;
  96. * </pre>
  97. * @param string the property name or event name
  98. * @return mixed the property value, event handlers attached to the event, or the named behavior (since version 1.0.2)
  99. * @throws CException if the property or event is not defined
  100. * @see __set
  101. */
  102. public function __get($name)
  103. {
  104. $getter='get'.$name;
  105. if(method_exists($this,$getter))
  106. return $this->$getter();
  107. else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
  108. {
  109. // duplicating getEventHandlers() here for performance
  110. $name=strtolower($name);
  111. if(!isset($this->_e[$name]))
  112. $this->_e[$name]=new CList;
  113. return $this->_e[$name];
  114. }
  115. else if(isset($this->_m[$name]))
  116. return $this->_m[$name];
  117. else
  118. throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.',
  119. array('{class}'=>get_class($this), '{property}'=>$name)));
  120. }
  121. /**
  122. * Sets value of a component property.
  123. * Do not call this method. This is a PHP magic method that we override
  124. * to allow using the following syntax to set a property or attach an event handler
  125. * <pre>
  126. * $this->propertyName=$value;
  127. * $this->eventName=$callback;
  128. * </pre>
  129. * @param string the property name or the event name
  130. * @param mixed the property value or callback
  131. * @throws CException if the property/event is not defined or the property is read only.
  132. * @see __get
  133. */
  134. public function __set($name,$value)
  135. {
  136. $setter='set'.$name;
  137. if(method_exists($this,$setter))
  138. $this->$setter($value);
  139. else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
  140. {
  141. // duplicating getEventHandlers() here for performance
  142. $name=strtolower($name);
  143. if(!isset($this->_e[$name]))
  144. $this->_e[$name]=new CList;
  145. $this->_e[$name]->add($value);
  146. }
  147. else if(method_exists($this,'get'.$name))
  148. throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.',
  149. array('{class}'=>get_class($this), '{property}'=>$name)));
  150. else
  151. throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.',
  152. array('{class}'=>get_class($this), '{property}'=>$name)));
  153. }
  154. /**
  155. * Checks if a property value is null.
  156. * Do not call this method. This is a PHP magic method that we override
  157. * to allow using isset() to detect if a component property is set or not.
  158. * @param string the property name or the event name
  159. * @since 1.0.1
  160. */
  161. public function __isset($name)
  162. {
  163. $getter='get'.$name;
  164. if(method_exists($this,$getter))
  165. return $this->$getter()!==null;
  166. else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
  167. {
  168. $name=strtolower($name);
  169. return isset($this->_e[$name]) && $this->_e[$name]->getCount();
  170. }
  171. else
  172. return false;
  173. }
  174. /**
  175. * Sets a component property to be null.
  176. * Do not call this method. This is a PHP magic method that we override
  177. * to allow using unset() to set a component property to be null.
  178. * @param string the property name or the event name
  179. * @throws CException if the property is read only.
  180. * @since 1.0.1
  181. */
  182. public function __unset($name)
  183. {
  184. $setter='set'.$name;
  185. if(method_exists($this,$setter))
  186. $this->$setter(null);
  187. else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
  188. unset($this->_e[strtolower($name)]);
  189. else if(method_exists($this,'get'.$name))
  190. throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.',
  191. array('{class}'=>get_class($this), '{property}'=>$name)));
  192. }
  193. /**
  194. * Calls the named method which is not a class method.
  195. * Do not call this method. This is a PHP magic method that we override
  196. * to implement the behavior feature.
  197. * @param string the method name
  198. * @param array method parameters
  199. * @return mixed the method return value
  200. * @since 1.0.2
  201. */
  202. public function __call($name,$parameters)
  203. {
  204. if($this->_m!==null)
  205. {
  206. foreach($this->_m as $object)
  207. {
  208. if($object->enabled && method_exists($object,$name))
  209. return call_user_func_array(array($object,$name),$parameters);
  210. }
  211. }
  212. throw new CException(Yii::t('yii','{class} does not have a method named "{name}".',
  213. array('{class}'=>get_class($this), '{name}'=>$name)));
  214. }
  215. /**
  216. * Returns the named behavior object.
  217. * The name 'asa' stands for 'as a'.
  218. * @param string the behavior name
  219. * @return IBehavior the behavior object, or null if the behavior does not exist
  220. * @since 1.0.2
  221. */
  222. public function asa($behavior)
  223. {
  224. return isset($this->_m[$behavior]) ? $this->_m[$behavior] : null;
  225. }
  226. /**
  227. * Attaches a list of behaviors to the component.
  228. * Each behavior is indexed by its name and should be an instance of
  229. * {@link IBehavior}, a string specifying the behavior class, or an
  230. * array of the following structure:
  231. * <pre>
  232. * array(
  233. * 'class'=>'path.to.BehaviorClass',
  234. * 'property1'=>'value1',
  235. * 'property2'=>'value2',
  236. * )
  237. * </pre>
  238. * @param array list of behaviors to be attached to the component
  239. * @since 1.0.2
  240. */
  241. public function attachBehaviors($behaviors)
  242. {
  243. foreach($behaviors as $name=>$behavior)
  244. $this->attachBehavior($name,$behavior);
  245. }
  246. /**
  247. * Detaches all behaviors from the component.
  248. * @since 1.0.2
  249. */
  250. public function detachBehaviors()
  251. {
  252. if($this->_m!==null)
  253. {
  254. foreach($this->_m as $name=>$behavior)
  255. $this->detachBehavior($name);
  256. $this->_m=null;
  257. }
  258. }
  259. /**
  260. * Attaches a behavior to this component.
  261. * This method will create the behavior object based on the given
  262. * configuration. After that, the behavior object will be initialized
  263. * by calling its {@link IBehavior::attach} method.
  264. * @param string the behavior's name. It should uniquely identify this behavior.
  265. * @param mixed the behavior configuration. This is passed as the first
  266. * parameter to {@link YiiBase::createComponent} to create the behavior object.
  267. * @return IBehavior the behavior object
  268. * @since 1.0.2
  269. */
  270. public function attachBehavior($name,$behavior)
  271. {
  272. if(!($behavior instanceof IBehavior))
  273. $behavior=Yii::createComponent($behavior);
  274. $behavior->setEnabled(true);
  275. $behavior->attach($this);
  276. return $this->_m[$name]=$behavior;
  277. }
  278. /**
  279. * Detaches a behavior from the component.
  280. * The behavior's {@link IBehavior::detach} method will be invoked.
  281. * @param string the behavior's name. It uniquely identifies the behavior.
  282. * @return IBehavior the detached behavior. Null if the behavior does not exist.
  283. * @since 1.0.2
  284. */
  285. public function detachBehavior($name)
  286. {
  287. if(isset($this->_m[$name]))
  288. {
  289. $this->_m[$name]->detach($this);
  290. $behavior=$this->_m[$name];
  291. unset($this->_m[$name]);
  292. return $behavior;
  293. }
  294. }
  295. /**
  296. * Enables all behaviors attached to this component.
  297. * @since 1.0.2
  298. */
  299. public function enableBehaviors()
  300. {
  301. if($this->_m!==null)
  302. {
  303. foreach($this->_m as $behavior)
  304. $behavior->setEnabled(true);
  305. }
  306. }
  307. /**
  308. * Disables all behaviors attached to this component.
  309. * @since 1.0.2
  310. */
  311. public function disableBehaviors()
  312. {
  313. if($this->_m!==null)
  314. {
  315. foreach($this->_m as $behavior)
  316. $behavior->setEnabled(false);
  317. }
  318. }
  319. /**
  320. * Enables an attached behavior.
  321. * A behavior is only effective when it is enabled.
  322. * A behavior is enabled when first attached.
  323. * @param string the behavior's name. It uniquely identifies the behavior.
  324. * @since 1.0.2
  325. */
  326. public function enableBehavior($name)
  327. {
  328. if(isset($this->_m[$name]))
  329. $this->_m[$name]->setEnabled(true);
  330. }
  331. /**
  332. * Disables an attached behavior.
  333. * A behavior is only effective when it is enabled.
  334. * @param string the behavior's name. It uniquely identifies the behavior.
  335. * @since 1.0.2
  336. */
  337. public function disableBehavior($name)
  338. {
  339. if(isset($this->_m[$name]))
  340. $this->_m[$name]->setEnabled(false);
  341. }
  342. /**
  343. * Determines whether a property is defined.
  344. * A property is defined if there is a getter or setter method
  345. * defined in the class. Note, property names are case-insensitive.
  346. * @param string the property name
  347. * @return boolean whether the property is defined
  348. * @see canGetProperty
  349. * @see canSetProperty
  350. */
  351. public function hasProperty($name)
  352. {
  353. return method_exists($this,'get'.$name) || method_exists($this,'set'.$name);
  354. }
  355. /**
  356. * Determines whether a property can be read.
  357. * A property can be read if the class has a getter method
  358. * for the property name. Note, property name is case-insensitive.
  359. * @param string the property name
  360. * @return boolean whether the property can be read
  361. * @see canSetProperty
  362. */
  363. public function canGetProperty($name)
  364. {
  365. return method_exists($this,'get'.$name);
  366. }
  367. /**
  368. * Determines whether a property can be set.
  369. * A property can be written if the class has a setter method
  370. * for the property name. Note, property name is case-insensitive.
  371. * @param string the property name
  372. * @return boolean whether the property can be written
  373. * @see canGetProperty
  374. */
  375. public function canSetProperty($name)
  376. {
  377. return method_exists($this,'set'.$name);
  378. }
  379. /**
  380. * Determines whether an event is defined.
  381. * An event is defined if the class has a method named like 'onXXX'.
  382. * Note, event name is case-insensitive.
  383. * @param string the event name
  384. * @return boolean whether an event is defined
  385. */
  386. public function hasEvent($name)
  387. {
  388. return !strncasecmp($name,'on',2) && method_exists($this,$name);
  389. }
  390. /**
  391. * Checks whether the named event has attached handlers.
  392. * @param string the event name
  393. * @return boolean whether an event has been attached one or several handlers
  394. */
  395. public function hasEventHandler($name)
  396. {
  397. $name=strtolower($name);
  398. return isset($this->_e[$name]) && $this->_e[$name]->getCount()>0;
  399. }
  400. /**
  401. * Returns the list of attached event handlers for an event.
  402. * @param string the event name
  403. * @return CList list of attached event handlers for the event
  404. * @throws CException if the event is not defined
  405. */
  406. public function getEventHandlers($name)
  407. {
  408. if($this->hasEvent($name))
  409. {
  410. $name=strtolower($name);
  411. if(!isset($this->_e[$name]))
  412. $this->_e[$name]=new CList;
  413. return $this->_e[$name];
  414. }
  415. else
  416. throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.',
  417. array('{class}'=>get_class($this), '{event}'=>$name)));
  418. }
  419. /**
  420. * Attaches an event handler to an event.
  421. *
  422. * An event handler must be a valid PHP callback, i.e., a string referring to
  423. * a global function name, or an array containing two elements with
  424. * the first element being an object and the second element a method name
  425. * of the object.
  426. *
  427. * An event handler must be defined with the following signature,
  428. * <pre>
  429. * function handlerName($event) {}
  430. * </pre>
  431. * where $event includes parameters associated with the event.
  432. *
  433. * This is a convenient method of attaching a handler to an event.
  434. * It is equivalent to the following code:
  435. * <pre>
  436. * $component->getEventHandlers($eventName)->add($eventHandler);
  437. * </pre>
  438. *
  439. * Using {@link getEventHandlers}, one can also specify the excution order
  440. * of multiple handlers attaching to the same event. For example:
  441. * <pre>
  442. * $component->getEventHandlers($eventName)->insertAt(0,$eventHandler);
  443. * </pre>
  444. * makes the handler to be invoked first.
  445. *
  446. * @param string the event name
  447. * @param callback the event handler
  448. * @throws CException if the event is not defined
  449. * @see detachEventHandler
  450. */
  451. public function attachEventHandler($name,$handler)
  452. {
  453. $this->getEventHandlers($name)->add($handler);
  454. }
  455. /**
  456. * Detaches an existing event handler.
  457. * This method is the opposite of {@link attachEventHandler}.
  458. * @param string event name
  459. * @param callback the event handler to be removed
  460. * @return boolean if the detachment process is successful
  461. * @see attachEventHandler
  462. */
  463. public function detachEventHandler($name,$handler)
  464. {
  465. if($this->hasEventHandler($name))
  466. return $this->getEventHandlers($name)->remove($handler)!==false;
  467. else
  468. return false;
  469. }
  470. /**
  471. * Raises an event.
  472. * This method represents the happening of an event. It invokes
  473. * all attached handlers for the event.
  474. * @param string the event name
  475. * @param CEvent the event parameter
  476. * @throws CException if the event is undefined or an event handler is invalid.
  477. */
  478. public function raiseEvent($name,$event)
  479. {
  480. $name=strtolower($name);
  481. if(isset($this->_e[$name]))
  482. {
  483. foreach($this->_e[$name] as $handler)
  484. {
  485. if(is_string($handler))
  486. call_user_func($handler,$event);
  487. else if(is_callable($handler,true))
  488. {
  489. if(is_array($handler))
  490. {
  491. // an array: 0 - object, 1 - method name
  492. list($object,$method)=$handler;
  493. if(is_string($object)) // static method call
  494. call_user_func($handler,$event);
  495. else if(method_exists($object,$method))
  496. $object->$method($event);
  497. else
  498. throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler "{handler}".',
  499. array('{class}'=>get_class($this), '{event}'=>$name, '{handler}'=>$handler[1])));
  500. }
  501. else // PHP 5.3: anonymous function
  502. call_user_func($handler,$event);
  503. }
  504. else
  505. throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler "{handler}".',
  506. array('{class}'=>get_class($this), '{event}'=>$name, '{handler}'=>gettype($handler))));
  507. // stop further handling if param.handled is set true
  508. if(($event instanceof CEvent) && $event->handled)
  509. return;
  510. }
  511. }
  512. else if(YII_DEBUG && !$this->hasEvent($name))
  513. throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.',
  514. array('{class}'=>get_class($this), '{event}'=>$name)));
  515. }
  516. }
  517. /**
  518. * CEvent is the base class for all event classes.
  519. *
  520. * It encapsulates the parameters associated with an event.
  521. * The {@link sender} property describes who raises the event.
  522. * And the {@link handled} property indicates if the event is handled.
  523. * If an event handler sets {@link handled} to true, those handlers
  524. * that are not invoked yet will not be invoked anymore.
  525. *
  526. * @author Qiang Xue <qiang.xue@gmail.com>
  527. * @version $Id: CComponent.php 1465 2009-10-17 11:06:11Z qiang.xue $
  528. * @package system.base
  529. * @since 1.0
  530. */
  531. class CEvent extends CComponent
  532. {
  533. /**
  534. * @var object the sender of this event
  535. */
  536. public $sender;
  537. /**
  538. * @var boolean whether the event is handled. Defaults to false.
  539. * When a handler sets this true, the rest uninvoked handlers will not be invoked anymore.
  540. */
  541. public $handled=false;
  542. /**
  543. * Constructor.
  544. * @param mixed sender of the event
  545. */
  546. public function __construct($sender=null)
  547. {
  548. $this->sender=$sender;
  549. }
  550. }
  551. /**
  552. * CEnumerable is the base class for all enumerable types.
  553. *
  554. * To define an enumerable type, extend CEnumberable and define string constants.
  555. * Each constant represents an enumerable value.
  556. * The constant name must be the same as the constant value.
  557. * For example,
  558. * <pre>
  559. * class TextAlign extends CEnumerable
  560. * {
  561. * const Left='Left';
  562. * const Right='Right';
  563. * }
  564. * </pre>
  565. * Then, one can use the enumerable values such as TextAlign::Left and
  566. * TextAlign::Right.
  567. *
  568. * @author Qiang Xue <qiang.xue@gmail.com>
  569. * @version $Id: CComponent.php 1465 2009-10-17 11:06:11Z qiang.xue $
  570. * @package system.base
  571. * @since 1.0
  572. */
  573. class CEnumerable
  574. {
  575. }