Component.php 20.9 KB
Newer Older
Qiang Xue committed
1
<?php
w  
Qiang Xue committed
2 3 4 5 6 7 8
/**
 * Component class file.
 *
 * @link http://www.yiiframework.com/
 * @copyright Copyright &copy; 2008-2012 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */
Qiang Xue committed
9 10 11

namespace yii\base;

w  
Qiang Xue committed
12 13 14 15 16 17 18 19 20
/**
 * Component is the base class for all components in Yii.
 *
 * Component implements the basis for *properties*, *events* and *behaviors*.
 *
 * A property is defined by a getter method (e.g. `getLabel`),
 * and/or a setter method (e.g. `setLabel`). For example, the following
 * getter and setter methods define a property named `label`:
 *
w  
Qiang Xue committed
21
 * ~~~
w  
Qiang Xue committed
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
 * private $_label;
 *
 * public function getLabel()
 * {
 *     return $this->_label;
 * }
 *
 * public function setLabel($value)
 * {
 *     $this->_label = $value;
 * }
 * ~~~
 *
 * A property can be accessed like a member variable of an object.
 * Reading or writing a property will cause the invocation of the corresponding
 * getter or setter method. For example,
 *
w  
Qiang Xue committed
39
 * ~~~
w  
Qiang Xue committed
40 41 42 43 44 45 46 47 48 49 50 51
 * // equivalent to $label = $component->getLabel();
 * $label = $component->label;
 * // equivalent to $component->setLabel('abc');
 * $component->label = 'abc';
 * ~~~
 *
 *
 * An event is defined by the presence of a method whose name starts with `on`.
 * The event name is the method name. When an event is raised, functions
 * (called *event handlers*) attached to the event will be invoked automatically.
 * The `on` method is typically declared like the following:
 *
w  
Qiang Xue committed
52
 * ~~~
w  
Qiang Xue committed
53 54 55 56 57 58 59 60 61 62 63 64 65
 * public function onClick($event)
 * {
 *     $this->raiseEvent('onClick', $event);
 * }
 * ~~~
 *
 * An event can be raised by calling the [[raiseEvent]] method, upon which
 * the attached event handlers will be invoked automatically in the order they
 * are attached to the event. In the above example, if we call the `onClick` method,
 * an `onClick` event will be raised.
 *
 * An event handler should be defined with the following signature:
 *
w  
Qiang Xue committed
66
 * ~~~
w  
Qiang Xue committed
67 68 69
 * public function foo($event) { ... }
 * ~~~
 *
w  
Qiang Xue committed
70
 * where `$event` is an [[Event]] object which includes parameters associated with the event.
w  
Qiang Xue committed
71 72 73 74
 *
 * To attach an event handler to an event, call [[attachEventHandler]].
 * Alternatively, you can also do the following:
 *
w  
Qiang Xue committed
75
 * ~~~
w  
Qiang Xue committed
76 77 78 79 80 81
 * $component->onClick = $callback;
 * // or $component->onClick->add($callback);
 * ~~~
 *
 * where `$callback` refers to a valid PHP callback. Some examples of `$callback` are:
 *
w  
Qiang Xue committed
82
 * ~~~
w  
Qiang Xue committed
83 84 85 86 87 88 89 90
 * 'handleOnClick'                    // handleOnClick() is a global function
 * array($object, 'handleOnClick')    // $object->handleOnClick()
 * array('Page', 'handleOnClick')     // Page::handleOnClick()
 * function($event) { ... }           // anonymous function
 * ~~~
 *
 * Both property names and event names are *case-insensitive*.
 *
w  
Qiang Xue committed
91 92 93
 * A behavior is an instance of [[Behavior]] or its child class. When a behavior is
 * attached to a component, its public properties and methods can be accessed via the
 * component directly, as if the component owns those properties and methods. For example,
w  
Qiang Xue committed
94
 *
w  
Qiang Xue committed
95
 * Multiple behaviors can be attached to the same component.
w  
Qiang Xue committed
96
 *
w  
Qiang Xue committed
97 98
 * To attach a behavior to a component, call [[attachBehavior]]; and to detach the behavior
 * from the component, call [[detachBehavior]].
w  
Qiang Xue committed
99
 *
w  
Qiang Xue committed
100 101
 * Components created via [[\Yii::createComponent]] have life cycles. In particular,
 *
w  
Qiang Xue committed
102
 * @author Qiang Xue <qiang.xue@gmail.com>
w  
Qiang Xue committed
103
 * @since 2.0
w  
Qiang Xue committed
104
 */
