Component.php 17.4 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;

Qiang Xue committed
10 11
use Yii;

w  
Qiang Xue committed
12
/**
Qiang Xue committed
13
 * @include @yii/base/Component.md
w  
Qiang Xue committed
14
 * @author Qiang Xue <qiang.xue@gmail.com>
w  
Qiang Xue committed
15
 * @since 2.0
w  
Qiang Xue committed
16
 */
Qiang Xue committed
17
class Component extends Object
Qiang Xue committed
18
{
Qiang Xue committed
19
	/**
Qiang Xue committed
20
	 * @var array the attached event handlers (event name => handlers)
Qiang Xue committed
21
	 */
Qiang Xue committed
22
	private $_events;
Qiang Xue committed
23 24 25
	/**
	 * @var Behavior[] the attached behaviors (behavior name => behavior)
	 */
Qiang Xue committed
26
	private $_behaviors;
Qiang Xue committed
27 28

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

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

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

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

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

Qiang Xue committed
196
		$this->ensureBehaviors();
Qiang Xue committed
197
		foreach ($this->_behaviors as $object) {
Qiang Xue committed
198
			if (method_exists($object, $name)) {
Qiang Xue committed
199
				return call_user_func_array(array($object, $name), $params);
w  
Qiang Xue committed
200
			}
Qiang Xue committed
201
		}
Qiang Xue committed
202

Qiang Xue committed
203
		throw new UnknownMethodException('Calling unknown method: ' . get_class($this) . "::$name()");
Qiang Xue committed
204 205
	}

Qiang Xue committed
206 207 208 209 210 211
	/**
	 * 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
212 213
		$this->_events = null;
		$this->_behaviors = null;
Qiang Xue committed
214 215
	}

Qiang Xue committed
216 217 218 219 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
	/**
	 * 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();
Qiang Xue committed
258
			foreach ($this->_behaviors as $behavior) {
Qiang Xue committed
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
				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();
Qiang Xue committed
288
			foreach ($this->_behaviors as $behavior) {
Qiang Xue committed
289 290 291 292 293 294 295 296
				if ($behavior->canSetProperty($name, $checkVar)) {
					return true;
				}
			}
			return false;
		}
	}

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

	/**
Qiang Xue committed
329
	 * Returns a value indicating whether there is any handler attached to the named event.
Qiang Xue committed
330
	 * @param string $name the event name
w  
Qiang Xue committed
331
	 * @return boolean whether there is any handler attached to the event.
Qiang Xue committed
332
	 */
w  
Qiang Xue committed
333
	public function hasEventHandlers($name)
Qiang Xue committed
334
	{
Qiang Xue committed
335
		$this->ensureBehaviors();
Qiang Xue committed
336
		return !empty($this->_events[$name]);
Qiang Xue committed
337 338 339 340 341
	}

	/**
	 * Attaches an event handler to an event.
	 *
w  
Qiang Xue committed
342 343 344
	 * An event handler must be a valid PHP callback. The followings are
	 * some examples:
	 *
w  
Qiang Xue committed
345
	 * ~~~
Qiang Xue committed
346
	 * function ($event) { ... }         // anonymous function
Qiang Xue committed
347 348 349
	 * array($object, 'handleClick')    // $object->handleClick()
	 * array('Page', 'handleClick')     // Page::handleClick()
	 * 'handleClick'                    // global function handleClick()
w  
Qiang Xue committed
350
	 * ~~~
Qiang Xue committed
351 352 353
	 *
	 * An event handler must be defined with the following signature,
	 *
w  
Qiang Xue committed
354
	 * ~~~
Qiang Xue committed
355
	 * function ($event)
w  
Qiang Xue committed
356
	 * ~~~
Qiang Xue committed
357
	 *
w  
Qiang Xue committed
358
	 * where `$event` is an [[Event]] object which includes parameters associated with the event.
Qiang Xue committed
359 360
	 *
	 * @param string $name the event name
Qiang Xue committed
361 362 363
	 * @param callback $handler the event handler
	 * @param mixed $data the data to be passed to the event handler when the event is triggered.
	 * When the event handler is invoked, this data can be accessed via [[Event::data]].
Qiang Xue committed
364
	 * @see off()
Qiang Xue committed
365
	 */
