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

namespace yii\base;

w  
Qiang Xue committed
12
/**
Qiang Xue committed
13
 * Component is the base class that provides the *property*, *event* and *behavior* features.
w  
Qiang Xue committed
14
 *
Qiang Xue committed
15
 * @include @yii/base/Component.md
w  
Qiang Xue committed
16
 *
Qiang Xue committed
17 18
 * @property Behavior[] behaviors list of behaviors currently attached to this component
 *
w  
Qiang Xue committed
19
 * @author Qiang Xue <qiang.xue@gmail.com>
w  
Qiang Xue committed
20
 * @since 2.0
w  
Qiang Xue committed
21
 */
Qiang Xue committed
22
class Component extends \yii\base\Object
Qiang Xue committed
23
{
Qiang Xue committed
24 25 26
	/**
	 * @var Vector[] the attached event handlers (event name => handlers)
	 */
Qiang Xue committed
27
	private $_e;
Qiang Xue committed
28 29 30
	/**
	 * @var Behavior[] the attached behaviors (behavior name => behavior)
	 */
Qiang Xue committed
31
	private $_b;
Qiang Xue committed
32 33

	/**
w  
Qiang Xue committed
34 35 36 37 38 39 40 41 42 43
	 * 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
	 *  - 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,
Qiang Xue committed
44
	 * the behavior, or the value of a behavior's property
Qiang Xue committed
45
	 * @throws UnknownPropertyException if the property is not defined
Qiang Xue committed
46 47 48 49 50
	 * @see __set
	 */
	public function __get($name)
	{
		$getter = 'get' . $name;
51 52
		if (method_exists($this, $getter)) {
			// read property, e.g. getName()
Qiang Xue committed
53
			return $this->$getter();
54 55
		} else {
			// behavior property
Qiang Xue committed
56
			$this->ensureBehaviors();
Qiang Xue committed
57 58
			foreach ($this->_b as $behavior) {
				if ($behavior->canGetProperty($name)) {
Qiang Xue committed
59
					return $behavior->$name;
Qiang Xue committed
60
				}
Qiang Xue committed
61 62
			}
		}
Qiang Xue committed
63
		throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '.' . $name);
Qiang Xue committed
64 65 66
	}

	/**
67
	 * Sets the value of a component property.
w  
Qiang Xue committed
68 69 70
	 * This method will check in the following order and act accordingly:
	 *
	 *  - a property defined by a setter: set the property value
Qiang Xue committed
71 72
	 *  - an event in the format of "on xyz": attach the handler to the event "xyz"
	 *  - a behavior in the format of "as xyz": attach the behavior named as "xyz"
w  
Qiang Xue committed
73 74 75 76
	 *  - 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
77
	 * @param string $name the property name or the event name
w  
Qiang Xue committed
78
	 * @param mixed $value the property value
79 80
	 * @throws UnknownPropertyException if the property is not defined
	 * @throws InvalidCallException if the property is read-only.
Qiang Xue committed
81 82
	 * @see __get
	 */
Qiang Xue committed
83
	public function __set($name, $value)
Qiang Xue committed
84
	{
Qiang Xue committed
85
		$setter = 'set' . $name;
Qiang Xue committed
86 87
		if (method_exists($this, $setter)) {
			// set property
Qiang Xue committed
88 89
			$this->$setter($value);
			return;
Qiang Xue committed
90 91
		} elseif (strncmp($name, 'on ', 3) === 0) {
			// on event: attach event handler
Qiang Xue committed
92
			$name = trim(substr($name, 3));
Qiang Xue committed
93 94
			$this->getEventHandlers($name)->add($value);
			return;
Qiang Xue committed
95 96 97
		} elseif (strncmp($name, 'as ', 3) === 0) {
			// as behavior: attach behavior
			$name = trim(substr($name, 3));
98
			$this->attachBehavior($name, $value instanceof Behavior ? $value : \Yii::createObject($value));
99 100
		} else {
			// behavior property
Qiang Xue committed
101
			$this->ensureBehaviors();
Qiang Xue committed
102 103
			foreach ($this->_b as $behavior) {
				if ($behavior->canSetProperty($name)) {
Qiang Xue committed
104 105
					$behavior->$name = $value;
					return;
Qiang Xue committed
106
				}
Qiang Xue committed
107 108
			}
		}
Qiang Xue committed
109
		if (method_exists($this, 'get' . $name)) {
Qiang Xue committed
110
			throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '.' . $name);
Qiang Xue committed
111
		} else {
Qiang Xue committed
112
			throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '.' . $name);
