Component.php 17.4 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
 * Component is the base class for all component classes in Yii.
w  
Qiang Xue committed
14
 *
15 16
 * Extending from [[Object]], Component implements the *event* and *behavior*
 * features in addition to the *property* feature.
w  
Qiang Xue committed
17 18
 *
 * An event is defined by the presence of a method whose name starts with `on`.
19 20
 * The event name is the method name. For example, the following method defines
 * the `onClick` event:
w  
Qiang Xue committed
21
 *
w  
Qiang Xue committed
22
 * ~~~
w  
Qiang Xue committed
23 24
 * public function onClick($event)
 * {
Qiang Xue committed
25
 *	 $this->raiseEvent('onClick', $event);
w  
Qiang Xue committed
26 27 28
 * }
 * ~~~
 *
29
 * Event names are case-insensitive.
w  
Qiang Xue committed
30
 *
31
 * An event can be attached with one or multiple PHP callbacks, called *event handlers*.
Qiang Xue committed
32
 * One can call [[raiseEvent()]] to raise an event. When an event is raised, the attached
33
 * event handlers will be invoked automatically in the order they are attached to the event.
w  
Qiang Xue committed
34
 *
35 36
 * To attach an event handler to an event, call [[attachEventHandler]]. Alternatively,
 * you can use the assignment syntax: `$component->onClick = $callback;`,
Qiang Xue committed
37
 * where `$callback` refers to a valid PHP callback which can be one of the followings:
w  
Qiang Xue committed
38
 *
39 40 41 42
 * - global function: `'handleOnClick'`
 * - object method: `array($object, 'handleOnClick')`
 * - static method: `array('Page', 'handleOnClick')`
 * - anonymous function: `function($event) { ... }`
w  
Qiang Xue committed
43
 *
44
 * The signature of an event handler should be like the following:
w  
Qiang Xue committed
45
 * ~~~
46
 * function foo($event)
w  
Qiang Xue committed
47 48
 * ~~~
 *
49 50 51 52 53
 * where `$event` is an [[Event]] object which includes parameters associated with the event.
 *
 * Because `$component->onClick` is returned as a [[Vector]] with each item in the vector being
 * an attached event handler, one can manipulate this [[Vector]] object to attach/detach event
 * handlers, or adjust their relative orders. For example,
w  
Qiang Xue committed
54
 *
w  
Qiang Xue committed
55
 * ~~~
56
 * $component->onClick->insertAt(0, $callback);  // attach a handler as the first one
Qiang Xue committed
57 58
 * $component->onClick[] = $callback;			// attach a handler as the last one
 * unset($component->onClick[0]);				// detach the first handler
w  
Qiang Xue committed
59 60 61
 * ~~~
 *
 *
Qiang Xue committed
62 63 64 65
 * A behavior is an instance of [[Behavior]] or its child class. A component can be attached
 * with one or multiple behaviors. 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.
w  
Qiang Xue committed
66
 *
Qiang Xue committed
67
 * To attach a behavior to a component, declare it in [[behaviors()]], or explicitly call [[attachBehavior]].
w  
Qiang Xue committed
68 69
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
w  
Qiang Xue committed
70
 * @since 2.0
w  
Qiang Xue committed
71
 */