Qiang Xue committed
366
	public function on($name, $handler, $data = null)
Qiang Xue committed
367
	{
Qiang Xue committed
368 369
		$this->ensureBehaviors();
		$this->_events[$name][] = array($handler, $data);
Qiang Xue committed
370 371 372
	}

	/**
Qiang Xue committed
373 374
	 * Detaches an existing event handler from this component.
	 * This method is the opposite of [[on()]].
Qiang Xue committed
375
	 * @param string $name event name
Qiang Xue committed
376 377
	 * @param callback $handler the event handler to be removed.
	 * If it is null, all handlers attached to the named event will be removed.
Qiang Xue committed
378
	 * @return boolean if a handler is found and detached
Qiang Xue committed
379
	 * @see on()
Qiang Xue committed
380
	 */
Qiang Xue committed
381
	public function off($name, $handler = null)
Qiang Xue committed
382
	{
Qiang Xue committed
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
		$this->ensureBehaviors();
		if (isset($this->_events[$name])) {
			if ($handler === null) {
				$this->_events[$name] = array();
			} else {
				$removed = false;
				foreach ($this->_events[$name] as $i => $event) {
					if ($event[0] === $handler) {
						unset($this->_events[$name][$i]);
						$removed = true;
					}
				}
				if ($removed) {
					$this->_events[$name] = array_values($this->_events[$name]);
				}
				return $removed;
			}
		}
		return false;
Qiang Xue committed
402 403 404
	}

	/**
Qiang Xue committed
405
	 * Triggers an event.
Qiang Xue committed
406 407 408
	 * 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
409
	 * @param Event $event the event parameter. If not set, a default [[Event]] object will be created.
Qiang Xue committed
410
	 */
Qiang Xue committed
411
	public function trigger($name, $event = null)
Qiang Xue committed
412
	{
Qiang Xue committed
413
		$this->ensureBehaviors();
Qiang Xue committed
414
		if (!empty($this->_events[$name])) {
Qiang Xue committed
415
			if ($event === null) {
Qiang Xue committed
416 417 418 419
				$event = new Event;
			}
			if ($event->sender === null) {
				$event->sender = $this;
Qiang Xue committed
420
			}
Qiang Xue committed
421 422
			$event->handled = false;
			$event->name = $name;
Qiang Xue committed
423 424 425
			foreach ($this->_events[$name] as $handler) {
				$event->data = $handler[1];
				call_user_func($handler[0], $event);
w  
Qiang Xue committed
426 427
				// stop further handling if the event is handled
				if ($event instanceof Event && $event->handled) {
Qiang Xue committed
428
					return;
w  
Qiang Xue committed
429 430 431 432 433 434 435
				}
			}
		}
	}

	/**
	 * Returns the named behavior object.
Qiang Xue committed
436
	 * @param string $name the behavior name
w  
Qiang Xue committed
437 438
	 * @return Behavior the behavior object, or null if the behavior does not exist
	 */
Qiang Xue committed
439
	public function getBehavior($name)
