### App Construct 先来看看在 __construct 中做了什么,基本任何框架都会在这里做一些基本的操作,也就是从这里开始延伸出去。 ```php public function __construct(string $rootPath = '') { $this->thinkPath = dirname(__DIR__) . DIRECTORY_SEPARATOR; $this->rootPath = $rootPath ? rtrim($rootPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : $this->getDefaultRootPath(); $this->appPath = $this->rootPath . 'app' . DIRECTORY_SEPARATOR; $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR; if (is_file($this->appPath . 'provider.php')) { $this->bind(include $this->appPath . 'provider.php'); } static::setInstance($this); $this->instance('app', $this); $this->instance('think\Container', $this); } ``` - 从魔术的方法的参数`rootPath`来看,是支持自定义根目录路径的。 - 设置了 `thinkPath`, `rootPath`, `appPath`, `runtimePath` - 绑定了默认的服务提供者,一共提供了两个,`app\Reques` 和 `app\ExceptionHandle`,实际上你使用的 `Request` 就是它。具体到 `appPath` 查看 - 设置当前容器实例 `APP` - 将 `App($this)` 实例 绑定到容器中,分别是 `app` 和 `think\Container` 这里需要注意的是 App 类是继承 Container 的,所以就是将自身实例绑定到容器中。 在这里似乎整个应用就已经初始化结束了?这里我需要把一部分 `Request run` 的内容放在这里说,因为那里才是框架主要的初始化工作,我并不认为将这一部分初始化工作放在 `Request run` 中是合理的。 ## 主要的初始化 ``` public function initialize() { $this->initialized = true; $this->beginTime = microtime(true); $this->beginMem = memory_get_usage(); // 加载环境变量 if (is_file($this->rootPath . '.env')) { $this->env->load($this->rootPath . '.env'); } $this->configExt = $this->env->get('config_ext', '.php'); $this->debugModeInit(); // 加载全局初始化文件 $this->load(); // 加载框架默认语言包 $langSet = $this->lang->defaultLangSet(); $this->lang->load($this->thinkPath . 'lang' . DIRECTORY_SEPARATOR . $langSet . '.php'); // 加载应用默认语言包 $this->loadLangPack($langSet); // 监听AppInit $this->event->trigger('AppInit'); date_default_timezone_set($this->config->get('app.default_timezone', 'Asia/Shanghai')); // 初始化 foreach ($this->initializers as $initializer) { $this->make($initializer)->init($this); } return $this; } ``` - 加载 .env 环境变量文件 - 加载配置文件以及应用内的文件 - 加载应用内的 common.php - 加载助手函数 在 `thinkPath` 目录下的 `helper.php` - 加载配置文件 - 加载应用目录下的 event.php 事件 - 注册应用目录下的 service.php 服务 - 加载语言包 - 监听 AppInit 事件,利用该事件可以做一些请求前的工作 - 设置时区 - 注入所有服务并且启动服务 ## 服务注册 初始化过程中,进行服务注册,那么服务注册做了哪些事情呢?该如何使用的服务呢? ``` public function register($service, bool $force = false) { $registered = $this->getService($service); if ($registered && !$force) { return $registered; } if (is_string($service)) { $service = new $service($this); } if (method_exists($service, 'register')) { $service->register(); } if (property_exists($service, 'bind')) { $this->bind($service->bind); } $this->services[] = $service; } ``` - 服务是否注册过,如果需要强制重新注册 - 实例化服务 - 如果实现了 `register` 方法,则需要执行 `register` 方法 - 如果设置了 `bind` 属性,则需要将 `service `实例绑定到容器 - 最后合并到整个 `service` 数组中,等待 `boot` ## 服务启动 目前在初始化的时候只有下面三个服务,在 `$this->initializers` 数组中 ``` foreach ($this->initializers as $initializer) { $this->make($initializer)->init($this); } ``` 这三个服务分别是: ``` think\initializer\BootService think\initializer\Error think\initializer\RegisterService ``` - `Error` 服务是用来处理框架异常和错误的 - `RegisterService` 从字面的意思就是注册服务的 - `BootService ` 就是启用服务的 `Error` 处理在之后再说,这里说一下 `RegisterService` 和 `BootService`。 当从 `Container` 中 `make` 出 `RegisterService` 的时候 > 这里有个隐藏的**静态方法** __make,每次如果首次从 Container 中 make 出来的实例对象都会执行 __make 方法,当然首先必须你实现了该方法。 随后会执行 Init 方法。当你进入到 `RegisterService` 的时候,你会看到该方法。方法内容如下: ``` public function init(App $app) { $file = $app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . 'services.php'; $services = $this->services; if (is_file($file)) { $services = array_merge($services, include $file); } foreach ($services as $service) { if (class_exists($service)) { $app->register($service); } } } ``` 该方法就很奇怪了,和我想象的有点不一样。服务是直接从 `runtime` 目录下面获取的,而非在 `config` 目录下的 `service.php` 中。为什么会这样呢?由于 composer 的发展,TP 框架也可以提供包的自动发现的功能,这也证明了开发组在不断向社区靠拢。下面来看一下是如何实现的。 因为这都是得益于 composer 的,所以来看一下 `rootPath` 下的 composer.json,到最下面,你会发现下面的配置 ``` "scripts": { "post-autoload-dump": [ "@php think service:discover", "@php think vendor:publish" ] } ``` 从配置来看,框架一共提供了两个指令,`service:discover `和 `vendor:publish`。具体实现这里就不说了,你只需要知道包的发现是由 `service:discover` 实现的。 还有就是这里默认注入了三个服务。 ``` PaginatorService::class, ValidateService::class, ModelService::class, ``` 最后再来看看 `BootService`,这个就很简单了。从命名来讲就不难看出,下面就是代码,正常的启动服务,但是这里要说明的是,服务类中必须实现了 `boot` 方法才会启动。 ``` public function init(App $app) { $app->boot(); } ``` 以上就是整个应用的初始化,具体细节在之后讨论