Qiang Xue committed
105 106 107
class Component
{
	private $_e;
Qiang Xue committed
108
	private $_b;
Qiang Xue committed
109 110

	/**
w  
Qiang Xue committed
111 112 113 114 115 116 117 118 119 120 121 122 123 124
	 * Returns the value of a component property.
	 * This method will check in the following order and act accordingly:
	 *
	 *  - a property defined by a getter: return the getter result
	 *  - an event: return a vector containing the attached event handlers
	 *  - a behavior: return the behavior object
	 *  - a property of a behavior: return the behavior property value
	 *
	 * Do not call this method directly as it is a PHP magic method that
	 * will be implicitly called when executing `$value = $component->property;`.
	 * @param string $name the property name
	 * @return mixed the property value, event handlers attached to the event,
	 * the named behavior, or the value of a behavior's property
	 * @throws Exception if the property is not defined
Qiang Xue committed
125 126 127 128 129
	 * @see __set
	 */
	public function __get($name)
	{
		$getter = 'get' . $name;
Qiang Xue committed
130
		if (method_exists($this, $getter)) { // read property, e.g. getName()
Qiang Xue committed
131
			return $this->$getter();
Qiang Xue committed
132 133 134 135
		}
		elseif (method_exists($this, $name) && strncasecmp($name, 'on', 2) === 0) { // event, e.g. onClick()
			$name = strtolower($name);
			if (!isset($this->_e[$name])) {
Qiang Xue committed
136
				$this->_e[$name] = new Vector;
Qiang Xue committed
137
			}
Qiang Xue committed
138 139
			return $this->_e[$name];
		}
Qiang Xue committed
140 141 142 143 144
		elseif (isset($this->_b[$name])) { // behavior
			return $this->_b[$name];
		}
		elseif (is_array($this->_b)) { // a behavior property
			foreach ($this->_b as $object) {
w  
Qiang Xue committed
145
				if (property_exists($object, $name) || $object->canGetProperty($name)) {
Qiang Xue committed
146
					return $object->$name;
Qiang Xue committed
147
				}
Qiang Xue committed
148 149
			}
		}
Qiang Xue committed
150
		throw new Exception('Getting unknown property: ' . get_class($this) . '.' . $name);
Qiang Xue committed
151 152 153 154
	}

	/**
	 * Sets value of a component property.
w  
Qiang Xue committed
155 156 157 158 159 160 161 162
	 * This method will check in the following order and act accordingly:
	 *
	 *  - a property defined by a setter: set the property value
	 *  - an event: attach the handler to the event
	 *  - a property of a behavior: set the behavior property value
	 *
	 * Do not call this method directly as it is a PHP magic method that
	 * will be implicitly called when executing `$component->property = $value;`.
Qiang Xue committed
163
	 * @param string $name the property name or the event name
w  
Qiang Xue committed
164 165
	 * @param mixed $value the property value
	 * @throws Exception if the property is not defined or read-only.
Qiang Xue committed
166 167
	 * @see __get
	 */
Qiang Xue committed
168
	public function __set($name, $value)
Qiang Xue committed
169
	{
Qiang Xue committed
170
		$setter = 'set' . $name;
w  
Qiang Xue committed
171
		if (method_exists($this, $setter)) {  // write property
Qiang Xue committed
172
			return $this->$setter($value);
Qiang Xue committed
173
		}
w  
Qiang Xue committed
174
		elseif (method_exists($this, $name) && strncasecmp($name, 'on', 2) === 0) {  // event
Qiang Xue committed
175 176
			$name = strtolower($name);
			if (!isset($this->_e[$name])) {
Qiang Xue committed
177
				$this->_e[$name] = new Vector;
Qiang Xue committed
178
			}
Qiang Xue committed
179 180
			return $this->_e[$name]->add($value);
		}
w  
Qiang Xue committed
181
		elseif (is_array($this->_b)) {  // behavior
Qiang Xue committed
182
			foreach ($this->_b as $object) {
w  
Qiang Xue committed
183
				if (property_exists($object, $name) || $object->canSetProperty($name)) {
Qiang Xue committed
184 185
					return $object->$name = $value;
				}
Qiang Xue committed
186 187
			}
		}
Qiang Xue committed
188
		if (method_exists($this, 'get' . $name)) {
Qiang Xue committed
189
			throw new Exception('Setting read-only property: ' . get_class($this) . '.' . $name);
Qiang Xue committed
190 191
		}
		else {
Qiang Xue committed
192
			throw new Exception('Setting unknown property: ' . get_class($this) . '.' . $name);
Qiang Xue committed
193
		}
Qiang Xue committed
194 195 196 197
	}