72
class Component extends Object
Qiang Xue committed
73
{
Qiang Xue committed
74 75 76
	/**
	 * @var Vector[] the attached event handlers (event name => handlers)
	 */
Qiang Xue committed
77
	private $_e;
Qiang Xue committed
78 79 80
	/**
	 * @var Behavior[] the attached behaviors (behavior name => behavior)
	 */
Qiang Xue committed
81
	private $_b;
Qiang Xue committed
82 83

	/**
w  
Qiang Xue committed
84 85 86 87 88 89 90 91 92 93 94 95 96 97
	 * 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
98 99 100 101 102
	 * @see __set
	 */
	public function __get($name)
	{
		$getter = 'get' . $name;
Qiang Xue committed
103
		if (method_exists($this, $getter)) { // read property, e.g. getName()
Qiang Xue committed
104
			return $this->$getter();
Qiang Xue committed
105
		} elseif (method_exists($this, $name) && strncasecmp($name, 'on', 2) === 0) { // event, e.g. onClick()
Qiang Xue committed
106 107
			$name = strtolower($name);
			if (!isset($this->_e[$name])) {
Qiang Xue committed
108
				$this->_e[$name] = new Vector;
Qiang Xue committed
109
			}
Qiang Xue committed
110
			return $this->_e[$name];
Qiang Xue committed
111 112 113 114 115
		} else { // behavior property
			$this->ensureBehaviors();
			foreach ($this->_b as $behavior) {
				if ($behavior->canGetProperty($name)) {
					return $behavior->$name;
Qiang Xue committed
116
				}
Qiang Xue committed
117 118
			}
		}
Qiang Xue committed
119
		throw new Exception('Getting unknown property: ' . get_class($this) . '.' . $name);
Qiang Xue committed
120 121 122
	}

	/**
123
	 * Sets the value of a component property.
w  
Qiang Xue committed
124 125 126 127 128 129 130 131
	 * 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
132
	 * @param string $name the property name or the event name
w  
Qiang Xue committed
133
	 * @param mixed $value the property value
Alexander Makarov committed
134
	 * @return mixed value that was set
w  
Qiang Xue committed
135
	 * @throws Exception if the property is not defined or read-only.
Qiang Xue committed
136 137
	 * @see __get
	 */
Qiang Xue committed
138
	public function __set($name, $value)
Qiang Xue committed
139
	{
Qiang Xue committed
140
		$setter = 'set' . $name;
Qiang Xue committed
141
		if (method_exists($this, $setter)) { // write property
Qiang Xue committed
142
			return $this->$setter($value);
Qiang Xue committed
143
		} elseif (method_exists($this, $name) && strncasecmp($name, 'on', 2) === 0) { // event
Qiang Xue committed
144 145
			$name = strtolower($name);
			if (!isset($this->_e[$name])) {
Qiang Xue committed
146
				$this->_e[$name] = new Vector;
Qiang Xue committed
147
			}
Qiang Xue committed
148
			return $this->_e[$name]->add($value);
Qiang Xue committed
149 150 151 152 153
		} else { // behavior property
			$this->ensureBehaviors();
			foreach ($this->_b as $behavior) {
				if ($behavior->canSetProperty($name)) {
					return $behavior->$name = $value;
Qiang Xue committed
154
				}
Qiang Xue committed
155 156
			}
		}
Qiang Xue committed
157
		if (method_exists($this, 'get' . $name)) {
Qiang Xue committed
158
			throw new Exception('Setting read-only property: ' . get_class($this) . '.' . $name);
Qiang Xue committed
159
		} else {
Qiang Xue committed
160
			throw new Exception('Setting unknown property: ' . get_class($this) . '.' . $name);
Qiang Xue committed
161
		}
Qiang Xue committed
162 163 164 165
	}

	/**
	 * Checks if a property value is null.
w  
Qiang Xue committed
166 167 168 169 170 171 172 173
	 * 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
174
	 * @param string $name the property name or the event name
w  
Qiang Xue committed
175
	 * @return boolean whether the named property is null
Qiang Xue committed
176 177 178
	 */
	public function __isset($name)
	{
Qiang Xue committed
179 180 181
		$getter = 'get' . $name;
		if (method_exists($this, $getter)) { // property is not null
			return $this->$getter() !== null;
Qiang Xue committed
182
		} elseif (method_exists($this, $name) && strncasecmp($name, 'on', 2) === 0) { // has event handler
Qiang Xue committed
183
			$name = strtolower($name);
Qiang Xue committed
184
			return isset($this->_e[$name]) && $this->_e[$name]->getCount();
Qiang Xue committed
185 186 187 188 189
		} else { // behavior property
			$this->ensureBehaviors();
			foreach ($this->_b as $behavior) {
				if ($behavior->canGetProperty($name)) {
					return $behavior->$name !== null;
Qiang Xue committed
190
				}
Qiang Xue committed
191 192 193 194 195 196 197
			}
		}
		return false;
	}

	/**
	 * Sets a component property to be null.
w  
Qiang Xue committed
198 199 200 201 202 203 204 205 206 207
	 * 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
208 209 210
	 */
	public function __unset($name)
	{
Qiang Xue committed
211
		$setter = 'set' . $name;
Qiang Xue committed
212
		if (method_exists($this, $setter)) { // write property
Qiang Xue committed
213 214
			$this->$setter(null);
			return;
Qiang Xue committed
215
		} elseif (method_exists($this, $name) && strncasecmp($name, 'on', 2) === 0) { // event
Qiang Xue committed
216 217
			unset($this->_e[strtolower($name)]);
			return;
Qiang Xue committed
218 219 220 221 222 223
		} else { // behavior property
			$this->ensureBehaviors();
			foreach ($this->_b as $behavior) {
				if ($behavior->canSetProperty($name)) {
					$behavior->$name = null;
					return;
Qiang Xue committed
224
				}
Qiang Xue committed
225
			}
Qiang Xue committed
226 227
		}
		if (method_exists($this, 'get' . $name)) {
Qiang Xue committed
228
			throw new Exception('Unsetting read-only property: ' . get_class($this) . '.' . $name);
w  
Qiang Xue committed
229
		}
Qiang Xue committed
230 231 232 233
	}

	/**
	 * Calls the named method which is not a class method.
w  
Qiang Xue committed
234 235 236 237 238 239 240
	 * 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
241
	 * @param string $name the method name
Qiang Xue committed
242
	 * @param array $params method parameters
Qiang Xue committed
243 244
	 * @return mixed the method return value
	 */
Qiang Xue committed
245
	public function __call($name, $params)
Qiang Xue committed
246
	{
Qiang Xue committed
247
		if ($this->canGetProperty($name, false)) {
Qiang Xue committed
248 249
			$func = $this->$name;
			if ($func instanceof \Closure) {
Qiang Xue committed
250
				return call_user_func_array($func, $params);
Qiang Xue committed
251 252 253
			}
		}

Qiang Xue committed
254 255 256 257
		$this->ensureBehaviors();
		foreach ($this->_b as $object) {
			if (method_exists($object, $name)) {
				return call_user_func_array(array($object, $name), $params);
w  
Qiang Xue committed
258
			}
Qiang Xue committed
259
		}
Qiang Xue committed
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289

		throw new Exception('Calling unknown method: ' . get_class($this) . "::$name()");
	}

	/**
	 * Returns a list of behaviors that this component should behave as.
	 *
	 * Child classes may override this method to specify the behaviors they want to behave as.
	 *
	 * The return value of this method should be an array of behavior objects or configurations
	 * indexed by behavior names. A behavior configuration can be either a string specifying
	 * the behavior class or an array of the following structure:
	 *
	 * ~~~
	 * 'behaviorName' => array(
	 *	 'class' => 'BehaviorClass',
	 *	 'property1' => 'value1',
	 *	 'property2' => 'value2',
	 * )
	 * ~~~
	 *
	 * Note that a behavior class must extend from [[Behavior]].
	 *
	 * Behaviors declared in this method will be attached to the component on demand.
	 *
	 * @return array the behavior configurations.
	 */
	public function behaviors()
	{
		return array();
Qiang Xue committed
290 291 292
	}

	/**
w  
Qiang Xue committed
293 294 295
	 * 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
296 297 298 299 300
	 * @param string $name the event name
	 * @return boolean whether an event is defined
	 */
	public function hasEvent($name)
	{
Qiang Xue committed
301
		return method_exists($this, $name) && strncasecmp($name, 'on', 2) === 0;
Qiang Xue committed
302 303 304
	}

	/**
Qiang Xue committed
305
	 * Returns a value indicating whether there is any handler attached to the named event.
Qiang Xue committed
306
	 * @param string $name the event name
w  
Qiang Xue committed
307
	 * @return boolean whether there is any handler attached to the event.
Qiang Xue committed
308
	 */
w  
Qiang Xue committed
309
	public function hasEventHandlers($name)
Qiang Xue committed
310
	{
Qiang Xue committed
311
		$this->ensureBehaviors();
Qiang Xue committed
312
		$name = strtolower($name);
w  
Qiang Xue committed
313
		return isset($this->_e[$name]) && $this->_e[$name]->getCount();
Qiang Xue committed
314 315 316 317
	}

	/**
	 * Returns the list of attached event handlers for an event.
w  
Qiang Xue committed
318 319 320
	 * You may manipulate the returned [[Vector]] object by adding or removing handlers.
	 * For example,
	 *
w  
Qiang Xue committed
321
	 * ~~~
w  
Qiang Xue committed
322 323
	 * $component->getEventHandlers($eventName)->insertAt(0, $eventHandler);
	 * ~~~
w  
Qiang Xue committed
324
	 *
Qiang Xue committed
325
	 * @param string $name the event name
w  
Qiang Xue committed
326 327
	 * @return Vector list of attached event handlers for the event
	 * @throws Exception if the event is not defined
Qiang Xue committed
328 329 330
	 */
	public function getEventHandlers($name)
	{
w  
Qiang Xue committed
331
		if ($this->hasEvent($name)) {
Qiang Xue committed
332
			$name = strtolower($name);
w  
Qiang Xue committed
333 334 335
			if (!isset($this->_e[$name])) {
				$this->_e[$name] = new Vector;
			}
Qiang Xue committed
336 337
			return $this->_e[$name];
		}
w  
Qiang Xue committed
338
		throw new Exception('Undefined event: ' . $name);
Qiang Xue committed
339 340 341 342 343
	}

	/**
	 * Attaches an event handler to an event.
	 *
w  
Qiang Xue committed
344 345
	 * This is equivalent to the following code:
	 *
w  
Qiang Xue committed
346
	 * ~~~
w  
Qiang Xue committed
347 348 349 350 351 352
	 * $component->getEventHandlers($eventName)->add($eventHandler);
	 * ~~~
	 *
	 * An event handler must be a valid PHP callback. The followings are
	 * some examples:
	 *
w  
Qiang Xue committed
353
	 * ~~~
Qiang Xue committed
354 355 356 357
	 * 'handleOnClick'					// handleOnClick() is a global function
	 * array($object, 'handleOnClick')	// $object->handleOnClick()
	 * array('Page', 'handleOnClick')	 // Page::handleOnClick()
	 * function($event) { ... }		   // anonymous function
w  
Qiang Xue committed
358
	 * ~~~
Qiang Xue committed
359 360 361
	 *
	 * An event handler must be defined with the following signature,
	 *
w  
Qiang Xue committed
362
	 * ~~~
w  
Qiang Xue committed
363 364
	 * function handlerName($event) {}
	 * ~~~
Qiang Xue committed
365
	 *
w  
Qiang Xue committed
366
	 * where `$event` is an [[Event]] object which includes parameters associated with the event.
Qiang Xue committed
367 368 369
	 *
	 * @param string $name the event name
	 * @param callback $handler the event handler
w  
Qiang Xue committed
370
	 * @throws Exception if the event is not defined
Qiang Xue committed
371 372
	 * @see detachEventHandler
	 */
Qiang Xue committed
373
	public function attachEventHandler($name, $handler)
Qiang Xue committed
374 375 376 377 378 379
	{
		$this->getEventHandlers($name)->add($handler);
	}

	/**
	 * Detaches an existing event handler.
w  
Qiang Xue committed
380
	 * This method is the opposite of [[attachEventHandler]].
Qiang Xue committed
381 382 383 384 385
	 * @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
386
	public function detachEventHandler($name, $handler)
Qiang Xue committed
387
	{
w  
Qiang Xue committed
388
		return $this->getEventHandlers($name)->remove($handler) !== false;
Qiang Xue committed
389 390 391 392 393 394 395
	}

	/**
	 * 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
396 397
	 * @param Event $event the event parameter
	 * @throws Exception if the event is undefined or an event handler is invalid.
Qiang Xue committed
398
	 */
Qiang Xue committed
399
	public function raiseEvent($name, $event)
Qiang Xue committed
400
	{
Qiang Xue committed
401
		$this->ensureBehaviors();
Qiang Xue committed
402
		$name = strtolower($name);
w  
Qiang Xue committed
403 404
		if ($event instanceof Event) {
			$event->name = $name;
405
			$event->handled = false;
w  
Qiang Xue committed
406
		}
w  
Qiang Xue committed
407 408 409
		if (isset($this->_e[$name])) {
			foreach ($this->_e[$name] as $handler) {
				if (is_string($handler) || $handler instanceof \Closure) {
Qiang Xue committed
410
					call_user_func($handler, $event);
Qiang Xue committed
411
				} elseif (is_callable($handler, true)) {
w  
Qiang Xue committed
412 413
					// an array: 0 - object, 1 - method name
					list($object, $method) = $handler;
Qiang Xue committed
414
					if (is_string($object)) { // static method call
Qiang Xue committed
415
						call_user_func($handler, $event);
Qiang Xue committed
416
					} elseif (method_exists($object, $method)) {
w  
Qiang Xue committed
417
						$object->$method($event);
Qiang Xue committed
418
					} else {
w  
Qiang Xue committed
419 420
						throw new Exception('Event "' . get_class($this) . '.' . $name . '" is attached with an invalid handler.');
					}
Qiang Xue committed
421
				} else {
w  
Qiang Xue committed
422 423 424 425 426
					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
427
					return;
w  
Qiang Xue committed
428 429
				}
			}
Qiang Xue committed
430
		} elseif (!$this->hasEvent($name)) {
431
			throw new Exception('Raising unknown event: ' . get_class($this) . '.' . $name);
w  
Qiang Xue committed
432 433 434 435 436 437 438 439 440 441 442
		}
	}

	/**
	 * 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)
	{
Qiang Xue committed
443
		$this->ensureBehaviors();
w  
Qiang Xue committed
444 445 446 447 448 449 450 451 452
		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.
Qiang Xue committed
453
	 * @param string|array|Behavior $behavior the behavior configuration. This can be one of the following:
w  
Qiang Xue committed
454 455 456
	 *
	 *  - a [[Behavior]] object
	 *  - a string specifying the behavior class
Qiang Xue committed
457
	 *  - an object configuration array that will be passed to [[\Yii::createObject()]] to create the behavior object.
w  
Qiang Xue committed
458 459 460 461 462 463
	 *
	 * @return Behavior the behavior object
	 * @see detachBehavior
	 */
	public function attachBehavior($name, $behavior)
	{
Qiang Xue committed
464 465
		$this->ensureBehaviors();
		return $this->attachBehaviorInternal($name, $behavior);
w  
Qiang Xue committed
466 467 468 469 470 471 472 473 474 475 476 477
	}

	/**
	 * 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)
	{
Qiang Xue committed
478
		$this->ensureBehaviors();
w  
Qiang Xue committed
479
		foreach ($behaviors as $name => $behavior) {
Qiang Xue committed
480
			$this->attachBehaviorInternal($name, $behavior);
w  
Qiang Xue committed
481 482 483 484 485 486 487 488 489 490 491 492 493 494
		}
	}

	/**
	 * 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])) {
			$behavior = $this->_b[$name];
			unset($this->_b[$name]);
Qiang Xue committed
495
			$behavior->detach($this);
w  
Qiang Xue committed
496
			return $behavior;
Qiang Xue committed
497 498
		} else {
			return null;
w  
Qiang Xue committed
499 500 501 502 503 504 505 506 507
		}
	}

	/**
	 * Detaches all behaviors from the component.
	 */
	public function detachBehaviors()
	{
		if ($this->_b !== null) {
Qiang Xue committed
508 509 510
			$behaviors = $this->_b;
			$this->_b = null;
			foreach ($behaviors as $name => $behavior) {
w  
Qiang Xue committed
511 512
				$this->detachBehavior($name);
			}
Qiang Xue committed
513 514 515 516 517 518 519 520 521 522 523
		}
	}

	/**
	 * Makes sure that the behaviors declared in [[behaviors()]] are attached to this component.
	 */
	public function ensureBehaviors()
	{
		if ($this->_b === null) {
			$this->_b = array();
			foreach ($this->behaviors() as $name => $behavior) {
Qiang Xue committed
524
				$this->attachBehaviorInternal($name, $behavior);
Qiang Xue committed
525
			}
w  
Qiang Xue committed
526 527
		}
	}
Qiang Xue committed
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542

	/**
	 * Attaches a behavior to this component.
	 * @param string $name the name of the behavior.
	 * @param string|array|Behavior $behavior the behavior to be attached
	 * @return Behavior the attached behavior.
	 */
	private function attachBehaviorInternal($name, $behavior)
	{
		if (!($behavior instanceof Behavior)) {
			$behavior = \Yii::createObject($behavior);
		}
		$behavior->attach($this);
		return $this->_b[$name] = $behavior;
	}
Qiang Xue committed
543
}