Component.php 17.5 KB
Newer Older
Qiang Xue committed
1
<?php
w  
Qiang Xue committed
2 3
/**
 * @link http://www.yiiframework.com/
Qiang Xue committed
4
 * @copyright Copyright (c) 2008 Yii Software LLC
w  
Qiang Xue committed
5 6
 * @license http://www.yiiframework.com/license/
 */
Qiang Xue committed
7 8 9

namespace yii\base;

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

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

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

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

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

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

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

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

Qiang Xue committed
210 211 212 213 214 215
	/**
	 * 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
216
		$this->_e = null;
Qiang Xue committed
217 218 219
		$this->_b = null;
	}

Qiang Xue committed
220 221 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
	/**
	 * 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
301 302 303 304 305 306 307 308 309 310 311
	/**
	 * 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
312 313 314
	 *     'class' => 'BehaviorClass',
	 *     'property1' => 'value1',
	 *     'property2' => 'value2',
Qiang Xue committed
315 316 317
	 * )
	 * ~~~
	 *
Qiang Xue committed
318 319 320 321
	 * 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
322
	 *
Qiang Xue committed
323
	 * Behaviors declared in this method will be attached to the component automatically (on demand).
Qiang Xue committed
324 325 326 327 328 329
	 *
	 * @return array the behavior configurations.
	 */
	public function behaviors()
	{
		return array();
Qiang Xue committed
330 331 332
	}

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

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

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

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

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

	/**
	 * Returns the named behavior object.
Qiang Xue committed
444
	 * @param string $name the behavior name
w  
Qiang Xue committed
445 446
	 * @return Behavior the behavior object, or null if the behavior does not exist
	 */
Qiang Xue committed
447
	public function getBehavior($name)
w  
Qiang Xue committed
448
	{
Qiang Xue committed
449
		$this->ensureBehaviors();
Qiang Xue committed
450 451 452 453 454 455 456 457 458 459 460
		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
461 462 463 464 465 466 467
	}

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

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

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

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

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

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