PageRenderTime 1290ms CodeModel.GetById 203ms app.highlight 417ms RepoModel.GetById 653ms 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

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

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