PageRenderTime 60ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/framework/TComponent.php

http://prado3.googlecode.com/
PHP | 1694 lines | 675 code | 103 blank | 916 comment | 158 complexity | 5b995e2637e9a205f42ac5d37137bebe MD5 | raw file
Possible License(s): Apache-2.0, IPL-1.0, LGPL-3.0, LGPL-2.1, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * TComponent, TPropertyValue classes
  4. * @author Qiang Xue <qiang.xue@gmail.com>
  5. *
  6. * Global Events, intra-object events, Class behaviors, expanded behaviors
  7. * @author Brad Anderson <javalizard@mac.com>
  8. *
  9. * @link http://www.pradosoft.com/
  10. * @copyright Copyright &copy; 2005-2012 PradoSoft
  11. * @license http://www.pradosoft.com/license/
  12. * @version $Id: TComponent.php 3212 2012-10-31 09:40:15Z ctrlaltca $
  13. * @package System
  14. */
  15. /**
  16. * TComponent class
  17. *
  18. * TComponent is the base class for all PRADO components.
  19. * TComponent implements the protocol of defining, using properties, behaviors,
  20. * and events.
  21. *
  22. * A property is defined by a getter method, and/or a setter method.
  23. * Properties can be accessed in the way like accessing normal object members.
  24. * Reading or writing a property will cause the invocation of the corresponding
  25. * getter or setter method, e.g.,
  26. * <code>
  27. * $a=$this->Text; // equivalent to $a=$this->getText();
  28. * $this->Text='abc'; // equivalent to $this->setText('abc');
  29. * </code>
  30. * The signatures of getter and setter methods are as follows,
  31. * <code>
  32. * // getter, defines a readable property 'Text'
  33. * function getText() { ... }
  34. * // setter, defines a writable property 'Text', with $value being the value to be set to the property
  35. * function setText($value) { ... }
  36. * </code>
  37. * Property names are case-insensitive. It is recommended that they are written
  38. * in the format of concatenated words, with the first letter of each word
  39. * capitalized (e.g. DisplayMode, ItemStyle).
  40. *
  41. * Javascript Get and Set
  42. *
  43. * Since Prado 3.2 a new class of javascript-friendly properties have been introduced
  44. * to better deal with potential security problems like cross-site scripting issues.
  45. * All the data that gets sent clientside inside a javascript block is now encoded by default.
  46. * Sometimes there's the need to bypass this encoding and be able to send raw javascript code.
  47. * This new class of javascript-friendly properties are identified by their name
  48. * starting with 'js' (case insensitive):
  49. * <code>
  50. * // getter, defines a readable property 'Text'
  51. * function getJsText() { ... }
  52. * // setter, defines a writable property 'Text', with $value being the value to be set to the property
  53. * function setJsText(TJavaScriptLiteral $value) { ... }
  54. * </code>
  55. * Js-friendly properties can be accessed using both their Js-less name and their Js-enabled name:
  56. * <code>
  57. * // set some simple text as property value
  58. * $component->Text = 'text';
  59. * // set some javascript code as property value
  60. * $component->JsText = 'raw javascript';
  61. * </code>
  62. * In the first case, the property value will automatically gets encoded when sent
  63. * clientside inside a javascript block.
  64. * In the second case, the property will be 'marked' as being a safe javascript
  65. * statement and will not be encoded when rendered inside a javascript block.
  66. * This special handling makes use of the {@link TJavaScriptLiteral} class.
  67. *
  68. * Events
  69. *
  70. * An event is defined by the presence of a method whose name starts with 'on'.
  71. * The event name is the method name and is thus case-insensitive.
  72. * An event can be attached with one or several methods (called event handlers).
  73. * An event can be raised by calling {@link raiseEvent} method, upon which
  74. * the attached event handlers will be invoked automatically in the order they
  75. * are attached to the event. Event handlers must have the following signature,
  76. * <code>
  77. * function eventHandlerFuncName($sender,$param) { ... }
  78. * </code>
  79. * where $sender refers to the object who is responsible for the raising of the event,
  80. * and $param refers to a structure that may contain event-specific information.
  81. * To raise an event (assuming named as 'Click') of a component, use
  82. * <code>
  83. * $component->raiseEvent('OnClick');
  84. * $component->raiseEvent('OnClick', $this, $param);
  85. * </code>
  86. * To attach an event handler to an event, use one of the following ways,
  87. * <code>
  88. * $component->OnClick=$callback; // or $component->OnClick->add($callback);
  89. * $component->attachEventHandler('OnClick',$callback);
  90. * </code>
  91. * The first two ways make use of the fact that $component->OnClick refers to
  92. * the event handler list {@link TPriorityList} for the 'OnClick' event.
  93. * The variable $callback contains the definition of the event handler that can
  94. * be either a string referring to a global function name, or an array whose
  95. * first element refers to an object and second element a method name/path that
  96. * is reachable by the object, e.g.
  97. * - 'buttonClicked' : buttonClicked($sender,$param);
  98. * - array($object,'buttonClicked') : $object->buttonClicked($sender,$param);
  99. * - array($object,'MainContent.SubmitButton.buttonClicked') :
  100. * $object->MainContent->SubmitButton->buttonClicked($sender,$param);
  101. *
  102. * @author Qiang Xue <qiang.xue@gmail.com>
  103. *
  104. * With the addition of behaviors, a more expansive event model is needed. There
  105. * are two new event types (global and dynamic events) as well as a more comprehensive
  106. * behavior model that includes class wide behaviors.
  107. *
  108. * A global event is defined by all events whose name starts with 'fx'.
  109. * The event name is potentially a method name and is thus case-insensitive. All 'fx' events
  110. * are valid as the whole 'fx' event/method space is global in nature. Any object may patch into
  111. * any global event by defining that event as a method. Global events have priorities
  112. * just like 'on' events; so as to be able to order the event execution. Due to the
  113. * nature of all events which start with 'fx' being valid, in effect, every object
  114. * has every 'fx' global event. It is simply an issue of tapping into the desired
  115. * global event.
  116. *
  117. * A global event that starts with 'fx' can be called even if the object does not
  118. * implement the method of the global event. A call to a non-existing 'fx' method
  119. * will, at minimal, function and return null. If a method argument list has a first
  120. * parameter, it will be returned instead of null. This allows filtering and chaining.
  121. * 'fx' methods do not automatically install and uninstall. To install and uninstall an
  122. * object's global event listeners, call the object's {@link listen} and
  123. * {@link unlisten} methods, respectively. An object may auto-install its global event
  124. * during {@link __construct} by overriding {@link getAutoGlobalListen} and returning true.
  125. *
  126. * As of PHP version 5.3, nulled objects without code references will still continue to persist
  127. * in the global event queue because {@link __destruct} is not automatically called. In the common
  128. * __destruct method, if an object is listening to global events, then {@link unlisten} is called.
  129. * {@link unlisten} is required to be manually called before an object is
  130. * left without references if it is currently listening to any global events. This includes
  131. * class wide behaviors.
  132. *
  133. * An object that contains a method that starts with 'fx' will have those functions
  134. * automatically receive those events of the same name after {@link listen} is called on the object.
  135. *
  136. * An object may listen to a global event without defining an 'fx' method of the same name by
  137. * adding an object method to the global event list. For example
  138. * <code>
  139. * $component->fxGlobalCheck=$callback; // or $component->OnClick->add($callback);
  140. * $component->attachEventHandler('fxGlobalCheck',array($object, 'someMethod'));
  141. * </code>
  142. *
  143. * Events between Objects and their behaviors, Dynamic Events
  144. *
  145. * An intra-object/behavior event is defined by methods that start with 'dy'. Just as with
  146. * 'fx' global events, every object has every dynamic event. Any call to a method that
  147. * starts with 'dy' will be handled, regardless of whether it is implemented. These
  148. * events are for communicating with attached behaviors.
  149. *
  150. * Dynamic events can be used in a variety of ways. They can be used to tell behaviors
  151. * when a non-behavior method is called. Dynamic events could be used as data filters.
  152. * They could also be used to specify when a piece of code is to be run, eg. should the
  153. * loop process be performed on a particular piece of data. In this way, some control
  154. * is handed to the behaviors over the process and/or data.
  155. *
  156. * If there are no handlers for an 'fx' or 'dy' event, it will return the first
  157. * parameter of the argument list. If there are no arguments, these events
  158. * will return null. If there are handlers an 'fx' method will be called directly
  159. * within the object. Global 'fx' events are triggered by calling {@link raiseEvent}.
  160. * For dynamic events where there are behaviors that respond to the dynamic events, a
  161. * {@link TCallChain} is developed. A call chain allows the behavior dynamic event
  162. * implementations to call further implementing behaviors within a chain.
  163. *
  164. * If an object implements {@link IDynamicMethods}, all global and object dynamic
  165. * events will be sent to {@link __dycall}. In the case of global events, all
  166. * global events will trigger this method. In the case of behaviors, all undefined
  167. * dynamic events which are called will be passed through to this method.
  168. *
  169. *
  170. * Behaviors
  171. *
  172. * There are two types of behaviors. There are individual object behaviors and
  173. * there are class wide behaviors. Class behaviors depend upon object behaviors.
  174. *
  175. * When a new class implements {@link IBehavior} or {@link IClassBehavior} or
  176. * extends {@link TBehavior} or {@link TClassBehavior}, it may be added to an
  177. * object by calling the object's {@link attachBehavior}. The behaviors associated
  178. * name can then be used to {@link enableBehavior} or {@link disableBehavior}
  179. * the specific behavior.
  180. *
  181. * All behaviors may be turned on and off via {@link enableBehaviors} and
  182. * {@link disableBehaviors}, respectively. To check if behaviors are on or off
  183. * a call to {@link getBehaviorsEnabled} will provide the variable.
  184. *
  185. * Attaching and detaching whole sets of behaviors is done using
  186. * {@link attachBehaviors} and {@link detachBehaviors}. {@link clearBehaviors}
  187. * removes all of an object's behaviors.
  188. *
  189. * {@link asa} returns a behavior of a specific name. {@link isa} is the
  190. * behavior inclusive function that acts as the PHP operator {@link instanceof}.
  191. * A behavior could provide the functionality of a specific class thus causing
  192. * the host object to act similarly to a completely different class. A behavior
  193. * would then implement {@link IInstanceCheck} to provide the identity of the
  194. * different class.
  195. *
  196. * Class behaviors are similar to object behaviors except that the class behavior
  197. * is the implementation for all instances of the class. A class behavior
  198. * will have the object upon which is being called be prepended to the parameter
  199. * list. This way the object is known across the class behavior implementation.
  200. *
  201. * Class behaviors are attached using {@link attachClassBehavior} and detached
  202. * using {@link detachClassBehavior}. Class behaviors are important in that
  203. * they will be applied to all new instances of a particular class. In this way
  204. * class behaviors become default behaviors to a new instances of a class in
  205. * {@link __construct}. Detaching a class behavior will remove the behavior
  206. * from the default set of behaviors created for an object when the object
  207. * is instanced.
  208. *
  209. * Class behaviors are also added to all existing instances via the global 'fx'
  210. * event mechanism. When a new class behavior is added, the event
  211. * {@link fxAttachClassBehavior} is raised and all existing instances that are
  212. * listening to this global event (primarily after {@link listen} is called)
  213. * will have this new behavior attached. A similar process is used when
  214. * detaching class behaviors. Any objects listening to the global 'fx' event
  215. * {@link fxDetachClassBehavior} will have a class behavior removed.
  216. *
  217. * Dynamic Intra-Object Events
  218. *
  219. * Dynamic events start with 'dy'. This mechanism is used to allow objects
  220. * to communicate with their behaviors directly. The entire 'dy' event space
  221. * is valid. All attached, enabled behaviors that implement a dynamic event
  222. * are called when the host object calls the dynamic event. If there is no
  223. * implementation or behaviors, this returns null when no parameters are
  224. * supplied and will return the first parameter when there is at least one
  225. * parameter in the dynamic event.
  226. * <code>
  227. * null == $this->dyBehaviorEvent();
  228. * 5 == $this->dyBehaviorEvent(5); //when no behaviors implement this dynamic event
  229. * </code>
  230. *
  231. * Dynamic events can be chained together within behaviors to allow for data
  232. * filtering. Dynamic events are implemented within behaviors by defining the
  233. * event as a method.
  234. * <code>
  235. * class TObjectBehavior extends TBehavior {
  236. * public function dyBehaviorEvent($param1, $callchain) {
  237. * //Do something, eg: $param1 += 13;
  238. * return $callchain->dyBehaviorEvent($param1);
  239. * }
  240. * }
  241. * </code>
  242. * This implementation of a behavior and dynamic event will flow through to the
  243. * next behavior implementing the dynamic event. The first parameter is always
  244. * return when it is supplied. Otherwise a dynamic event returns null.
  245. *
  246. * In the case of a class behavior, the object is also prepended to the dynamic
  247. * event.
  248. * <code>
  249. * class TObjectClassBehavior extends TClassBehavior {
  250. * public function dyBehaviorEvent($hostobject, $param1, $callchain) {
  251. * //Do something, eg: $param1 += $hostobject->getNumber();
  252. * return $callchain->dyBehaviorEvent($param1);
  253. * }
  254. * }
  255. * </code>
  256. * When calling a dynamic event, only the parameters are passed. The host object
  257. * and the call chain are built into the framework.
  258. *
  259. * Global Event and Dynamic event catching
  260. *
  261. * Given that all global 'fx' events and dynamic 'dy' events are valid and
  262. * operational, there is a mechanism for catching events called that are not
  263. * implemented (similar to the built-in PHP method {@link __call}). When
  264. * a dynamic or global event is called but a behavior does not implement it,
  265. * yet desires to know when an undefined dynamic event is run, the behavior
  266. * implements the interface {@link IDynamicMethods} and method {@link __dycall}.
  267. *
  268. * In the case of dynamic events, {@link __dycall} is supplied with the method
  269. * name and its parameters. When a global event is raised, via {@link raiseEvent},
  270. * the method is the event name and the parameters are supplied.
  271. *
  272. * When implemented, this catch-all mechanism is called for event global event event
  273. * when implemented outside of a behavior. Within a behavior, it will also be called
  274. * when the object to which the behavior is attached calls any unimplemented dynamic
  275. * event. This is the fall-back mechanism for informing a class and/or behavior
  276. * of when an global and/or undefined dynamic event is executed.
  277. *
  278. * @author Brad Anderson <javalizard@mac.com>
  279. * @version $Id: TComponent.php 3212 2012-10-31 09:40:15Z ctrlaltca $
  280. * @package System
  281. * @since 3.0
  282. */
  283. class TComponent
  284. {
  285. /**
  286. * @var array event handler lists
  287. */
  288. private $_e=array();
  289. /**
  290. * @var boolean if listening is enabled. Automatically turned on or off in
  291. * constructor according to {@link getAutoGlobalListen}. Default false, off
  292. */
  293. private $_listeningenabled=false;
  294. /**
  295. * @var array static registered global event handler lists
  296. */
  297. private static $_ue=array();
  298. /**
  299. * @var boolean if object behaviors are on or off. default true, on
  300. */
  301. private $_behaviorsenabled=true;
  302. /**
  303. * @var TPriorityMap list of object behaviors
  304. */
  305. private $_m=null;
  306. /**
  307. * @var array static global class behaviors, these behaviors are added upon instantiation of a class
  308. */
  309. private static $_um=array();
  310. /**
  311. * @const string the name of the global {@link raiseEvent} listener
  312. */
  313. const GLOBAL_RAISE_EVENT_LISTENER='fxGlobalListener';
  314. /**
  315. * The common __construct
  316. * If desired by the new object, this will auto install and listen to global event functions
  317. * as defined by the object via 'fx' methods. This also attaches any predefined behaviors.
  318. * This function installs all class behaviors in a class hierarchy from the deepest subclass
  319. * through each parent to the top most class, TComponent.
  320. */
  321. public function __construct() {
  322. if($this->getAutoGlobalListen())
  323. $this->listen();
  324. $classes=array_reverse($this->getClassHierarchy(true));
  325. foreach($classes as $class) {
  326. if(isset(self::$_um[$class]))
  327. $this->attachBehaviors(self::$_um[$class]);
  328. }
  329. }
  330. /**
  331. * Tells TComponent whether or not to automatically listen to global events.
  332. * Defaults to false because PHP variable cleanup is affected if this is true.
  333. * When unsetting a variable that is listening to global events, {@link unlisten}
  334. * must explicitly be called when cleaning variables allocation or else the global
  335. * event registry will contain references to the old object. This is true for PHP 5.4
  336. *
  337. * Override this method by a subclass to change the setting. When set to true, this
  338. * will enable {@link __construct} to call {@link listen}.
  339. *
  340. * @return boolean whether or not to auto listen to global events during {@link __construct}, default false
  341. */
  342. public function getAutoGlobalListen() {
  343. return false;
  344. }
  345. /**
  346. * The common __destruct
  347. * This unlistens from the global event routines if listening
  348. *
  349. * PHP 5.3 does not __destruct objects when they are nulled and thus unlisten must be
  350. * called must be explicitly called.
  351. */
  352. public function __destruct() {
  353. if($this->_listeningenabled)
  354. $this->unlisten();
  355. }
  356. /**
  357. * This utility function is a private array filter method. The array values
  358. * that start with 'fx' are filtered in.
  359. */
  360. private function filter_prado_fx($name) {
  361. return strncasecmp($name,'fx',2)===0;
  362. }
  363. /**
  364. * This returns an array of the class name and the names of all its parents. The base object first,
  365. * {@link TComponent}, and the deepest subclass is last.
  366. * @param boolean optional should the names be all lowercase true/false
  367. * @return array array of strings being the class hierarchy of $this.
  368. */
  369. public function getClassHierarchy($lowercase = false)
  370. {
  371. $class=get_class($this);
  372. $classes=array($class);
  373. while($class=get_parent_class($class)){array_unshift($classes,$class);}
  374. if($lowercase)
  375. return array_map('strtolower',$classes);
  376. return $classes;
  377. }
  378. /**
  379. * This adds an object's fx event handlers into the global broadcaster to listen into any
  380. * broadcast global events called through {@link raiseEvent}
  381. *
  382. * Behaviors may implement the function:
  383. * <code>
  384. * public function dyListen($globalEvents[, $chain]) {
  385. * $this->listen(); //eg
  386. * }
  387. * </code>
  388. * to be executed when listen is called. All attached behaviors are notified through dyListen.
  389. *
  390. * @return numeric the number of global events that were registered to the global event registry
  391. */
  392. public function listen() {
  393. if($this->_listeningenabled)
  394. return;
  395. $fx=array_filter(get_class_methods($this),array($this,'filter_prado_fx'));
  396. foreach($fx as $func)
  397. $this->attachEventHandler($func,array($this,$func));
  398. if(is_a($this,'IDynamicMethods')) {
  399. $this->attachEventHandler(TComponent::GLOBAL_RAISE_EVENT_LISTENER,array($this,'__dycall'));
  400. array_push($fx,TComponent::GLOBAL_RAISE_EVENT_LISTENER);
  401. }
  402. $this->_listeningenabled=true;
  403. $this->dyListen($fx);
  404. return count($fx);
  405. }
  406. /**
  407. * this removes an object's fx events from the global broadcaster
  408. *
  409. * Behaviors may implement the function:
  410. * <code>
  411. * public function dyUnlisten($globalEvents[, $chain]) {
  412. * $this->behaviorUnlisten(); //eg
  413. * }
  414. * </code>
  415. * to be executed when listen is called. All attached behaviors are notified through dyUnlisten.
  416. *
  417. * @return numeric the number of global events that were unregistered from the global event registry
  418. */
  419. public function unlisten() {
  420. if(!$this->_listeningenabled)
  421. return;
  422. $fx=array_filter(get_class_methods($this),array($this,'filter_prado_fx'));
  423. foreach($fx as $func)
  424. $this->detachEventHandler($func,array($this,$func));
  425. if(is_a($this,'IDynamicMethods')) {
  426. $this->detachEventHandler(TComponent::GLOBAL_RAISE_EVENT_LISTENER,array($this,'__dycall'));
  427. array_push($fx,TComponent::GLOBAL_RAISE_EVENT_LISTENER);
  428. }
  429. $this->_listeningenabled=false;
  430. $this->dyUnlisten($fx);
  431. return count($fx);
  432. }
  433. /**
  434. * Gets the state of listening to global events
  435. * @return boolean is Listening to global broadcast enabled
  436. */
  437. public function getListeningToGlobalEvents()
  438. {
  439. return $this->_listeningenabled;
  440. }
  441. /**
  442. * Calls a method.
  443. * Do not call this method directly. This is a PHP magic method that we override
  444. * to allow behaviors, dynamic events (intra-object/behavior events),
  445. * undefined dynamic and global events, and
  446. * to allow using the following syntax to call a property setter or getter.
  447. * <code>
  448. * $this->getPropertyName($value); // if there's a $this->getjsPropertyName() method
  449. * $this->setPropertyName($value); // if there's a $this->setjsPropertyName() method
  450. * </code>
  451. *
  452. * Additional object behaviors override class behaviors.
  453. * dynamic and global events do not fail even if they aren't implemented.
  454. * Any intra-object/behavior dynamic events that are not implemented by the behavior
  455. * return the first function paramater or null when no parameters are specified.
  456. *
  457. * @param string method name that doesn't exist and is being called on the object
  458. * @param mixed method parameters
  459. * @throws TInvalidOperationException If the property is not defined or read-only or
  460. * method is undefined
  461. * @return mixed result of the method call, or false if 'fx' or 'dy' function but
  462. * is not found in the class, otherwise it runs
  463. */
  464. public function __call($method, $args)
  465. {
  466. $getset=substr($method,0,3);
  467. if(($getset=='get')||($getset=='set'))
  468. {
  469. $propname=substr($method,3);
  470. $jsmethod=$getset.'js'.$propname;
  471. if(method_exists($this,$jsmethod))
  472. {
  473. if(count($args)>0)
  474. if($args[0]&&!($args[0] instanceof TJavaScriptString))
  475. $args[0]=new TJavaScriptString($args[0]);
  476. return call_user_func_array(array($this,$jsmethod),$args);
  477. }
  478. if (($getset=='set')&&method_exists($this,'getjs'.$propname))
  479. throw new TInvalidOperationException('component_property_readonly',get_class($this),$method);
  480. }
  481. if($this->_m!==null&&$this->_behaviorsenabled)
  482. {
  483. if(strncasecmp($method,'dy',2)===0)
  484. {
  485. $callchain=new TCallChain($method);
  486. foreach($this->_m->toArray() as $behavior)
  487. {
  488. if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&(method_exists($behavior,$method)||($behavior instanceof IDynamicMethods)))
  489. {
  490. $behavior_args=$args;
  491. if($behavior instanceof IClassBehavior)
  492. array_unshift($behavior_args,$this);
  493. $callchain->addCall(array($behavior,$method),$behavior_args);
  494. }
  495. }
  496. if($callchain->getCount()>0)
  497. return call_user_func_array(array($callchain,'call'),$args);
  498. }
  499. else
  500. {
  501. foreach($this->_m->toArray() as $behavior)
  502. {
  503. if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&method_exists($behavior,$method))
  504. {
  505. if($behavior instanceof IClassBehavior)
  506. array_unshift($args,$this);
  507. return call_user_func_array(array($behavior,$method),$args);
  508. }
  509. }
  510. }
  511. }
  512. if(strncasecmp($method,'dy',2)===0||strncasecmp($method,'fx',2)===0)
  513. {
  514. if($this instanceof IDynamicMethods)
  515. return $this->__dycall($method,$args);
  516. return isset($args[0])?$args[0]:null;
  517. }
  518. throw new TApplicationException('component_method_undefined',get_class($this),$method);
  519. }
  520. /**
  521. * Returns a property value or an event handler list by property or event name.
  522. * Do not call this method. This is a PHP magic method that we override
  523. * to allow using the following syntax to read a property:
  524. * <code>
  525. * $value=$component->PropertyName;
  526. * $value=$component->jsPropertyName; // return JavaScript literal
  527. * </code>
  528. * and to obtain the event handler list for an event,
  529. * <code>
  530. * $eventHandlerList=$component->EventName;
  531. * </code>
  532. * This will also return the global event handler list when specifing an 'fx'
  533. * event,
  534. * <code>
  535. * $globalEventHandlerList=$component->fxEventName;
  536. * </code>
  537. * When behaviors are enabled, this will return the behavior of a specific
  538. * name, a property of a behavior, or an object 'on' event defined by the behavior.
  539. * @param string the property name or the event name
  540. * @return mixed the property value or the event handler list as {@link TPriorityList}
  541. * @throws TInvalidOperationException if the property/event is not defined.
  542. */
  543. public function __get($name)
  544. {
  545. if(method_exists($this,$getter='get'.$name))
  546. {
  547. // getting a property
  548. return $this->$getter();
  549. }
  550. else if(method_exists($this,$jsgetter='getjs'.$name))
  551. {
  552. // getting a javascript property
  553. return (string)$this->$jsgetter();
  554. }
  555. else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
  556. {
  557. // getting an event (handler list)
  558. $name=strtolower($name);
  559. if(!isset($this->_e[$name]))
  560. $this->_e[$name]=new TPriorityList;
  561. return $this->_e[$name];
  562. }
  563. else if(strncasecmp($name,'fx',2)===0)
  564. {
  565. // getting a global event (handler list)
  566. $name=strtolower($name);
  567. if(!isset(self::$_ue[$name]))
  568. self::$_ue[$name]=new TPriorityList;
  569. return self::$_ue[$name];
  570. }
  571. else if($this->_behaviorsenabled)
  572. {
  573. // getting a behavior property/event (handler list)
  574. if(isset($this->_m[$name]))
  575. return $this->_m[$name];
  576. else if($this->_m!==null)
  577. {
  578. foreach($this->_m->toArray() as $behavior)
  579. {
  580. if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&
  581. (property_exists($behavior,$name)||$behavior->canGetProperty($name)||$behavior->hasEvent($name)))
  582. return $behavior->$name;
  583. }
  584. }
  585. }
  586. throw new TInvalidOperationException('component_property_undefined',get_class($this),$name);
  587. }
  588. /**
  589. * Sets value of a component property.
  590. * Do not call this method. This is a PHP magic method that we override
  591. * to allow using the following syntax to set a property or attach an event handler.
  592. * <code>
  593. * $this->PropertyName=$value;
  594. * $this->jsPropertyName=$value; // $value will be treated as a JavaScript literal
  595. * $this->EventName=$handler;
  596. * $this->fxEventName=$handler; //global event listener
  597. * </code>
  598. * When behaviors are enabled, this will also set a behaviors properties and events.
  599. * @param string the property name or event name
  600. * @param mixed the property value or event handler
  601. * @throws TInvalidOperationException If the property is not defined or read-only.
  602. */
  603. public function __set($name,$value)
  604. {
  605. if(method_exists($this,$setter='set'.$name))
  606. {
  607. if(strncasecmp($name,'js',2)===0&&$value&&!($value instanceof TJavaScriptLiteral))
  608. $value = new TJavaScriptLiteral($value);
  609. return $this->$setter($value);
  610. }
  611. else if(method_exists($this,$jssetter='setjs'.$name))
  612. {
  613. if($value&&!($value instanceof TJavaScriptString))
  614. $value=new TJavaScriptString($value);
  615. return $this->$jssetter($value);
  616. }
  617. else if((strncasecmp($name,'on',2)===0&&method_exists($this,$name))||strncasecmp($name,'fx',2)===0)
  618. {
  619. return $this->attachEventHandler($name,$value);
  620. }
  621. else if($this->_m!==null&&$this->_m->getCount()>0&&$this->_behaviorsenabled)
  622. {
  623. $sets=0;
  624. foreach($this->_m->toArray() as $behavior)
  625. {
  626. if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&
  627. (property_exists($behavior,$name)||$behavior->canSetProperty($name)||$behavior->hasEvent($name))) {
  628. $behavior->$name=$value;
  629. $sets++;
  630. }
  631. }
  632. if($sets)return $value;
  633. }
  634. if(method_exists($this,'get'.$name)||method_exists($this,'getjs'.$name))
  635. {
  636. throw new TInvalidOperationException('component_property_readonly',get_class($this),$name);
  637. }
  638. else
  639. {
  640. throw new TInvalidOperationException('component_property_undefined',get_class($this),$name);
  641. }
  642. }
  643. /**
  644. * Checks if a property value is null, there are no events in the object
  645. * event list or global event list registered under the name, and, if
  646. * behaviors are enabled,
  647. * Do not call this method. This is a PHP magic method that we override
  648. * to allow using isset() to detect if a component property is set or not.
  649. * This also works for global events. When behaviors are enabled, it
  650. * will check for a behavior of the specified name, and also check
  651. * the behavior for events and properties.
  652. * @param string the property name or the event name
  653. * @since 3.2.1
  654. */
  655. public function __isset($name)
  656. {
  657. if(method_exists($this,$getter='get'.$name))
  658. return $this->$getter()!==null;
  659. else if(method_exists($this,$jsgetter='getjs'.$name))
  660. return $this->$jsgetter()!==null;
  661. else if(strncasecmp($name,'on',2)===0&&method_exists($this,$name))
  662. {
  663. $name=strtolower($name);
  664. return isset($this->_e[$name])&&$this->_e[$name]->getCount();
  665. }
  666. else if(strncasecmp($name,'fx',2)===0)
  667. {
  668. $name=strtolower($name);
  669. return isset(self::$_ue[$name])&&self::$_ue[$name]->getCount();
  670. }
  671. else if($this->_m!==null&&$this->_m->getCount()>0&&$this->_behaviorsenabled)
  672. {
  673. if(isset($this->_m[$name]))
  674. return true;
  675. foreach($this->_m->toArray() as $behavior)
  676. {
  677. if((!($behavior instanceof IBehavior)||$behavior->getEnabled()))
  678. return isset($behavior->$name);
  679. }
  680. }
  681. else
  682. return false;
  683. }
  684. /**
  685. * Sets a component property to be null. Clears the object or global
  686. * events. When enabled, loops through all behaviors and unsets the
  687. * property or event.
  688. * Do not call this method. This is a PHP magic method that we override
  689. * to allow using unset() to set a component property to be null.
  690. * @param string the property name or the event name
  691. * @throws TInvalidOperationException if the property is read only.
  692. * @since 3.2.1
  693. */
  694. public function __unset($name)
  695. {
  696. if(method_exists($this,$setter='set'.$name))
  697. $this->$setter(null);
  698. else if(method_exists($this,$jssetter='setjs'.$name))
  699. $this->$jssetter(null);
  700. else if(strncasecmp($name,'on',2)===0&&method_exists($this,$name))
  701. $this->_e[strtolower($name)]->clear();
  702. else if(strncasecmp($name,'fx',2)===0)
  703. $this->getEventHandlers($name)->remove(array($this, $name));
  704. else if($this->_m!==null&&$this->_m->getCount()>0&&$this->_behaviorsenabled)
  705. {
  706. if(isset($this->_m[$name]))
  707. $this->detachBehavior($name);
  708. else {
  709. $unset=0;
  710. foreach($this->_m->toArray() as $behavior)
  711. {
  712. if((!($behavior instanceof IBehavior)||$behavior->getEnabled())) {
  713. unset($behavior->$name);
  714. $unset++;
  715. }
  716. }
  717. if(!$unset&&method_exists($this,'get'.$name))
  718. throw new TInvalidOperationException('component_property_readonly',get_class($this),$name);
  719. }
  720. } else if(method_exists($this,'get'.$name))
  721. throw new TInvalidOperationException('component_property_readonly',get_class($this),$name);
  722. }
  723. /**
  724. * Determines whether a property is defined.
  725. * A property is defined if there is a getter or setter method
  726. * defined in the class. Note, property names are case-insensitive.
  727. * @param string the property name
  728. * @return boolean whether the property is defined
  729. */
  730. public function hasProperty($name)
  731. {
  732. return $this->canGetProperty($name)||$this->canSetProperty($name);
  733. }
  734. /**
  735. * Determines whether a property can be read.
  736. * A property can be read if the class has a getter method
  737. * for the property name. Note, property name is case-insensitive.
  738. * This also checks for getjs. When enabled, it loops through all
  739. * active behaviors for the get property when undefined by the object.
  740. * @param string the property name
  741. * @return boolean whether the property can be read
  742. */
  743. public function canGetProperty($name)
  744. {
  745. if(method_exists($this,'get'.$name)||method_exists($this,'getjs'.$name))
  746. return true;
  747. else if($this->_m!==null&&$this->_behaviorsenabled)
  748. {
  749. foreach($this->_m->toArray() as $behavior)
  750. {
  751. if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->canGetProperty($name))
  752. return true;
  753. }
  754. }
  755. return false;
  756. }
  757. /**
  758. * Determines whether a property can be set.
  759. * A property can be written if the class has a setter method
  760. * for the property name. Note, property name is case-insensitive.
  761. * This also checks for setjs. When enabled, it loops through all
  762. * active behaviors for the set property when undefined by the object.
  763. * @param string the property name
  764. * @return boolean whether the property can be written
  765. */
  766. public function canSetProperty($name)
  767. {
  768. if(method_exists($this,'set'.$name)||method_exists($this,'setjs'.$name))
  769. return true;
  770. else if($this->_m!==null&&$this->_behaviorsenabled)
  771. {
  772. foreach($this->_m->toArray() as $behavior)
  773. {
  774. if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->canSetProperty($name))
  775. return true;
  776. }
  777. }
  778. return false;
  779. }
  780. /**
  781. * Evaluates a property path.
  782. * A property path is a sequence of property names concatenated by '.' character.
  783. * For example, 'Parent.Page' refers to the 'Page' property of the component's
  784. * 'Parent' property value (which should be a component also).
  785. * When a property is not defined by an object, this also loops through all
  786. * active behaviors of the object.
  787. * @param string property path
  788. * @return mixed the property path value
  789. */
  790. public function getSubProperty($path)
  791. {
  792. $object=$this;
  793. foreach(explode('.',$path) as $property)
  794. $object=$object->$property;
  795. return $object;
  796. }
  797. /**
  798. * Sets a value to a property path.
  799. * A property path is a sequence of property names concatenated by '.' character.
  800. * For example, 'Parent.Page' refers to the 'Page' property of the component's
  801. * 'Parent' property value (which should be a component also).
  802. * When a property is not defined by an object, this also loops through all
  803. * active behaviors of the object.
  804. * @param string property path
  805. * @param mixed the property path value
  806. */
  807. public function setSubProperty($path,$value)
  808. {
  809. $object=$this;
  810. if(($pos=strrpos($path,'.'))===false)
  811. $property=$path;
  812. else
  813. {
  814. $object=$this->getSubProperty(substr($path,0,$pos));
  815. $property=substr($path,$pos+1);
  816. }
  817. $object->$property=$value;
  818. }
  819. /**
  820. * Determines whether an event is defined.
  821. * An event is defined if the class has a method whose name is the event name
  822. * prefixed with 'on', 'fx', or 'dy'.
  823. * Every object responds to every 'fx' and 'dy' event as they are in a universally
  824. * accepted event space. 'on' event must be declared by the object.
  825. * When enabled, this will loop through all active behaviors for 'on' events
  826. * defined by the behavior.
  827. * Note, event name is case-insensitive.
  828. * @param string the event name
  829. * @return boolean
  830. */
  831. public function hasEvent($name)
  832. {
  833. if((strncasecmp($name,'on',2)===0&&method_exists($this,$name))||strncasecmp($name,'fx',2)===0||strncasecmp($name,'dy',2)===0)
  834. return true;
  835. else if($this->_m!==null&&$this->_behaviorsenabled)
  836. {
  837. foreach($this->_m->toArray() as $behavior)
  838. {
  839. if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->hasEvent($name))
  840. return true;
  841. }
  842. }
  843. return false;
  844. }
  845. /**
  846. * Checks if an event has any handlers. This function also checks through all
  847. * the behaviors for 'on' events when behaviors are enabled.
  848. * 'dy' dynamic events are not handled by this function.
  849. * @param string the event name
  850. * @return boolean whether an event has been attached one or several handlers
  851. */
  852. public function hasEventHandler($name)
  853. {
  854. $name=strtolower($name);
  855. if(strncasecmp($name,'fx',2)===0)
  856. return isset(self::$_ue[$name])&&self::$_ue[$name]->getCount()>0;
  857. else
  858. {
  859. if(isset($this->_e[$name])&&$this->_e[$name]->getCount()>0)
  860. return true;
  861. else if($this->_m!==null&&$this->_behaviorsenabled) {
  862. foreach($this->_m->toArray() as $behavior)
  863. {
  864. if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->hasEventHandler($name))
  865. return true;
  866. }
  867. }
  868. }
  869. return false;
  870. }
  871. /**
  872. * Returns the list of attached event handlers for an 'on' or 'fx' event. This function also
  873. * checks through all the behaviors for 'on' event lists when behaviors are enabled.
  874. * @return TPriorityList list of attached event handlers for an event
  875. * @throws TInvalidOperationException if the event is not defined
  876. */
  877. public function getEventHandlers($name)
  878. {
  879. if(strncasecmp($name,'on',2)===0&&method_exists($this,$name))
  880. {
  881. $name=strtolower($name);
  882. if(!isset($this->_e[$name]))
  883. $this->_e[$name]=new TPriorityList;
  884. return $this->_e[$name];
  885. }
  886. else if(strncasecmp($name,'fx',2)===0)
  887. {
  888. $name=strtolower($name);
  889. if(!isset(self::$_ue[$name]))
  890. self::$_ue[$name]=new TPriorityList;
  891. return self::$_ue[$name];
  892. }
  893. else if($this->_m!==null&&$this->_behaviorsenabled)
  894. {
  895. foreach($this->_m->toArray() as $behavior)
  896. {
  897. if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->hasEvent($name))
  898. return $behavior->getEventHandlers($name);
  899. }
  900. }
  901. throw new TInvalidOperationException('component_event_undefined',get_class($this),$name);
  902. }
  903. /**
  904. * Attaches an event handler to an event.
  905. *
  906. * The handler must be a valid PHP callback, i.e., a string referring to
  907. * a global function name, or an array containing two elements with
  908. * the first element being an object and the second element a method name
  909. * of the object. In Prado, you can also use method path to refer to
  910. * an event handler. For example, array($object,'Parent.buttonClicked')
  911. * uses a method path that refers to the method $object->Parent->buttonClicked(...).
  912. *
  913. * The event handler must be of the following signature,
  914. * <code>
  915. * function handlerName($sender, $param) {}
  916. * function handlerName($sender, $param, $name) {}
  917. * </code>
  918. * where $sender represents the object that raises the event,
  919. * and $param is the event parameter. $name refers to the event name
  920. * being handled.
  921. *
  922. * This is a convenient method to add an event handler.
  923. * It is equivalent to {@link getEventHandlers}($name)->add($handler).
  924. * For complete management of event handlers, use {@link getEventHandlers}
  925. * to get the event handler list first, and then do various
  926. * {@link TPriorityList} operations to append, insert or remove
  927. * event handlers. You may also do these operations like
  928. * getting and setting properties, e.g.,
  929. * <code>
  930. * $component->OnClick[]=array($object,'buttonClicked');
  931. * $component->OnClick->insertAt(0,array($object,'buttonClicked'));
  932. * </code>
  933. * which are equivalent to the following
  934. * <code>
  935. * $component->getEventHandlers('OnClick')->add(array($object,'buttonClicked'));
  936. * $component->getEventHandlers('OnClick')->insertAt(0,array($object,'buttonClicked'));
  937. * </code>
  938. *
  939. * Due to the nature of {@link getEventHandlers}, any active behaviors defining
  940. * new 'on' events, this method will pass through to the behavior transparently.
  941. *
  942. * @param string the event name
  943. * @param callback the event handler
  944. * @param numeric|null the priority of the handler, defaults to null which translates into the
  945. * default priority of 10.0 within {@link TPriorityList}
  946. * @throws TInvalidOperationException if the event does not exist
  947. */
  948. public function attachEventHandler($name,$handler,$priority=null)
  949. {
  950. $this->getEventHandlers($name)->add($handler,$priority);
  951. }
  952. /**
  953. * Detaches an existing event handler.
  954. * This method is the opposite of {@link attachEventHandler}. It will detach
  955. * any 'on' events definedb by an objects active behaviors as well.
  956. * @param string event name
  957. * @param callback the event handler to be removed
  958. * @param numeric|false|null the priority of the handler, defaults to false which translates
  959. * to an item of any priority within {@link TPriorityList}; null means the default priority
  960. * @return boolean if the removal is successful
  961. */
  962. public function detachEventHandler($name,$handler,$priority=false)
  963. {
  964. if($this->hasEventHandler($name))
  965. {
  966. try
  967. {
  968. $this->getEventHandlers($name)->remove($handler,$priority);
  969. return true;
  970. }
  971. catch(Exception $e)
  972. {
  973. }
  974. }
  975. return false;
  976. }
  977. /**
  978. * Raises an event. This raises both inter-object 'on' events and global 'fx' events.
  979. * This method represents the happening of an event and will
  980. * invoke all attached event handlers for the event in {@link TPriorityList} order.
  981. * This method does not handle intra-object/behavior dynamic 'dy' events.
  982. *
  983. * There are ways to handle event responses. By defailt {@link EVENT_RESULT_FILTER},
  984. * all event responses are stored in an array, filtered for null responses, and returned.
  985. * If {@link EVENT_RESULT_ALL} is specified, all returned results will be stored along
  986. * with the sender and param in an array
  987. * <code>
  988. * $result[] = array('sender'=>$sender,'param'=>$param,'response'=>$response);
  989. * </code>
  990. *
  991. * If {@link EVENT_RESULT_FEED_FORWARD} is specified, then each handler result is then
  992. * fed forward as the parameters for the next event. This allows for events to filter data
  993. * directly by affecting the event parameters
  994. *
  995. * If a callable function is set in the response type or the post function filter is specified then the
  996. * result of each called event handler is post processed by the callable function. Used in
  997. * combination with {@link EVENT_RESULT_FEED_FORWARD}, any event (and its result) can be chained.
  998. *
  999. * When raising a global 'fx' event, registered handlers in the global event list for
  1000. * {@link GLOBAL_RAISE_EVENT_LISTENER} are always added into the set of event handlers. In this way,
  1001. * these global events are always raised for every global 'fx' event. The registered handlers for global
  1002. * raiseEvent events have priorities. Any registered global raiseEvent event handlers with a priority less than zero
  1003. * are added before the main event handlers being raised and any registered global raiseEvent event handlers
  1004. * with a priority equal or greater than zero are added after the main event handlers being raised. In this way
  1005. * all {@link GLOBAL_RAISE_EVENT_LISTENER} handlers are always called for every raised 'fx' event.
  1006. *
  1007. * Behaviors may implement the following functions:
  1008. * <code>
  1009. * public function dyPreRaiseEvent($name,$sender,$param,$responsetype,$postfunction[, $chain]) {
  1010. * return $name; //eg, the event name may be filtered/changed
  1011. * }
  1012. * public function dyIntraRaiseEventTestHandler($handler,$sender,$param,$name[, $chain]) {
  1013. * return true; //should this particular handler be executed? true/false
  1014. * }
  1015. * public function dyIntraRaiseEventPostHandler($name,$sender,$param,$handler,$response[, $chain]) {
  1016. * //contains the per handler response
  1017. * }
  1018. * public function dyPostRaiseEvent($responses,$name,$sender,$param,$responsetype,$postfunction[, $chain]) {
  1019. * return $responses;
  1020. * }
  1021. * </code>
  1022. * to be executed when raiseEvent is called. The 'intra' dynamic events are called per handler in
  1023. * the handler loop.
  1024. *
  1025. * dyPreRaiseEvent has the effect of being able to change the event being raised. This intra
  1026. * object/behavior event returns the name of the desired event to be raised. It will pass through
  1027. * if no dynamic event is specified, or if the original event name is returned.
  1028. * dyIntraRaiseEventTestHandler returns true or false as to whether a specific handler should be
  1029. * called for a specific raised event (and associated event arguments)
  1030. * dyIntraRaiseEventPostHandler does not return anything. This allows behaviors to access the results
  1031. * of an event handler in the per handler loop.
  1032. * dyPostRaiseEvent returns the responses. This allows for any post processing of the event
  1033. * results from the sum of all event handlers
  1034. *
  1035. * When handling a catch-all {@link __dycall}, the method name is the name of the event
  1036. * and the parameters are the sender, the param, and then the name of the event.
  1037. *
  1038. * @param string the event name
  1039. * @param mixed the event sender object
  1040. * @param TEventParameter the event parameter
  1041. * @param numeric how the results of the event are tabulated. default: {@link EVENT_RESULT_FILTER} The default filters out
  1042. * null responses. optional
  1043. * @param function any per handler filtering of the response result needed is passed through
  1044. * this if not null. default: null. optional
  1045. * @return mixed the results of the event
  1046. * @throws TInvalidOperationException if the event is undefined
  1047. * @throws TInvalidDataValueException If an event handler is invalid
  1048. */
  1049. public function raiseEvent($name,$sender,$param,$responsetype=null,$postfunction=null)
  1050. {
  1051. $p=$param;
  1052. if(is_callable($responsetype))
  1053. {
  1054. $postfunction=$responsetype;
  1055. $responsetype=null;
  1056. }
  1057. if($responsetype===null)
  1058. $responsetype=TEventResults::EVENT_RESULT_FILTER;
  1059. $name=strtolower($name);
  1060. $responses=array();
  1061. $name=$this->dyPreRaiseEvent($name,$sender,$param,$responsetype,$postfunction);
  1062. if($this->hasEventHandler($name)||$this->hasEventHandler(TComponent::GLOBAL_RAISE_EVENT_LISTENER))
  1063. {
  1064. $handlers=$this->getEventHandlers($name);
  1065. $handlerArray=$handlers->toArray();
  1066. if(strncasecmp($name,'fx',2)===0&&$this->hasEventHandler(TComponent::GLOBAL_RAISE_EVENT_LISTENER))
  1067. {
  1068. $globalhandlers=$this->getEventHandlers(TComponent::GLOBAL_RAISE_EVENT_LISTENER);
  1069. $handlerArray=array_merge($globalhandlers->toArrayBelowPriority(0),$handlerArray,$globalhandlers->toArrayAbovePriority(0));
  1070. }
  1071. $response=null;
  1072. foreach($handlerArray as $handler)
  1073. {
  1074. if($this->dyIntraRaiseEventTestHandler($handler,$sender,$param,$name)===false)
  1075. continue;
  1076. if(is_string($handler))
  1077. {
  1078. if(($pos=strrpos($handler,'.'))!==false)
  1079. {
  1080. $object=$this->getSubProperty(substr($handler,0,$pos));
  1081. $method=substr($handler,$pos+1);
  1082. if(method_exists($object,$method)||strncasecmp($method,'dy',2)===0||strncasecmp($method,'fx',2)===0)
  1083. {
  1084. if($method=='__dycall')
  1085. $response=$object->__dycall($name,array($sender,$param,$name));
  1086. else
  1087. $response=$object->$method($sender,$param,$name);
  1088. }
  1089. else
  1090. throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,$handler);
  1091. }
  1092. else
  1093. $response=call_user_func($handler,$sender,$param,$name);
  1094. }
  1095. else if(is_callable($handler,true))
  1096. {
  1097. list($object,$method)=$handler;
  1098. if(is_string($object))
  1099. $response=call_user_func($handler,$sender,$param,$name);
  1100. else
  1101. {
  1102. if(($pos=strrpos($method,'.'))!==false)
  1103. {
  1104. $object=$this->getSubProperty(substr($method,0,$pos));
  1105. $method=substr($method,$pos+1);
  1106. }
  1107. if(method_exists($object,$method)||strncasecmp($method,'dy',2)===0||strncasecmp($method,'fx',2)===0)
  1108. {
  1109. if($method=='__dycall')
  1110. $response=$object->__dycall($name,array($sender,$param,$name));
  1111. else
  1112. $response=$object->$method($sender,$param,$name);
  1113. }
  1114. else
  1115. throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,$handler[1]);
  1116. }
  1117. }
  1118. else
  1119. throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,gettype($handler));
  1120. $this->dyIntraRaiseEventPostHandler($name,$sender,$param,$handler,$response);
  1121. if($postfunction)
  1122. $response=call_user_func_array($postfunction,array($sender,$param,$this,$response));
  1123. if($responsetype&TEventResults::EVENT_RESULT_ALL)
  1124. $responses[]=array('sender'=>$sender,'param'=>$param,'response'=>$response);
  1125. else
  1126. $responses[]=$response;
  1127. if($response!==null&&($responsetype&TEventResults::EVENT_RESULT_FEED_FORWARD))
  1128. $param=$response;
  1129. }
  1130. }
  1131. else if(strncasecmp($name,'on',2)===0&&!$this->hasEvent($name))
  1132. throw new TInvalidOperationException('component_event_undefined',get_class($this),$name);
  1133. if($responsetype&TEventResults::EVENT_RESULT_FILTER)
  1134. $responses=array_filter($responses);
  1135. $responses=$this->dyPostRaiseEvent($responses,$name,$sender,$param,$responsetype,$postfunction);
  1136. return $responses;
  1137. }
  1138. /**
  1139. * Evaluates a PHP expression in the context of this control.
  1140. *
  1141. * Behaviors may implement the function:
  1142. * <code>
  1143. * public function dyEvaluateExpressionFilter($expression, $chain) {
  1144. * return $chain->dyEvaluateExpressionFilter(str_replace('foo', 'bar', $expression)); //example
  1145. * }
  1146. * </code>
  1147. * to be executed when evaluateExpression is called. All attached behaviors are notified through
  1148. * dyEvaluateExpressionFilter. The chaining is important in this function due to the filtering
  1149. * pass-through effect.
  1150. *
  1151. * @param string PHP expression
  1152. * @return mixed the expression result
  1153. * @throws TInvalidOperationException if the expression is invalid
  1154. */
  1155. public function evaluateExpression($expression)
  1156. {
  1157. $expression=$this->dyEvaluateExpressionFilter($expression);
  1158. try
  1159. {
  1160. if(eval("\$result=$expression;")===false)
  1161. throw new Exception('');
  1162. return $result;
  1163. }
  1164. catch(Exception $e)
  1165. {
  1166. throw new TInvalidOperationException('component_expression_invalid',get_class($this),$expression,$e->getMessage());
  1167. }
  1168. }
  1169. /**
  1170. * Evaluates a list of PHP statements.
  1171. *
  1172. * Behaviors may implement the function:
  1173. * <code>
  1174. * public function dyEvaluateStatementsFilter($statements, $chain) {
  1175. * return $chain->dyEvaluateStatementsFilter(str_replace('foo', 'bar', $statements)); //example
  1176. * }
  1177. * </code>
  1178. * to be executed when evaluateStatements is called. All attached behaviors are notified through
  1179. * dyEvaluateStatementsFilter. The chaining is important in this function due to the filtering
  1180. * pass-through effect.
  1181. *
  1182. * @param string PHP statements
  1183. * @return string content echoed or printed by the PHP statements
  1184. * @throws TInvalidOpeā€¦

Large files files are truncated, but you can click here to view the full file