组件(component),是Yii框架的基类,实现了属性、事件、行为三类功能,如果需要事件和行为的功能,需要继承该类,不需要可直接继承Object类;
1 namespace yii\base; 2 3 use Yii; 4 5 /** 6 * Component is the base class that implements the *property*, *event* and *behavior* features. 7 * 组件,是Yii框架的基类,实现了属性、事件、行为三类功能 8 * Component provides the *event* and *behavior* features, in addition to the *property* feature which is implemented in 9 * its parent class [[Object]]. 10 * 组件提供了事件和行为功能,通过附加到父类[[Object]]的属性功能中 11 * Event is a way to "inject" custom code into existing code at certain places. For example, a comment object can trigger 12 * an "add" event when the user adds a comment. We can write custom code and attach it to this event so that when the event 13 * is triggered (i.e. comment will be added), our custom code will be executed. 14 * 事件,可以在特定的时点,触发执行预先设定的一段代码,例如一个评论对象能够在用户添加评论的时候触发一个add事件,我们能够自定义一些代码添加到 15 * 这个事件中,在添加评论的时候会触发事件,执行自定义的代码 16 * An event is identified by a name that should be unique within the class it is defined at. Event names are *case-sensitive*. 17 * 事件的名称在类中是唯一的,区分大小写 18 * One or multiple PHP callbacks, called *event handlers*, can be attached to an event. You can call [[trigger()]] to 19 raise an event. When an event is raised, the event handlers will be invoked automatically in the order they were 20 attached. 21 * 一个或者多个事件处理程序嫩巩固被添加到一个事件,能够通过调用[[trigger()]]触发事件,事件处理程序将会按照添加的先后顺序依次执行 22 * To attach an event handler to an event, call [[on()]]: 23 * 添加事件是通过on()方法添加 24 * ```php 25 * $post->on(‘update‘, function ($event) { //给update方法添加一个匿名函数的时间,该事件的作用是发送邮件信息 26 * // send email notification 27 * }); 28 * ``` 29 * 30 * In the above, an anonymous function is attached to the "update" event of the post. You may attach 31 * the following types of event handlers: 32 * 事件处理程序有以下几种形式 33 * - anonymous function: `function ($event) { ... }`匿名函数 34 * - object method: `[$object, ‘handleAdd‘]` 对象的方法 以数组的方式传入 35 * - static class method: `[‘Page‘, ‘handleAdd‘]` 静态方法 以数组的方式传入 36 * - global function: `‘handleAdd‘` 全局函数 不带参数和括号,只有函数名 37 * 38 * The signature of an event handler should be like the following: 39 * 40 * ```php 41 * function foo($event) 事件处理程序的格式 42 * ``` 43 * 44 * where `$event` is an [[Event]] object which includes parameters associated with the event. 45 * 46 * You can also attach a handler to an event when configuring a component with a configuration array. 47 * The syntax is like the following: 48 * 可以通过配置文件来配置事件,格式如下 49 * ```php 50 * [ 51 * ‘on add‘ => function ($event) { ... } 给add 添加一个事件 52 * ] 53 * ``` 54 * 55 * where `on add` stands for attaching an event to the `add` event. 56 * 57 * Sometimes, you may want to associate extra data with an event handler when you attach it to an event 58 * and then access it when the handler is invoked. You may do so by 59 * on()方法的第三个参数可以给事件处理程序传入参数 60 * ```php 61 * $post->on(‘update‘, function ($event) { 62 * // the data can be accessed via $event->data 63 * }, $data); 64 * ``` 65 * 66 * A behavior is an instance of [[Behavior]] or its child class. A component can be attached with one or multiple 67 * behaviors. When a behavior is attached to a component, its public properties and methods can be accessed via the 68 * component directly, as if the component owns those properties and methods. 69 * 行为可以在不修改现有类的情况下,对类的功能进行扩充。 通过将行为绑定到一个类,可以使类具有行为本身所定义的属性和方法, 70 * 就好像类本来就有这些属性和方法一样。 而且不需要写一个新的类去继承或包含现有类。 71 * To attach a behavior to a component, declare it in [[behaviors()]], or explicitly call [[attachBehavior]]. Behaviors 72 * declared in [[behaviors()]] are automatically attached to the corresponding component. 73 * 74 * One can also attach a behavior to a component when configuring it with a configuration array. The syntax is like the 75 * following: 76 * 配置行为的数组格式 77 * ```php 78 * [ 79 * ‘as tree‘ => [ 80 * ‘class‘ => ‘Tree‘, 81 * ], 82 * ] 83 * ``` 84 * 85 * where `as tree` stands for attaching a behavior named `tree`, and the array will be passed to [[\Yii::createObject()]] 86 * to create the behavior object. 87 * 88 * @property Behavior[] $behaviors List of behaviors attached to this component. This property is read-only. 89 * 90 * @author Qiang Xue <[email protected]> 91 * @since 2.0 92 */ 93 class Component extends Object 94 { 95 /** 96 * @var array 当前对象的已绑定的事件数组 (事件名 => 处理程序) 97 */ 98 private $_events = []; 99 /** 100 * @var Behavior[]|null 当前对象的已绑定的行为数组 (行为名 => 行为类). 初始化之前为空. 101 */ 102 private $_behaviors; 103 104 105 /** 106 * Returns the value of a component property. 107 * This method will check in the following order and act accordingly: 108 * 109 * - a property defined by a getter: return the getter result 110 * - a property of a behavior: return the behavior property value 111 * 112 * 重写 Object 中的 getter 方法,添加对 behaviors 的处理 113 * 如果当前组件的属性中没有该属性,则遍历 behaviors,如果其中有相应的属性,返回behaviors中的属性 114 * 115 * Do not call this method directly as it is a PHP magic method that 116 * will be implicitly called when executing `$value = $component->property;`. 117 * @param string $name 属性名 118 * @return mixed 属性值或者行为中的属性值 119 * @throws 属性未定义 120 * @throws 属性只读 121 * @see __set() 122 */ 123 public function __get($name) 124 { 125 $getter = ‘get‘ . $name; 126 if (method_exists($this, $getter)) { 127 // 构造getter方法,如果getter方法存在的话,调用getter方法,返回属性值 128 return $this->$getter(); 129 } else { 130 // behavior property 131 $this->ensureBehaviors();//调用该方法确保行为已绑定 132 foreach ($this->_behaviors as $behavior) { 133 if ($behavior->canGetProperty($name)) { 134 //调用父类的方法,判断检查对象或类是否能够获取 $name 属性,如果第二个参数为 true(默认是true),则不局限于是否有 getter 135 // 如果 behavior 中含有该属性,就返回 behavior 中的这个属性 136 return $behavior->$name; 137 } 138 } 139 } 140 if (method_exists($this, ‘set‘ . $name)) {//跟父类相同,判断属性是否只写 141 throw new InvalidCallException(‘Getting write-only property: ‘ . get_class($this) . ‘::‘ . $name); 142 } else {//抛出异常 143 throw new UnknownPropertyException(‘Getting unknown property: ‘ . get_class($this) . ‘::‘ . $name); 144 } 145 } 146 147 /** 148 * Sets the value of a component property. 149 * This method will check in the following order and act accordingly: 150 * 151 * - a property defined by a setter: set the property value 152 * - an event in the format of "on xyz": attach the handler to the event "xyz" 153 * - a behavior in the format of "as xyz": attach the behavior named as "xyz" 154 * - a property of a behavior: set the behavior property value 155 ** 156 * 重写 Object 中的 setter 方法 157 * 如果 $name 是 ‘on xyz‘,就会将 xyz 事件添加到该对象中 158 * 如果 $name 是 ‘as xyz‘,就会将 xyz 行为添加到该对象中 159 * 添加对 behaviors 的处理,循环 behaviors,如果其中有相应的属性,就设置它 160 * 161 * Do not call this method directly as it is a PHP magic method that 162 * will be implicitly called when executing `$component->property = $value;`. 163 * @param string $name 属性名或者事件名 164 * @param mixed $value the 属性值 165 * @throws UnknownPropertyException if the property is not defined 166 * @throws InvalidCallException if the property is read-only. 167 * @see __get() 168 */ 169 public function __set($name, $value) 170 { 171 $setter = ‘set‘ . $name;//在属性名前面加set构建setter方法 172 if (method_exists($this, $setter)) {//如果setter方法存在 173 // 调用该方法设置属性值 174 $this->$setter($value); 175 176 return; 177 } elseif (strncmp($name, ‘on ‘, 3) === 0) {//如果方法不存在,且$name以on+空格开始 178 // on event: attach event handler 179 $this->on(trim(substr($name, 3)), $value);//调用on方法将事件处理程序附加到事件 180 181 return; 182 } elseif (strncmp($name, ‘as ‘, 3) === 0) {//否则,如果$name以as+空格开始 183 // as behavior: attach behavior 184 $name = trim(substr($name, 3));//截取as后面的字符 185 $this->attachBehavior($name, $value instanceof Behavior ? $value : Yii::createObject($value));//用attachBehavior添加一个行为到组件 186 187 return; 188 } else {//否则 189 // behavior property 190 $this->ensureBehaviors(); 191 foreach ($this->_behaviors as $behavior) { 192 if ($behavior->canSetProperty($name)) {//遍历行为(behaviors),如果行为中有可以设置的属性 193 $behavior->$name = $value;//给该行为类中的属性设置属性值 194 195 return; 196 } 197 } 198 } 199 if (method_exists($this, ‘get‘ . $name)) {//如果该属性的get方法存在,表名该方法只读,抛出异常 200 throw new InvalidCallException(‘Setting read-only property: ‘ . get_class($this) . ‘::‘ . $name); 201 } else {//否则抛出异常,未知的属性 202 throw new UnknownPropertyException(‘Setting unknown property: ‘ . get_class($this) . ‘::‘ . $name); 203 } 204 } 205 206 /** 207 * Checks if a property is set, i.e. defined and not null. 208 * This method will check in the following order and act accordingly: 209 * 210 * - a property defined by a setter: return whether the property is set 211 * - a property of a behavior: return whether the property is set 212 * - return `false` for non existing properties 213 * 214 * 重写 Object 中的 isset 方法,添加对 behaviors 的处理, 215 * 如果component中不存在该属性,遍历 behaviors,如果其中有相应的属性,就认为有 216 * 217 * Do not call this method directly as it is a PHP magic method that 218 * will be implicitly called when executing `isset($component->property)`. 219 * @param string $name 属性名或事件名 220 * @return boolean whether the named property is set 221 * @see http://php.net/manual/en/function.isset.php 222 */ 223 public function __isset($name) 224 { 225 $getter = ‘get‘ . $name; 226 if (method_exists($this, $getter)) { 227 // 判断是否有getter方法,且是否有返回值,有 getter 方法且获取的值不为 null,才认为该属性存在 228 return $this->$getter() !== null; 229 } else { 230 // behavior property 231 $this->ensureBehaviors();//确定行为已经绑定 232 foreach ($this->_behaviors as $behavior) {//遍历 behaviors 233 if ($behavior->canGetProperty($name)) {//如果行为(behaviors)中有相应的属性,就认为有 234 return $behavior->$name !== null; 235 } 236 } 237 } 238 return false; 239 } 240 241 /** 242 * Sets a component property to be null. 243 * This method will check in the following order and act accordingly: 244 * 245 * - a property defined by a setter: set the property value to be null 246 * - a property of a behavior: set the property value to be null 247 * 248 * 重写 Object 中的 unset 方法,添加对 behaviors 的处理 249 * 如果component中不存在该属性,遍历 behaviors,如果其中有相应的属性,设置为空 250 * 251 * Do not call this method directly as it is a PHP magic method that 252 * will be implicitly called when executing `unset($component->property)`. 253 * @param string $name 属性名 254 * @throws InvalidCallException if the property is read only. 255 * @see http://php.net/manual/en/function.unset.php 256 */ 257 public function __unset($name) 258 { 259 $setter = ‘set‘ . $name; 260 if (method_exists($this, $setter)) { 261 // 如果setter方法存在,通过 setter 方法,将它设置为 null 262 $this->$setter(null); 263 return; 264 } else { 265 // behavior property 266 $this->ensureBehaviors(); 267 foreach ($this->_behaviors as $behavior) {//否则遍历 behaviors 268 if ($behavior->canSetProperty($name)) {//如果行为中有相应的属性,设置为空 269 $behavior->$name = null; 270 return; 271 } 272 } 273 } 274 //抛出异常 275 throw new InvalidCallException(‘Unsetting an unknown or read-only property: ‘ . get_class($this) . ‘::‘ . $name); 276 } 277 278 /** 279 * Calls the named method which is not a class method. 280 * 281 * This method will check if any attached behavior has 282 * the named method and will execute it if available. 283 * 284 * 重写 Object 中的 call 方法,添加对 behaviors 的处理 285 * 遍历 behaviors,如果其中有相应方法,就执行该 behavior 的方法 286 * 287 * Do not call this method directly as it is a PHP magic method that 288 * will be implicitly called when an unknown method is being invoked. 289 * @param string $name 方法名 290 * @param array $params 参数 291 * @return mixed 方法执行后的返回值 292 * @throws UnknownMethodException when calling unknown method 293 */ 294 public function __call($name, $params) 295 { 296 $this->ensureBehaviors();//确认行为已经绑定 297 foreach ($this->_behaviors as $object) { 298 if ($object->hasMethod($name)) {//调用父类的方法,判断该行为中是否有$name方法 299 return call_user_func_array([$object, $name], $params);//如果行为中有该方法,则执行该方法 300 } 301 } 302 throw new UnknownMethodException(‘Calling unknown method: ‘ . get_class($this) . "::$name()"); 303 } 304 305 /** 306 * This method is called after the object is created by cloning an existing one. 307 * It removes all behaviors because they are attached to the old object. 308 * 309 * 在对象克隆clone的时候调用这个方法,将其 _events 和 _behaviors 设置为空 310 * 当对象被复制后,PHP 5 会对对象的所有属性执行一个浅复制(shallow copy)。 311 * 所有的引用属性 仍然会是一个指向原来的变量的引用,所以没必要浪费内存 312 * 313 */ 314 public function __clone() 315 { 316 // 对象复制时,将它的 _events 设置为空数组,将 _behaviors 设置为 null 317 $this->_events = []; 318 $this->_behaviors = null; 319 } 320 321 /** 322 * Returns a value indicating whether a property is defined for this component. 323 * A property is defined if: 324 * 325 * - the class has a getter or setter method associated with the specified name 326 * (in this case, property name is case-insensitive); 327 * - the class has a member variable with the specified name (when `$checkVars` is true); 328 * - an attached behavior has a property of the given name (when `$checkBehaviors` is true). 329 * 330 * 与 Object 中的方法类似,只是添加了是否检测 behavior 的参数 331 * 332 * @param string $name the property name 333 * @param boolean $checkVars whether to treat member variables as properties 334 * @param boolean $checkBehaviors whether to treat behaviors‘ properties as properties of this component 335 * @return boolean whether the property is defined 336 * @see canGetProperty() 337 * @see canSetProperty() 338 */ 339 public function hasProperty($name, $checkVars = true, $checkBehaviors = true) 340 { 341 // $checkVars 参数,用来设置是否局限于是否有 getter/setter 342 //$checkBehaviors参数,用来设置是否检测behavior 343 return $this->canGetProperty($name, $checkVars, $checkBehaviors) || $this->canSetProperty($name, false, $checkBehaviors); 344 } 345 346 /** 347 * Returns a value indicating whether a property can be read. 348 * A property can be read if: 349 * 350 * - the class has a getter method associated with the specified name 351 * (in this case, property name is case-insensitive); 352 * - the class has a member variable with the specified name (when `$checkVars` is true); 353 * - an attached behavior has a readable property of the given name (when `$checkBehaviors` is true). 354 * 355 * 检查对象或类是否能够获取 $name 属性,增加了对behavior的检查 356 * 357 * @param string $name the property name 358 * @param boolean $checkVars whether to treat member variables as properties 359 * @param boolean $checkBehaviors whether to treat behaviors‘ properties as properties of this component 360 * @return boolean whether the property can be read 361 * @see canSetProperty() 362 */ 363 public function canGetProperty($name, $checkVars = true, $checkBehaviors = true) 364 { // $checkVars 参数,用来设置是否局限于是否有 getter/setter 365 if (method_exists($this, ‘get‘ . $name) || $checkVars && property_exists($this, $name)) { 366 return true; 367 } elseif ($checkBehaviors) { 368 //$checkBehaviors参数,用来设置是否检测behavior 369 $this->ensureBehaviors(); 370 foreach ($this->_behaviors as $behavior) { 371 if ($behavior->canGetProperty($name, $checkVars)) { 372 return true; 373 } 374 } 375 } 376 return false; 377 } 378 379 /** 380 * Returns a value indicating whether a property can be set. 381 * A property can be written if: 382 * 383 * - the class has a setter method associated with the specified name 384 * (in this case, property name is case-insensitive); 385 * - the class has a member variable with the specified name (when `$checkVars` is true); 386 * - an attached behavior has a writable property of the given name (when `$checkBehaviors` is true). 387 * 388 * 检查对象或类是否能够设置 $name 属性,增加了对behavior的检查 389 * 390 * @param string $name the property name 391 * @param boolean $checkVars whether to treat member variables as properties 392 * @param boolean $checkBehaviors whether to treat behaviors‘ properties as properties of this component 393 * @return boolean whether the property can be written 394 * @see canGetProperty() 395 */ 396 public function canSetProperty($name, $checkVars = true, $checkBehaviors = true) 397 { 398 // $checkVars 参数,用来设置是否局限于是否有 getter/setter 399 if (method_exists($this, ‘set‘ . $name) || $checkVars && property_exists($this, $name)) { 400 return true; 401 } elseif ($checkBehaviors) {//$checkBehaviors参数,用来设置是否检测behavior 402 $this->ensureBehaviors(); 403 foreach ($this->_behaviors as $behavior) { 404 if ($behavior->canSetProperty($name, $checkVars)) {//调用父类方法判断 405 return true; 406 } 407 } 408 } 409 return false; 410 }
时间: 2024-10-07 21:36:26