Qiang Xue committed
113
		}
Qiang Xue committed
114 115 116 117
	}

	/**
	 * Checks if a property value is null.
w  
Qiang Xue committed
118 119 120 121 122 123 124
	 * This method will check in the following order and act accordingly:
	 *
	 *  - a property defined by a setter: return whether the property value is null
	 *  - 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
125
	 * @param string $name the property name or the event name
w  
Qiang Xue committed
126
	 * @return boolean whether the named property is null
Qiang Xue committed
127 128 129
	 */
	public function __isset($name)
	{
Qiang Xue committed
130
		$getter = 'get' . $name;
131
		if (method_exists($this, $getter)) {
Qiang Xue committed
132
			return $this->$getter() !== null;
133 134
		} else {
			// behavior property
Qiang Xue committed
135
			$this->ensureBehaviors();
Qiang Xue committed
136 137
			foreach ($this->_b as $behavior) {
				if ($behavior->canGetProperty($name)) {
Qiang Xue committed
138
					return $behavior->$name !== null;
Qiang Xue committed
139
				}
Qiang Xue committed
140 141 142 143 144 145 146
			}
		}
		return false;
	}

	/**
	 * Sets a component property to be null.
w  
Qiang Xue committed
147 148 149 150 151 152 153 154
	 * This method will check in the following order and act accordingly:
	 *
	 *  - a property defined by a setter: set the property value to be null
	 *  - 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
155
	 * @throws InvalidCallException if the property is read only.
Qiang Xue committed
156 157 158
	 */
	public function __unset($name)
	{
Qiang Xue committed
159
		$setter = 'set' . $name;
160
		if (method_exists($this, $setter)) {
Qiang Xue committed
161 162
			$this->$setter(null);
			return;
163 164
		} else {
			// behavior property
Qiang Xue committed
165
			$this->ensureBehaviors();
Qiang Xue committed
166 167
			foreach ($this->_b as $behavior) {
				if ($behavior->canSetProperty($name)) {
Qiang Xue committed
168 169
					$behavior->$name = null;
					return;
Qiang Xue committed
170
				}
Qiang Xue committed
171
			}
Qiang Xue committed
172 173
		}
		if (method_exists($this, 'get' . $name)) {
Qiang Xue committed
174
			throw new InvalidCallException('Unsetting read-only property: ' . get_class($this) . '.' . $name);
w  
Qiang Xue committed
175
		}
Qiang Xue committed
176 177 178 179
	}

	/**
	 * Calls the named method which is not a class method.
w  
Qiang Xue committed
180 181 182 183 184 185 186
	 * 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
187
	 * @param string $name the method name
Qiang Xue committed
188
	 * @param array $params method parameters
Qiang Xue committed
189
	 * @return mixed the method return value
Qiang Xue committed
190
	 * @throws UnknownMethodException when calling unknown method
Qiang Xue committed
191
	 */
Qiang Xue committed
192
	public function __call($name, $params)
Qiang Xue committed
193
	{
Qiang Xue committed
194 195 196
		$getter = 'get' . $name;
		if (method_exists($this, $getter)) {
			$func = $this->$getter();
Qiang Xue committed
197
			if ($func instanceof \Closure) {
Qiang Xue committed
198
				return call_user_func_array($func, $params);
Qiang Xue committed
199 200 201
			}
		}

Qiang Xue committed
202
		$this->ensureBehaviors();
Qiang Xue committed
203 204
		foreach ($this->_b as $object) {
			if (method_exists($object, $name)) {
Qiang Xue committed
205
				return call_user_func_array(array($object, $name), $params);
w  
Qiang Xue committed
206
			}
Qiang Xue committed
207
		}
Qiang Xue committed
208

Qiang Xue committed
209
		throw new UnknownMethodException('Calling unknown method: ' . get_class($this) . "::$name()");
Qiang Xue committed
210 211
	}

Qiang Xue committed
212 213 214 215 216 217
	/**
	 * This method is called after the object is created by cloning an existing one.
	 * It removes all behaviors because they are attached to the old object.
	 */
	public function __clone()
	{
Qiang Xue committed
218
		$this->_e = null;
Qiang Xue committed
219 220 221
		$this->_b = null;
	}