	/**
	 * Checks if a property value is null.
w  
Qiang Xue committed
198 199 200 201 202 203 204 205
	 * This method will check in the following order and act accordingly:
	 *
	 *  - a property defined by a setter: return whether the property value is null
	 *  - an event: return whether the event has any handler attached
	 *  - a property of a behavior: return whether the property value is null
	 *
	 * Do not call this method directly as it is a PHP magic method that
	 * will be implicitly called when executing `isset($component->property)`.
Qiang Xue committed
206
	 * @param string $name the property name or the event name
w  
Qiang Xue committed
207
	 * @return boolean whether the named property is null
Qiang Xue committed
208 209 210
	 */
	public function __isset($name)
	{
Qiang Xue committed
211 212 213 214 215 216
		$getter = 'get' . $name;
		if (method_exists($this, $getter)) { // property is not null
			return $this->$getter() !== null;
		}
		elseif (method_exists($this, $name) && strncasecmp($name, 'on', 2) === 0) { // has event handler
			$name = strtolower($name);
Qiang Xue committed
217 218
			return isset($this->_e[$name]) && $this->_e[$name]->getCount();
		}
Qiang Xue committed
219
 		elseif (isset($this->_b[$name])) { // has behavior
Qiang Xue committed
220
 			return true;
Qiang Xue committed
221 222 223
 		}
		elseif (is_array($this->_b)) {
			foreach ($this->_b as $object) {
w  
Qiang Xue committed
224
				if (property_exists($object, $name) || $object->canGetProperty($name)) {
Qiang Xue committed
225 226
					return $object->$name !== null;
				}
Qiang Xue committed
227 228 229 230 231 232 233
			}
		}
		return false;
	}

	/**
	 * Sets a component property to be null.
w  
Qiang Xue committed
234 235 236 237 238 239 240 241 242 243
	 * This method will check in the following order and act accordingly:
	 *
	 *  - a property defined by a setter: set the property value to be null
	 *  - an event: remove all attached event handlers
	 *  - a property of a behavior: set the property value to be null
	 *
	 * Do not call this method directly as it is a PHP magic method that
	 * will be implicitly called when executing `unset($component->property)`.
	 * @param string $name the property name
	 * @throws Exception if the property is read only.
Qiang Xue committed
244 245 246
	 */
	public function __unset($name)
	{
Qiang Xue committed
247
		$setter = 'set' . $name;
w  
Qiang Xue committed
248
		if (method_exists($this, $setter)) {  // write property
Qiang Xue committed
249
			$this->$setter(null);
Qiang Xue committed
250
		}
w  
Qiang Xue committed
251
		elseif (method_exists($this, $name) && strncasecmp($name, 'on', 2) === 0) {  // event
Qiang Xue committed
252
			unset($this->_e[strtolower($name)]);
Qiang Xue committed
253
		}
w  
Qiang Xue committed
254
		elseif (isset($this->_b[$name])) {  // behavior
Qiang Xue committed
255
			$this->detachBehavior($name);
Qiang Xue committed
256
		}
w  
Qiang Xue committed
257
		elseif (is_array($this->_b)) {  // behavior property
w  
Qiang Xue committed
258
			foreach ($this->_b as $object) {
w  
Qiang Xue committed
259 260 261 262 263
				if (property_exists($object, $name)) {
					return $object->$name = null;
				}
				elseif ($object->canSetProperty($name)) {
					return $object->$setter(null);
Qiang Xue committed
264
				}
Qiang Xue committed
265 266
			}
		}
w  
Qiang Xue committed
267
		elseif (method_exists($this, 'get' . $name)) {
Qiang Xue committed
268
			throw new Exception('Unsetting read-only property: ' . get_class($this) . '.' . $name);
w  
Qiang Xue committed
269
		}
Qiang Xue committed
270 271 272 273
	}

