yii 之(new yii-web-Application($config))

本博客要講的是yii整個Application的創建過程,而不是運行過程哈,讀者注意,沒有run啊。
閱讀前須知:
web的Application          extends  yii\base\Application
yii\base\Application      extends  yii\base\module
yii\base\module            extends  yii\base\servicelocator
yii\base\servicelocator  extends  yii\base\component
yii\base\component      extends  yii\base\pbject
其中我最關心的 __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' => 'yii\db\Connection',
'dsn' => 'mysql:host=db1-dev.bj1.haodf.net;dbname=test',
'username' => 'weihu_dev',
'password' => '[email protected]',
'charset' => 'gbk',
],
'mailer' => [
'class' => 'yii\swiftmailer\Mailer',
'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'];



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章