Qiang Xue committed
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 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 290 291 292 293 294 295 296 297 298 299 300 301 302
	/**
	 * Returns a value indicating whether a property is defined for this component.
	 * A property is defined if:
	 *
	 * - the class has a getter or setter method associated with the specified name
	 *   (in this case, property name is case-insensitive);
	 * - the class has a member variable with the specified name (when `$checkVar` is true);
	 * - an attached behavior has a property of the given name (when `$checkBehavior` is true).
	 *
	 * @param string $name the property name
	 * @param boolean $checkVar whether to treat member variables as properties
	 * @param boolean $checkBehavior whether to treat behaviors' properties as properties of this component
	 * @return boolean whether the property is defined
	 * @see canGetProperty
	 * @see canSetProperty
	 */
	public function hasProperty($name, $checkVar = true, $checkBehavior = true)
	{
		return $this->canGetProperty($name, $checkVar, $checkBehavior) || $this->canSetProperty($name, $checkVar, $checkBehavior);
	}

	/**
	 * Returns a value indicating whether a property can be read.
	 * A property can be read if:
	 *
	 * - the class has a getter method associated with the specified name
	 *   (in this case, property name is case-insensitive);
	 * - the class has a member variable with the specified name (when `$checkVar` is true);
	 * - an attached behavior has a readable property of the given name (when `$checkBehavior` is true).
	 *
	 * @param string $name the property name
	 * @param boolean $checkVar whether to treat member variables as properties
	 * @param boolean $checkBehavior whether to treat behaviors' properties as properties of this component
	 * @return boolean whether the property can be read
	 * @see canSetProperty
	 */
	public function canGetProperty($name, $checkVar = true, $checkBehavior = true)
	{
		if (method_exists($this, 'get' . $name) || $checkVar && property_exists($this, $name)) {
			return true;
		} else {
			$this->ensureBehaviors();
			foreach ($this->_b as $behavior) {
				if ($behavior->canGetProperty($name, $checkVar)) {
					return true;
				}
			}
			return false;
		}
	}

	/**
	 * Returns a value indicating whether a property can be set.
	 * A property can be written if:
	 *
	 * - the class has a setter method associated with the specified name
	 *   (in this case, property name is case-insensitive);
	 * - the class has a member variable with the specified name (when `$checkVar` is true);
	 * - an attached behavior has a writable property of the given name (when `$checkBehavior` is true).
	 *
	 * @param string $name the property name
	 * @param boolean $checkVar whether to treat member variables as properties
	 * @param boolean $checkBehavior whether to treat behaviors' properties as properties of this component
	 * @return boolean whether the property can be written
	 * @see canGetProperty
	 */
	public function canSetProperty($name, $checkVar = true, $checkBehavior = true)
	{
		if (method_exists($this, 'set' . $name) || $checkVar && property_exists($this, $name)) {
			return true;
		} else {
			$this->ensureBehaviors();
			foreach ($this->_b as $behavior) {
				if ($behavior->canSetProperty($name, $checkVar)) {
					return true;
				}
			}
			return false;
		}
	}

Qiang Xue committed
303 304 305 306 307 308 309 310 311 312 313
	/**
	 * 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(
Qiang Xue committed
314 315 316
	 *     'class' => 'BehaviorClass',
	 *     'property1' => 'value1',
	 *     'property2' => 'value2',
Qiang Xue committed
317 318 319
	 * )
	 * ~~~
	 *
Qiang Xue committed
320 321 322 323
	 * Note that a behavior class must extend from [[Behavior]]. Behavior names can be strings
	 * or integers. If the former, they uniquely identify the behaviors. If the latter, the corresponding
	 * behaviors are anonymous and their properties and methods will NOT be made available via the component
	 * (however, the behaviors can still respond to the component's events).
Qiang Xue committed
324
	 *
Qiang Xue committed
325
	 * Behaviors declared in this method will be attached to the component automatically (on demand).
Qiang Xue committed
326 327 328 329 330 331
	 *
	 * @return array the behavior configurations.
	 */
	public function behaviors()
	{
		return array();
Qiang Xue committed
332 333 334
	}

	/**
Qiang Xue committed
335
	 * Returns a value indicating whether there is any handler attached to the named event.
Qiang Xue committed
336
	 * @param string $name the event name
w  
Qiang Xue committed
337
	 * @return boolean whether there is any handler attached to the event.
Qiang Xue committed
338
	 */
w  
Qiang Xue committed
339
	public function hasEventHandlers($name)