	/**
	 * Calls the named method which is not a class method.
w  
Qiang Xue committed
274 275 276 277 278 279 280
	 * If the name refers to a component property whose value is
	 * an anonymous function, the method will execute the function.
	 * Otherwise, it will check if any attached behavior has
	 * the named method and will execute it if available.
	 *
	 * Do not call this method directly as it is a PHP magic method that
	 * will be implicitly called when an unknown method is being invoked.
Qiang Xue committed
281 282 283 284
	 * @param string $name the method name
	 * @param array $parameters method parameters
	 * @return mixed the method return value
	 */
Qiang Xue committed
285
	public function __call($name, $parameters)
Qiang Xue committed
286
	{
Qiang Xue committed
287 288 289 290
		if ($this->canGetProperty($name)) {
			$func = $this->$name;
			if ($func instanceof \Closure) {
				return call_user_func_array($func, $parameters);
Qiang Xue committed
291 292 293
			}
		}

Qiang Xue committed
294
		if ($this->_b !== null)
Qiang Xue committed
295
		{
w  
Qiang Xue committed
296 297
			foreach ($this->_b as $object)
			{
w  
Qiang Xue committed
298
				if (method_exists($object, $name)) {
w  
Qiang Xue committed
299 300 301
					return call_user_func_array(array($object, $name), $parameters);
				}
			}
Qiang Xue committed
302
		}
Qiang Xue committed
303
		throw new Exception('Unknown method: ' . get_class($this) . "::$name()");
Qiang Xue committed
304 305
	}

w  
Qiang Xue committed
306 307 308 309
	/**
	 * Creates a new component instance.
	 *
	 * This method differs from the PHP `new` operator in that it does the following
w  
Qiang Xue committed
310
	 * steps to create a new component instance:
w  
Qiang Xue committed
311
	 *
w  
Qiang Xue committed
312
	 * - Call class constructor (same the `new` operator);
w  
Qiang Xue committed
313
	 * - Initialize the component properties using the name-value pairs given as the
w  
Qiang Xue committed
314
	 *   last parameter to this method;
w  
Qiang Xue committed
315 316
	 * - Call [[Initable::init|init]] if the class implements [[Initable]].
	 *
w  
Qiang Xue committed
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
	 * Parameters passed to this method will be used as the parameters to the object
	 * constructor. If, however, the last parameter is an array and the count of the parameters
	 * is one more than the count of declared constructor parameters, that parameter
	 * will be treated as name-value pairs for initializing the component properties.
	 * For example,
	 *
	 * ~~~
	 * class Foo extends \yii\base\Component {
	 *     public $c;
	 *     public function __construct($a, $b) { ... }
	 * }
	 *
	 * $model = Foo::create(1, 2, array('c' => 3));
	 * // which is equivalent to the following lines:
	 * $model = new Foo(1, 2);
	 * $model->c = 3;
	 * $model->init();
	 * ~~~
w  
Qiang Xue committed
335 336 337 338
	 *
	 * @return object the created component
	 * @throws Exception if the configuration is invalid.
	 */
w  
Qiang Xue committed
339
	public static function create()
w  
Qiang Xue committed
340
	{
w  
Qiang Xue committed
341 342
		$class = '\\' . get_called_class();
		if (($n = func_num_args()) > 0) {
w  
Qiang Xue committed
343
			$args = func_get_args();
w  
Qiang Xue committed
344 345 346 347 348 349 350 351 352 353
			if (is_array($args[$n-1])) {
				// the last parameter could be configuration array
				$method = new \ReflectionMethod($class, '__construct');
				if ($method->getNumberOfParameters()+1 == $n) {
					$config = $args[$n-1];
					array_pop($args);
				}
			}
			$config['class'] = $class;
			array_unshift($args, $config);
w  
Qiang Xue committed
354 355 356
			return call_user_func_array('\Yii::createComponent', $args);
		}
		else {
w  
Qiang Xue committed
357
			return \Yii::createComponent($class);
w  
Qiang Xue committed
358 359 360
		}
	}

Qiang Xue committed
361
	/**
w  
Qiang Xue committed
362
	 * Returns a value indicating whether a property is defined.
Qiang Xue committed
363 364 365 366 367 368 369
	 * A property is defined if there is a getter or setter method
	 * defined in the class. Note, property names are case-insensitive.
	 * @param string $name the property name
	 * @return boolean whether the property is defined
	 * @see canGetProperty
	 * @see canSetProperty
	 */
w  
Qiang Xue committed
370
	public function hasProperty($name)
Qiang Xue committed
371
	{
w  
Qiang Xue committed
372
		return $this->canGetProperty($name) || $this->canSetProperty($name);
Qiang Xue committed
373 374 375
	}

