/ yii2  

Yii2 源码阅读 01 - index.php

参见:入口脚本(Entry Scripts)

web/index

步骤:

  1. defined const
  2. composer vendor/autoload
  3. require Yii.php
  4. require config web.php
  5. new Application and run()

Yii.php

1、2 比较简单,从 3 开始,进入 /../vendor/yiisoft/yii2/Yii.php' 首行:

require __DIR__ . '/BaseYii.php';

进入 BaseYii.php

头部仍是 defined const,YII_DEBUG false YII_ENV prod

YII_ENABLE_ERROR_HANDLER true 默认启用错误处理,在 yii\base\Application::registerErrorHandler 中使用:

/**
* Registers the errorHandler component as a PHP error handler.
* @param array $config application config
*/
protected function registerErrorHandler(&$config)
{
if (YII_ENABLE_ERROR_HANDLER) {
if (!isset($config['components']['errorHandler']['class'])) {
echo "Error: no errorHandler component is configured.\n";
exit(1);
}
$this->set('errorHandler', $config['components']['errorHandler']);
unset($config['components']['errorHandler']);
$this->getErrorHandler()->register();
}
}

$this->set 调用的 yii\di\ServiceLocator::set 注册 component 定义定位器,后文再分析。

获取 error handler 注册 yii\base\ErrorHandler

/**
* Register this error handler.
* @since 2.0.32 this will not do anything if the error handler was already registered
*/
public function register()
{
if (!$this->_registered) {
ini_set('display_errors', false);
set_exception_handler([$this, 'handleException']);
if (defined('HHVM_VERSION')) {
set_error_handler([$this, 'handleHhvmError']);
} else {
set_error_handler([$this, 'handleError']);
}
if ($this->memoryReserveSize > 0) {
$this->_memoryReserve = str_repeat('x', $this->memoryReserveSize);
}
register_shutdown_function([$this, 'handleFatalError']);
$this->_registered = true;
}
}

错误处理:

  • set_exception_handler 自定义异常处理
  • set_error_handler 设置用户自定义的错误处理函数
  • register_shutdown_function 定义 PHP 程序执行完成后执行的函数

handleError 里最后,将抛出 yii\base\ErrorException,将处理交给 handleException

更详细的 异常处理 将在后文再分析,此处不展开。

BaseYii is the core helper class for the Yii framework.

返回 Yii 类:

class Yii extends \yii\BaseYii
{
}
// 没有再添加额外的属性或方法。

// 注册自动加载的执行方法 Yii::autoload
spl_autoload_register(['Yii', 'autoload'], true, true);

// $classMap 是 Yii 自动加载机制使用的 类映射
// 数组键是类名(不带前导反斜杠),数组值是相应的类文件路径(或[路径别名])
// 主要被用于 Yii::autoload
Yii::$classMap = require __DIR__ . '/classes.php';

// Container the dependency injection (DI) container used by createObject()
// 可以使用 Container::set() 来设置类所需的依赖项及其初始属性值
Yii::$container = new yii\di\Container();

分析下 Yii::autoload($className)

/**
* Class autoload loader.
*
* 当 PHP 遇到未知类时,将自动调用此方法。
* 该方法将按照以下过程尝试 include 类文件:
*
* 1. Search in [[classMap]];
* 2. If the class is namespaced (e.g. `yii\base\Component`),
* 它将尝试包含与相应路径 别名 相关联的文件 (e.g. `@yii/base/Component.php`);
*
* This autoloader allows loading classes that follow the [PSR-4 standard](http://www.php-fig.org/psr/psr-4/)
* and have its top-level namespace or sub-namespaces defined as path aliases.
*
* @param string $className 不带前导反斜杠 "\" 的完全限定类名
* @throws UnknownClassException if the class does not exist in the class file
*/
public static function autoload($className)
{
if (isset(static::$classMap[$className])) {
// 1. Search in [[classMap]];
// 框架的类
$classFile = static::$classMap[$className];
if (strpos($classFile, '@') === 0) {
// 可以利用这里重写框架里的类 非常巧妙
// When aliases `@yii` and `@yii/bootstrap` are defined, classes in the `yii\bootstrap` namespace
// will be loaded using the `@yii/bootstrap` alias which points to the directory where bootstrap extension
// files are installed and all classes from other `yii` namespaces will be loaded from the yii framework directory.
$classFile = static::getAlias($classFile);
}
} elseif (strpos($className, '\\') !== false) {
// 含 \
// 走到这里的类,也就是自己写的类,非框架的
// eg. yii\console\Controller
// @yii/console/Controller.php
// /var/www/service/vendor/yiisoft/yii2/console/Controller.php
// PSR4 规则
$classFile = static::getAlias('@' . str_replace('\\', '/', $className) . '.php', false);
if ($classFile === false || !is_file($classFile)) {
// 处理不了,交给 composer 尝试处理
return;
}
} else {
// 没有命名空间?什么时候会触发呢?
return;
}

// 核心
include $classFile;

if (YII_DEBUG && !class_exists($className, false) && !interface_exists($className, false) && !trait_exists($className, false)) {
// if the class does not exist in the class file
throw new UnknownClassException("Unable to find '$className' in file: $classFile. Namespace missing?");
}
}

更详细的 yii\di\Container 将在后文再分析,此处不展开。

yii\web\Application

再返回 index.php,第 5 步:

(new yii\web\Application($config))->run();

yii\web\Application 类的层级结构:

yii\base\Configurable
|--- yii\base\BaseObject
|--- yii\base\Component
|--- yii\di\ServiceLocator
|--- yii\base\Module
|--- yii\base\Application
|--- yii\web\Application
/**
* Runs the application.
* This is the main entrance of an application.
* @return int the exit status (0 means normal, non-zero values mean abnormal)
*/
public function run()
{
try {
$this->state = self::STATE_BEFORE_REQUEST;
$this->trigger(self::EVENT_BEFORE_REQUEST);

$this->state = self::STATE_HANDLING_REQUEST;
$response = $this->handleRequest($this->getRequest());

$this->state = self::STATE_AFTER_REQUEST;
$this->trigger(self::EVENT_AFTER_REQUEST);

$this->state = self::STATE_SENDING_RESPONSE;
$response->send();

$this->state = self::STATE_END;

return $response->exitStatus;
} catch (ExitException $e) {
$this->end($e->statusCode, isset($response) ? $response : null);
return $e->statusCode;
}
}
Application statevaluedesc
STATE_BEGIN0just started 在 base\Application __construct
STATE_INIT1initializing 在 base\Application init
STATE_BEFORE_REQUEST2triggering EVENT_BEFORE_REQUEST 在 base\Application run
STATE_HANDLING_REQUEST3handling 在 base\Application run
STATE_AFTER_REQUEST4triggering EVENT_AFTER_REQUEST 在 base\Application run
STATE_SENDING_RESPONSE5send response 在 base\Application run
STATE_END6has ended 在 base\Application run

这里就涉及很多核心概念了,下节见。

– EOF –