Qiang Xue committed
340
	{
Qiang Xue committed
341
		$this->ensureBehaviors();
w  
Qiang Xue committed
342
		return isset($this->_e[$name]) && $this->_e[$name]->getCount();
Qiang Xue committed
343 344 345 346
	}

	/**
	 * Returns the list of attached event handlers for an event.
w  
Qiang Xue committed
347 348 349
	 * You may manipulate the returned [[Vector]] object by adding or removing handlers.
	 * For example,
	 *
w  
Qiang Xue committed
350
	 * ~~~
w  
Qiang Xue committed
351 352
	 * $component->getEventHandlers($eventName)->insertAt(0, $eventHandler);
	 * ~~~
w  
Qiang Xue committed
353
	 *
Qiang Xue committed
354
	 * @param string $name the event name
w  
Qiang Xue committed
355
	 * @return Vector list of attached event handlers for the event
Qiang Xue committed
356 357 358
	 */
	public function getEventHandlers($name)
	{
Qiang Xue committed
359 360
		if (!isset($this->_e[$name])) {
			$this->_e[$name] = new Vector;
Qiang Xue committed
361
		}
Qiang Xue committed
362 363
		$this->ensureBehaviors();
		return $this->_e[$name];
Qiang Xue committed
364 365 366 367 368
	}

	/**
	 * Attaches an event handler to an event.
	 *
w  
Qiang Xue committed
369 370
	 * This is equivalent to the following code:
	 *
w  
Qiang Xue committed
371
	 * ~~~
w  
Qiang Xue committed
372 373 374 375 376 377
	 * $component->getEventHandlers($eventName)->add($eventHandler);
	 * ~~~
	 *
	 * An event handler must be a valid PHP callback. The followings are
	 * some examples:
	 *
w  
Qiang Xue committed
378
	 * ~~~
Qiang Xue committed
379 380 381 382
	 * function($event) { ... }         // anonymous function
	 * array($object, 'handleClick')    // $object->handleClick()
	 * array('Page', 'handleClick')     // Page::handleClick()
	 * 'handleClick'                    // global function handleClick()
w  
Qiang Xue committed
383
	 * ~~~
Qiang Xue committed
384 385 386
	 *
	 * An event handler must be defined with the following signature,
	 *
w  
Qiang Xue committed
387
	 * ~~~
w  
Qiang Xue committed
388 389
	 * function handlerName($event) {}
	 * ~~~
Qiang Xue committed
390
	 *
w  
Qiang Xue committed
391
	 * where `$event` is an [[Event]] object which includes parameters associated with the event.
Qiang Xue committed
392 393
	 *
	 * @param string $name the event name
Qiang Xue committed
394
	 * @param string|array|\Closure $handler the event handler
Qiang Xue committed
395
	 * @see off()
Qiang Xue committed
396
	 */
Qiang Xue committed
397
	public function on($name, $handler)
Qiang Xue committed
398 399 400 401 402
	{
		$this->getEventHandlers($name)->add($handler);
	}

	/**
Qiang Xue committed
403 404
	 * Detaches an existing event handler from this component.
	 * This method is the opposite of [[on()]].
Qiang Xue committed
405
	 * @param string $name event name
Qiang Xue committed
406
	 * @param string|array|\Closure $handler the event handler to be removed
Qiang Xue committed
407
	 * @return boolean if a handler is found and detached
Qiang Xue committed
408
	 * @see on()
Qiang Xue committed
409
	 */
Qiang Xue committed
410
	public function off($name, $handler)
Qiang Xue committed
411
	{
w  
Qiang Xue committed
412
		return $this->getEventHandlers($name)->remove($handler) !== false;
Qiang Xue committed
413 414 415
	}

	/**
Qiang Xue committed
416
	 * Triggers an event.
Qiang Xue committed
417 418 419
	 * This method represents the happening of an event. It invokes
	 * all attached handlers for the event.
	 * @param string $name the event name
Qiang Xue committed
420
	 * @param Event $event the event parameter. If not set, a default [[Event]] object will be created.
Qiang Xue committed
421
	 */
Qiang Xue committed
422
	public function trigger($name, $event = null)