	/**
w  
Qiang Xue committed
376
	 * Returns a value indicating whether a property can be read.
Qiang Xue committed
377 378 379 380 381 382
	 * A property can be read if the class has a getter method
	 * for the property name. Note, property name is case-insensitive.
	 * @param string $name the property name
	 * @return boolean whether the property can be read
	 * @see canSetProperty
	 */
w  
Qiang Xue committed
383
	public function canGetProperty($name)
Qiang Xue committed
384
	{
w  
Qiang Xue committed
385
		return method_exists($this, 'get' . $name);
Qiang Xue committed
386 387 388
	}

	/**
w  
Qiang Xue committed
389
	 * Returns a value indicating whether a property can be set.
Qiang Xue committed
390 391 392 393 394 395
	 * A property can be written if the class has a setter method
	 * for the property name. Note, property name is case-insensitive.
	 * @param string $name the property name
	 * @return boolean whether the property can be written
	 * @see canGetProperty
	 */
w  
Qiang Xue committed
396
	public function canSetProperty($name)
Qiang Xue committed
397
	{
w  
Qiang Xue committed
398
		return method_exists($this, 'set' . $name);
Qiang Xue committed
399 400 401
	}

	/**
w  
Qiang Xue committed
402 403 404
	 * Returns a value indicating whether an event is defined.
	 * An event is defined if the class has a method whose name starts with `on` (e.g. `onClick`).
	 * Note that event name is case-insensitive.
Qiang Xue committed
405 406 407 408 409
	 * @param string $name the event name
	 * @return boolean whether an event is defined
	 */
	public function hasEvent($name)
	{
w  
Qiang Xue committed
410
		return method_exists($this, $name) && strncasecmp($name, 'on', 2)===0;
Qiang Xue committed
411 412 413
	}

	/**
w  
Qiang Xue committed
414
	 * Returns a value indicating whether there is any handler attached to the event.
Qiang Xue committed
415
	 * @param string $name the event name
w  
Qiang Xue committed
416
	 * @return boolean whether there is any handler attached to the event.
Qiang Xue committed
417
	 */
w  
Qiang Xue committed
418
	public function hasEventHandlers($name)
Qiang Xue committed
419
	{
Qiang Xue committed
420
		$name = strtolower($name);
w  
Qiang Xue committed
421
		return isset($this->_e[$name]) && $this->_e[$name]->getCount();
Qiang Xue committed
422 423 424 425
	}

	/**
	 * Returns the list of attached event handlers for an event.
w  
Qiang Xue committed
426 427 428
	 * You may manipulate the returned [[Vector]] object by adding or removing handlers.
	 * For example,
	 *
w  
Qiang Xue committed
429
	 * ~~~
w  
Qiang Xue committed
430 431
	 * $component->getEventHandlers($eventName)->insertAt(0, $eventHandler);
	 * ~~~
w  
Qiang Xue committed
432
	 *
Qiang Xue committed
433
	 * @param string $name the event name
w  
Qiang Xue committed
434 435
	 * @return Vector list of attached event handlers for the event
	 * @throws Exception if the event is not defined
Qiang Xue committed
436 437 438
	 */
	public function getEventHandlers($name)
	{
w  
Qiang Xue committed
439
		if ($this->hasEvent($name)) {
Qiang Xue committed
440
			$name = strtolower($name);
w  
Qiang Xue committed
441 442 443
			if (!isset($this->_e[$name])) {
				$this->_e[$name] = new Vector;
			}
Qiang Xue committed
444 445
			return $this->_e[$name];
		}
w  
Qiang Xue committed
446
		throw new Exception('Undefined event: ' . $name);
Qiang Xue committed
447 448 449 450 451
	}