w  
Qiang Xue committed
440
	{
Qiang Xue committed
441
		$this->ensureBehaviors();
Qiang Xue committed
442
		return isset($this->_behaviors[$name]) ? $this->_behaviors[$name] : null;
Qiang Xue committed
443 444 445 446 447 448 449 450 451
	}

	/**
	 * Returns all behaviors attached to this component.
	 * @return Behavior[] list of behaviors attached to this component
	 */
	public function getBehaviors()
	{
		$this->ensureBehaviors();
Qiang Xue committed
452
		return $this->_behaviors;
w  
Qiang Xue committed
453 454 455 456 457 458
	}

	/**
	 * 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
Qiang Xue committed
459
	 * this component by calling the [[Behavior::attach()]] method.
Qiang Xue committed
460
	 * @param string $name the name of the behavior.
Qiang Xue committed
461
	 * @param string|array|Behavior $behavior the behavior configuration. This can be one of the following:
w  
Qiang Xue committed
462 463 464
	 *
	 *  - a [[Behavior]] object
	 *  - a string specifying the behavior class
Qiang Xue committed
465
	 *  - an object configuration array that will be passed to [[Yii::createObject()]] to create the behavior object.
w  
Qiang Xue committed
466 467 468 469 470 471
	 *
	 * @return Behavior the behavior object
	 * @see detachBehavior
	 */
	public function attachBehavior($name, $behavior)
	{
Qiang Xue committed
472 473
		$this->ensureBehaviors();
		return $this->attachBehaviorInternal($name, $behavior);
w  
Qiang Xue committed
474 475 476 477 478
	}

	/**
	 * Attaches a list of behaviors to the component.
	 * Each behavior is indexed by its name and should be a [[Behavior]] object,
Qiang Xue committed
479
	 * a string specifying the behavior class, or an configuration array for creating the behavior.
w  
Qiang Xue committed
480 481 482 483 484
	 * @param array $behaviors list of behaviors to be attached to the component
	 * @see attachBehavior
	 */
	public function attachBehaviors($behaviors)
	{
Qiang Xue committed
485
		$this->ensureBehaviors();
w  
Qiang Xue committed
486
		foreach ($behaviors as $name => $behavior) {
Qiang Xue committed
487
			$this->attachBehaviorInternal($name, $behavior);
w  
Qiang Xue committed
488 489 490 491 492
		}
	}

	/**
	 * Detaches a behavior from the component.
Qiang Xue committed
493
	 * The behavior's [[Behavior::detach()]] method will be invoked.
w  
Qiang Xue committed
494 495 496 497 498
	 * @param string $name the behavior's name.
	 * @return Behavior the detached behavior. Null if the behavior does not exist.
	 */
	public function detachBehavior($name)
	{
Qiang Xue committed
499 500 501
		if (isset($this->_behaviors[$name])) {
			$behavior = $this->_behaviors[$name];
			unset($this->_behaviors[$name]);
502
			$behavior->detach();
w  
Qiang Xue committed
503
			return $behavior;
Qiang Xue committed
504 505
		} else {
			return null;
w  
Qiang Xue committed
506 507 508 509 510 511 512 513
		}
	}

	/**
	 * Detaches all behaviors from the component.
	 */
	public function detachBehaviors()
	{
Qiang Xue committed
514 515
		if ($this->_behaviors !== null) {
			foreach ($this->_behaviors as $name => $behavior) {
w  
Qiang Xue committed
516 517
				$this->detachBehavior($name);
			}
Qiang Xue committed
518
		}
Qiang Xue committed
519
		$this->_behaviors = array();
Qiang Xue committed
520 521 522 523 524 525 526
	}

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

	/**
	 * Attaches a behavior to this component.
Qiang Xue committed
537
	 * @param string $name the name of the behavior.
Qiang Xue committed
538 539 540 541 542 543
	 * @param string|array|Behavior $behavior the behavior to be attached
	 * @return Behavior the attached behavior.
	 */
	private function attachBehaviorInternal($name, $behavior)
	{
		if (!($behavior instanceof Behavior)) {
Qiang Xue committed
544
			$behavior = Yii::createObject($behavior);
Qiang Xue committed
545
		}
Qiang Xue committed
546 547
		if (isset($this->_behaviors[$name])) {
			$this->_behaviors[$name]->detach();
Qiang Xue committed
548
		}
Qiang Xue committed
549
		$behavior->attach($this);
Qiang Xue committed
550
		return $this->_behaviors[$name] = $behavior;
Qiang Xue committed
551
	}
Qiang Xue committed
552
}