继承树分析
看yii2框架的源代码会发现,请求的入口,也就是index.php文件,相当的简单,在入口文件中有一行下列代码:
(new yii\web\Application($config))->run();
这里面实例化了一个Application类的对象,并调用了这个对象的run方法,这篇文章要记录的,就是这个类的来龙去脉。
想要理清这个类具体是干什么的,首先来分析一下这个类的所有父类,这个类的继承树如下,
- yii\web\Application
- yii\base\Application
- yii\base\Module
- yii\di\ServiceLocator
- yii\base\Component
- yii\base\Object
- yii\base\Configurable
这个列表后边的依次是前面的父类。那么现在就从下往上依次研究一下这些类。
yii\base\Configurable
这个类只是一个很简单的接口,原代码如下:
interface Configurable
{
}
竟然什么都没有。。。那么这个类的功能到底是什么呢,通过查看注释可以看出,这个Configurable接口其实就是定义了一种约定,所有实现这个接口的类都必须满足一个条件,这个条件就是这个类的构造函数的最后一个参数是一个配置数组,其中key为这个类对应的属性,value为属性对应的值。构造函数需要采用如下的形式:
public function __constructor($param1, $param2, ..., $config = []) ```
这个类很简单,接下来继续来看。
## yii\base\Object
先来看一下这个类的方法有哪些:
```php
class Object implements Configurable
{
public static function className()
{
return get_called_class();
}
public function __construct($config = [])
{
if (!empty($config)) {
Yii::configure($this, $config);
}
$this->init();
}
public function init(){
}
public function __get($name)
public function __set($name, $value)
public function __isset($name)
public function __unset($name)
public function __call($name, $params)
public function hasProperty($name, $checkVars = true)
public function canGetProperty($name, $checkVars = true)
public function canSetProperty($name, $checkVars = true)
public function hasMethod($name)
}
其中只有init是个空方法。需要说明一点的是,yii\base\Object这个类提供了yii2框架所采用的一个非常重要的特性:属性。属性用来表示对象的状态,定义一个属性通常采用如下的方法:
private $_label;//第一步,定义一个private的属性
public function getLabel() // 设置get方法,可读属性
{
return $this->_label;
}
public function setLabel($value) //设置set方法,可写属性
{
$this->_label = $value;
}
上面是对属性这个概念进行分析,Object对象是怎么提供这个特性的呢,这里首先要看一下构造函数中的代码:
Yii::configure($this, $config);
定位到这个函数:
public static function configure($object, $properties)
{
foreach ($properties as $name => $value) {
$object->$name = $value;
}
return $object;
}
看起来好简单的样子,遍历,然后属性赋值。当调用类不存在的属性时会调用魔术方法,Object类重载了魔术方法:
public function __set($name, $value)
{
$setter = 'set' . $name;
if (method_exists($this, $setter)) {
$this->$setter($value);
} elseif (method_exists($this, 'get' . $name)) {
throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name);
} else {
throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name);
}
}
当赋值某一个不存在的属性时会去调用相应的set方法,相应的获取某个属性的值会调用get方法。
每个Object子类如果有构造方法,在其中一定会调用父类的构造方法,并将property数组传递过去,由此终于知道Object类怎么实现属性这个特性了。如果我们想要以及编写的类拥有属性这个特性,首先需要为每一个属性设定相应的get和set方法,然后在实例化的时候传递一个配置数组,将这个数组传递到父类的构造函数就行了。所以在入口文件,实例化Application的时候,传递了一个$config数组,里面的key便是这个Application的一个个属性。
还需要注意的一点就是,在Object的构造函数中,调用了init方法进行一些初始化,在子类中可以覆盖这个方法进行一些初始化的操作