	/**
	 * Attaches an event handler to an event.
	 *
w  
Qiang Xue committed
452 453
	 * This is equivalent to the following code:
	 *
w  
Qiang Xue committed
454
	 * ~~~
w  
Qiang Xue committed
455 456 457 458 459 460
	 * $component->getEventHandlers($eventName)->add($eventHandler);
	 * ~~~
	 *
	 * An event handler must be a valid PHP callback. The followings are
	 * some examples:
	 *
w  
Qiang Xue committed
461
	 * ~~~
w  
Qiang Xue committed
462 463 464 465 466
	 * 'handleOnClick'                    // handleOnClick() is a global function
	 * array($object, 'handleOnClick')    // $object->handleOnClick()
	 * array('Page', 'handleOnClick')     // Page::handleOnClick()
	 * function($event) { ... }           // anonymous function
	 * ~~~
Qiang Xue committed
467 468 469
	 *
	 * An event handler must be defined with the following signature,
	 *
w  
Qiang Xue committed
470
	 * ~~~
w  
Qiang Xue committed
471 472
	 * function handlerName($event) {}
	 * ~~~
Qiang Xue committed
473
	 *
w  
Qiang Xue committed
474
	 * where `$event` is an [[Event]] object which includes parameters associated with the event.
Qiang Xue committed
475 476 477
	 *
	 * @param string $name the event name
	 * @param callback $handler the event handler
w  
Qiang Xue committed
478
	 * @throws Exception if the event is not defined
Qiang Xue committed
479 480
	 * @see detachEventHandler
	 */
Qiang Xue committed
481
	public function attachEventHandler($name, $handler)
Qiang Xue committed
482 483 484 485 486 487
	{
		$this->getEventHandlers($name)->add($handler);
	}

	/**
	 * Detaches an existing event handler.
w  
Qiang Xue committed
488
	 * This method is the opposite of [[attachEventHandler]].
Qiang Xue committed
489 490 491 492 493
	 * @param string $name event name
	 * @param callback $handler the event handler to be removed
	 * @return boolean if the detachment process is successful
	 * @see attachEventHandler
	 */
Qiang Xue committed
494
	public function detachEventHandler($name, $handler)
Qiang Xue committed
495
	{
w  
Qiang Xue committed
496
		return $this->getEventHandlers($name)->remove($handler) !== false;
Qiang Xue committed
497 498 499 500 501 502 503
	}

	/**
	 * Raises an event.
	 * This method represents the happening of an event. It invokes
	 * all attached handlers for the event.
	 * @param string $name the event name
w  
Qiang Xue committed
504 505
	 * @param Event $event the event parameter
	 * @throws Exception if the event is undefined or an event handler is invalid.
Qiang Xue committed
506
	 */
Qiang Xue committed
507
	public function raiseEvent($name, $event)
Qiang Xue committed
508
	{
Qiang Xue committed
509
		$name = strtolower($name);
w  
Qiang Xue committed
510 511 512
		if ($event instanceof Event) {
			$event->name = $name;
		}
w  
Qiang Xue committed
513 514 515
		if (isset($this->_e[$name])) {
			foreach ($this->_e[$name] as $handler) {
				if (is_string($handler) || $handler instanceof \Closure) {
Qiang Xue committed
516
					call_user_func($handler, $event);
w  
Qiang Xue committed
517 518 519 520 521
				}
				elseif (is_callable($handler, true)) {
					// an array: 0 - object, 1 - method name
					list($object, $method) = $handler;
					if (is_string($object)) {	// static method call
Qiang Xue committed
522
						call_user_func($handler, $event);
w  
Qiang Xue committed
523 524 525 526 527 528 529
					}
					elseif (method_exists($object, $method)) {
						$object->$method($event);
					}
					else {
						throw new Exception('Event "' . get_class($this) . '.' . $name . '" is attached with an invalid handler.');
					}
Qiang Xue committed
530
				}
w  
Qiang Xue committed
531 532 533 534 535 536
				else {
					throw new Exception('Event "' . get_class($this) . '.' . $name . '" is attached with an invalid handler.');
				}

				// stop further handling if the event is handled
				if ($event instanceof Event && $event->handled) {
Qiang Xue committed
537
					return;
w  
Qiang Xue committed
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
				}
			}
		}
		elseif (!$this->hasEvent($name)) {
			throw new Exception('Undefined event: ' . get_class($this) . '.' . $name);
		}
	}

	/**
	 * Returns the named behavior object.
	 * The name 'asa' stands for 'as a'.
	 * @param string $behavior the behavior name
	 * @return Behavior the behavior object, or null if the behavior does not exist
	 */
	public function asa($behavior)
	{
		return isset($this->_b[$behavior]) ? $this->_b[$behavior] : null;
	}

