yii 之(new yii-web-Application($config))
扫描二维码
随时随地手机看文章
本博客要讲的是yii整个Application的创建过程,而不是运行过程哈,读者注意,没有run啊。
阅读前须知:
web的Application extends yiibaseApplication
yiibaseApplication extends yiibasemodule
yiibasemodule extends yiibaseservicelocator
yiibaseservicelocator extends yiibasecomponent
yiibasecomponent extends yiibasepbject
其中我最关心的 __get __set函数是在component类中定义的
1.$config该变量是我们从配置文件加载出来的,一个配置数组的合集(其中的各种require文件就不说了啊)
2.Application的创建过程
public function __construct($config = []) { Yii::$app = $this; static::setInstance($this); $this->state = self::STATE_BEGIN; $this->preInit($config); $this->registerErrorHandler($config); Component::__construct($config); }
解读setInstance,函数定义如下:
public static function setInstance($instance) { if ($instance === null) { unset(Yii::$app->loadedModules[get_called_class()]); } else { Yii::$app->loadedModules[get_class($instance)] = $instance; } }
现在的loadedModules,我还没有具体看不知道啥意思,不过对于我这种小白来说,可以暂时忽略 解读preInit($config)
public function preInit(&$config) { if (!isset($config['id'])) { throw new InvalidConfigException('The "id" configuration for the Application is required.'); } if (isset($config['basePath'])) { $this->setBasePath($config['basePath']); unset($config['basePath']); } else { throw new InvalidConfigException('The "basePath" configuration for the Application is required.'); } if (isset($config['vendorPath'])) { $this->setVendorPath($config['vendorPath']); unset($config['vendorPath']); } else { // set "@vendor" $this->getVendorPath(); } if (isset($config['runtimePath'])) { $this->setRuntimePath($config['runtimePath']); unset($config['runtimePath']); } else { // set "@runtime" $this->getRuntimePath(); } if (isset($config['timeZone'])) { $this->setTimeZone($config['timeZone']); unset($config['timeZone']); } elseif (!ini_get('date.timezone')) { $this->setTimeZone('UTC'); } //以上除了id,意外,配置文件必须包含 basePath,vendorPath runtimePath等变量,并将变量放在module类中对应的成员变量中
//并且设置完成之后进行unset操作
// merge core components with custom components foreach ($this->coreComponents() as $id => $component) { if (!isset($config['components'][$id])) { $config['components'][$id] = $component; } elseif (is_array($config['components'][$id]) && !isset($config['components'][$id]['class'])) { $config['components'][$id]['class'] = $component['class']; } } }
需要注意的就是这个coreComponent函数了,这个函数,将web的Application和base的Application的coreComponents返回的数组进行合并操作,也就是说在 整个应用中需要用到的components都需要在webApplication的coreComponents或者其父类的coreComponents中进行定义,并且组件的定义优先取用配置文件中的定义 PS:以上这几步,我们可以认为是进行预整理$config 接下来是registerErrorHandler,这个函数以后会着重看,先按下不表。 看一下Component::__construct的定义,他最终调用的是Yii::Configure
public static function configure($object, $properties) { foreach ($properties as $name => $value) { $object->$name = $value; } return $object; }
接下来,要说的就是这个 ->$name = $value;我们知道这个$object是web下的Application,该类有一个父类是Component,不知道的可以去看,阅读前须知 该类中定义了注入函数__set
public function __set($name, $value) { $setter = 'set' . $name; if (method_exists($this, $setter)) { // set property $this->$setter($value); return; } elseif (strncmp($name, 'on ', 3) === 0) { // on event: attach event handler $this->on(trim(substr($name, 3)), $value); return; } elseif (strncmp($name, 'as ', 3) === 0) { // as behavior: attach behavior $name = trim(substr($name, 3)); $this->attachBehavior($name, $value instanceof Behavior ? $value : Yii::createObject($value)); return; } else { // behavior property $this->ensureBehaviors(); foreach ($this->_behaviors as $behavior) { if ($behavior->canSetProperty($name)) { $behavior->$name = $value; return; } } } if (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.$name函数),我们在配置文件中会定义诸如
return [ 'components' => [ 'db' => [ 'class' => 'yiidbConnection', 'dsn' => 'mysql:host=db1-dev.bj1.haodf.net;dbname=test', 'username' => 'weihu_dev', 'password' => 'hdf@haodf.com', 'charset' => 'gbk', ], 'mailer' => [ 'class' => 'yiiswiftmailerMailer', 'viewPath' => '@common/mail', // send all mails to a file by default. You have to set // 'useFileTransport' to false and configure a transport // for the mailer to send real emails. 'useFileTransport' => true, ], ], ];
但是在ServiceLocator中定义了也就是对于配置文件中的components,定义了setComponents函数进行处理
public function setComponents($components) { foreach ($components as $id => $component) { $this->set($id, $component); } }
继续分析
public function set($id, $definition) { if ($definition === null) { unset($this->_components[$id], $this->_definitions[$id]); return; } unset($this->_components[$id]); if (is_object($definition) || is_callable($definition, true)) { // an object, a class name, or a PHP callable $this->_definitions[$id] = $definition; } elseif (is_array($definition)) { // a configuration array if (isset($definition['class'])) { $this->_definitions[$id] = $definition; } else { throw new InvalidConfigException("The configuration for the "$id" component must contain a "class" element."); } } else { throw new InvalidConfigException("Unexpected configuration type for the "$id" component: " . gettype($definition)); } }
这样所有Components就会有单独的_components数组进行统一维护了!!!!(比如db,比如cache就是在这里进行组成的)
还需要说明的就是除了components意外的其他配置,
可以在配置文件中添加整个应用的事件或者行为而不是单独针对某个controller的了,其配置如下:
$config['on test']=['class'=>'test'];