Qiang Xue committed
423
	{
Qiang Xue committed
424
		$this->ensureBehaviors();
425
		if (isset($this->_e[$name]) && $this->_e[$name]->getCount()) {
Qiang Xue committed
426 427
			if ($event === null) {
				$event = new Event($this);
Qiang Xue committed
428
			}
Qiang Xue committed
429 430
			$event->handled = false;
			$event->name = $name;
w  
Qiang Xue committed
431
			foreach ($this->_e[$name] as $handler) {
Qiang Xue committed
432
				call_user_func($handler, $event);
w  
Qiang Xue committed
433 434
				// stop further handling if the event is handled
				if ($event instanceof Event && $event->handled) {
Qiang Xue committed
435
					return;
w  
Qiang Xue committed
436 437 438 439 440 441 442
				}
			}
		}
	}

	/**
	 * Returns the named behavior object.
Qiang Xue committed
443
	 * @param string $name the behavior name
w  
Qiang Xue committed
444 445
	 * @return Behavior the behavior object, or null if the behavior does not exist
	 */
Qiang Xue committed
446
	public function getBehavior($name)
w  
Qiang Xue committed
447
	{
Qiang Xue committed
448
		$this->ensureBehaviors();
Qiang Xue committed
449 450 451 452 453 454 455 456 457 458 459
		return isset($this->_b[$name]) ? $this->_b[$name] : null;
	}

	/**
	 * Returns all behaviors attached to this component.
	 * @return Behavior[] list of behaviors attached to this component
	 */
	public function getBehaviors()
	{
		$this->ensureBehaviors();
		return $this->_b;
w  
Qiang Xue committed
460 461 462 463 464 465 466
	}

	/**
	 * 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.
Qiang Xue committed
467
	 * @param string $name the name of the behavior.
Qiang Xue committed
468
	 * @param string|array|Behavior $behavior the behavior configuration. This can be one of the following:
w  
Qiang Xue committed
469 470 471
	 *
	 *  - a [[Behavior]] object
	 *  - a string specifying the behavior class
Qiang Xue committed
472
	 *  - an object configuration array that will be passed to [[\Yii::createObject()]] to create the behavior object.
w  
Qiang Xue committed
473 474 475 476 477 478
	 *
	 * @return Behavior the behavior object
	 * @see detachBehavior
	 */
	public function attachBehavior($name, $behavior)
	{
Qiang Xue committed
479 480
		$this->ensureBehaviors();
		return $this->attachBehaviorInternal($name, $behavior);
w  
Qiang Xue committed
481 482 483 484 485
	}

	/**
	 * Attaches a list of behaviors to the component.
	 * Each behavior is indexed by its name and should be a [[Behavior]] object,
Qiang Xue committed
486
	 * a string specifying the behavior class, or an configuration array for creating the behavior.
w  
Qiang Xue committed
487 488 489 490 491
	 * @param array $behaviors list of behaviors to be attached to the component
	 * @see attachBehavior
	 */
	public function attachBehaviors($behaviors)
	{
Qiang Xue committed
492
		$this->ensureBehaviors();
w  
Qiang Xue committed
493
		foreach ($behaviors as $name => $behavior) {
Qiang Xue committed
494
			$this->attachBehaviorInternal($name, $behavior);
w  
Qiang Xue committed
495 496 497 498 499 500 501 502 503 504 505 506 507 508
		}
	}

	/**
	 * 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]);
509
			$behavior->detach();
w  
Qiang Xue committed
510
			return $behavior;
Qiang Xue committed
511 512
		} else {
			return null;
w  
Qiang Xue committed
513 514 515 516 517 518 519 520 521
		}
	}

	/**
	 * Detaches all behaviors from the component.
	 */
	public function detachBehaviors()
	{
		if ($this->_b !== null) {
Qiang Xue committed
522
			foreach ($this->_b as $name => $behavior) {
w  
Qiang Xue committed
523 524
				$this->detachBehavior($name);
			}
Qiang Xue committed
525
		}
Qiang Xue committed
526
		$this->_b = array();
Qiang Xue committed
527 528 529 530 531 532 533 534 535 536
	}

	/**
	 * 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
537
				$this->attachBehaviorInternal($name, $behavior);
Qiang Xue committed
538
			}
w  
Qiang Xue committed
539 540
		}
	}
Qiang Xue committed
541 542 543

	/**
	 * Attaches a behavior to this component.
Qiang Xue committed
544
	 * @param string $name the name of the behavior.
Qiang Xue committed
545 546 547 548 549 550 551 552
	 * @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);
		}
Qiang Xue committed
553
		if (isset($this->_b[$name])) {
554
			$this->_b[$name]->detach();
Qiang Xue committed
555
		}
Qiang Xue committed
556 557
		$behavior->attach($this);
		return $this->_b[$name] = $behavior;
Qiang Xue committed
558
	}
Qiang Xue committed
559
}