	/**
	 * Attaches a behavior to this component.
	 * This method will create the behavior object based on the given
	 * configuration. After that, the behavior object will be attached to
	 * this component by calling the [[Behavior::attach]] method.
	 * @param string $name the behavior's name. It should uniquely identify this behavior.
	 * @param mixed $behavior the behavior configuration. This can be one of the following:
	 *
	 *  - a [[Behavior]] object
	 *  - a string specifying the behavior class
	 *  - an object configuration array
	 *
	 * parameter to [[\Yii::createComponent]] to create the behavior object.
	 * @return Behavior the behavior object
	 * @see detachBehavior
	 */
	public function attachBehavior($name, $behavior)
	{
		if (!($behavior instanceof Behavior)) {
			$behavior = \Yii::createComponent($behavior);
		}
		$behavior->attach($this);
		return $this->_b[$name] = $behavior;
	}

	/**
	 * Attaches a list of behaviors to the component.
	 * Each behavior is indexed by its name and should be a [[Behavior]] object,
	 * a string specifying the behavior class, or an
	 * configuration array for creating the behavior.
	 * @param array $behaviors list of behaviors to be attached to the component
	 * @see attachBehavior
	 */
	public function attachBehaviors($behaviors)
	{
		foreach ($behaviors as $name => $behavior) {
			$this->attachBehavior($name, $behavior);
		}
	}

	/**
	 * Detaches a behavior from the component.
	 * The behavior's [[Behavior::detach]] method will be invoked.
	 * @param string $name the behavior's name.
	 * @return Behavior the detached behavior. Null if the behavior does not exist.
	 */
	public function detachBehavior($name)
	{
		if (isset($this->_b[$name])) {
			$this->_b[$name]->detach($this);
			$behavior = $this->_b[$name];
			unset($this->_b[$name]);
			return $behavior;
		}
	}

	/**
	 * Detaches all behaviors from the component.
	 */
	public function detachBehaviors()
	{
		if ($this->_b !== null) {
			foreach ($this->_b as $name => $behavior) {
				$this->detachBehavior($name);
			}
			$this->_b = null;
		}
	}

Qiang Xue committed
626 627 628 629
	/**
	 * Evaluates a PHP expression or callback under the context of this component.
	 *
	 * Valid PHP callback can be class method name in the form of
w  
Qiang Xue committed
630
	 * array(ClassName/Object, MethodName), or anonymous function.
Qiang Xue committed
631 632
	 *
	 * If a PHP callback is used, the corresponding function/method signature should be
w  
Qiang Xue committed
633
	 *
w  
Qiang Xue committed
634
	 * ~~~
Qiang Xue committed
635
	 * function foo($param1, $param2, ..., $component) { ... }
w  
Qiang Xue committed
636 637
	 * ~~~
	 *
Qiang Xue committed
638
	 * where the array elements in the second parameter to this method will be passed
w  
Qiang Xue committed
639
	 * to the callback as `$param1`, `$param2`, ...; and the last parameter will be the component itself.
Qiang Xue committed
640 641
	 *
	 * If a PHP expression is used, the second parameter will be "extracted" into PHP variables
w  
Qiang Xue committed
642 643
	 * that can be directly accessed in the expression. See [PHP extract](http://us.php.net/manual/en/function.extract.php)
	 * for more details. In the expression, the component object can be accessed using `$this`.
Qiang Xue committed
644 645 646 647 648
	 *
	 * @param mixed $_expression_ a PHP expression or PHP callback to be evaluated.
	 * @param array $_data_ additional parameters to be passed to the above expression/callback.
	 * @return mixed the expression result
	 */
w  
Qiang Xue committed
649
	public function evaluateExpression($_expression_, $_data_=array())
Qiang Xue committed
650
	{
w  
Qiang Xue committed
651
		if (is_string($_expression_)) {
Qiang Xue committed
652
			extract($_data_);
Qiang Xue committed
653
			return eval('return ' . $_expression_ . ';');
Qiang Xue committed
654
		}
w  
Qiang Xue committed
655
		else {
Qiang Xue committed
656
			$_data_[] = $this;
Qiang Xue committed
657 658 659 660
			return call_user_func_array($_expression_, $_data_);
		}
	}
}