项目目录相对为tp6
请求被统一转发到入口文件(入口文件地址为 tp6/index.php)
1.入口文件引入了composer自动载入文件类库
<php? namespace think; require __DIR__ . '/../vendor/autoload.php';
(文件地址为 tp6/vendor/autoload.php')
2.实例化 thinkApp 对象 赋值给$app
<?php $app = new App();
(App类文件地址为 tp6/vendor/topthink/framework/src/think/App.php')
执行App类中的__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; // 加载服务provide容器 if (is_file($this->appPath . 'provider.php')) { // 执行 Containerind()方法 绑定类、闭包、实例、接口 $this->bind(include $this->appPath . 'provider.php'); } // 设置一个容器实例 static::setInstance($this); // 绑定类的实例到容器 $this->instance('app', $this); $this->instance('thinkContainer', $this); }
其中在属性$bind中已经有了一些框架类的别名与实现的映射数组
此时$app因为是继承了Container类,所以$app实际也是一个容器
3.通过$app类调用http类(web管理类)
$http = $app->http;
3.0 引用http类的过程如下:
(http类文件地址为 tp6/vendor/topthink/framework/src/think/Http.php')
3.1 首先App中不存在http方法,但是在容器类中存在魔术方法__get,会触发该魔术方法
<?php public function __get($name) { return $this->get($name); }
get方法从app应用容器中获取http对象实例
<php? /** * 获取容器中的对象实例 * @access public * @param string $abstract 类名或者标识 * @return object */ public function get($abstract) { if ($this->has($abstract)) { return $this->make($abstract); } throw new ClassNotFoundException('class not exists: ' . $abstract, $abstract); }
3.2 通过app应用容器中的make方法进行类的实例,以及执行instances方法将实例化的http类绑定到容器数组对象中
<?php /** * 创建类的实例 已经存在则直接获取 * @access public * @param string $abstract 类名或者标识 * @param array $vars 变量 * @param bool $newInstance 是否每次创建新的实例 * @return mixed */ public function make(string $abstract, array $vars = [], bool $newInstance = false) { $abstract = $this->getAlias($abstract); if (isset($this->instances[$abstract]) && !$newInstance) { return $this->instances[$abstract]; } if (isset($this->bind[$abstract]) && $this->bind[$abstract] instanceof Closure) { $object = $this->invokeFunction($this->bind[$abstract], $vars); } else { $object = $this->invokeClass($abstract, $vars); } if (!$newInstance) { $this->instances[$abstract] = $object; } return $object; }
返回http对象实例到调用处 第3步开始的地方
4 http对象执行run方法,应用程序的执行开始
<?php /** * 执行应用程序 * @access public * @param Request|null $request * @return Response */ public function run(Request $request = null): Response { //自动创建request对象 $request = $request ?? $this->app->make('request', [], true); // 绑定request类的实例化对象到容器中 $this->app->instance('request', $request); try { // 执行应用程序返回response类 $response = $this->runWithRequest($request); } catch (Throwable $e) { $this->reportException($e); $response = $this->renderException($request, $e); } return $response; }
4.1 run 方法中首先创建Request类,执行instances方法将实例化的Request类绑定到容器数组对象中,标识为request
然后进行路由调度,执行 app容器中runWithRequest方法
/** * 执行应用程序 * @param Request $request * @return mixed */ protected function runWithRequest(Request $request) { // 初始化app应用程序 $this->initialize(); // 加载全局中间件 $this->loadMiddleware(); // 监听HttpRun $this->app->event->trigger(HttpRun::class); // 中间件调度 return $this->app->middleware->pipeline() ->send($request) ->then(function ($request) { return $this->dispatchToRoute($request); }); }
4.2然后在在http类中runWithRequest方法中执行了dispatchToRoute 讲请求分发到路由
(dispatchToRoute 类方法的文件地址为 tp6/vendor/topthink/framework/src/think/Route.php')
// 分发请求到路由
<?php protected function dispatchToRoute($request) { // 是否启用路由, 默认启用路由 $withRoute = $this->app->config->get('app.with_route', true) ? function () { $this->loadRoutes(); } : null; // 执行路由调度 return $this->app->route->dispatch($request, $withRoute); }
4.3 在http类中dispatchToRoute 通过app容器获取了route实例,并调用了route实例中的dispatch方法
<?php
/**
* 路由调度
* @param Request $request
* @param Closure|bool $withRoute
* @return Response
*/
public function dispatch(Request $request, $withRoute = true)
{
$this->request = $request;
$this->host = $this->request->host(true);
$this->init();
if ($withRoute) {
// 加载路由设置
if ($withRoute instanceof Closure) {
$withRoute();
}
// 检查路由
$dispatch = $this->check();
} else {
// 如果没有路由,则使用默认url解析
$dispatch = $this->url($this->path());
}
// $dispatch 为think
outedispatchController 的实例化 中的初始化,提取控制名称以及操作名称
// 1, 通过最终think
outeRule::dispatch来确路由调度的最终执行动作
$dispatch->init($this->app);
return $this->app->middleware->pipeline('route')
->send($request)
->then(function () use ($dispatch) {
// 执行动作方法
return $dispatch->run();
});
}
4.4 在route类中的dispatch中执行了run方法,
(run 类方法的文件地址为 tp6/vendor/topthink/framework/src/think/route/Dispatch.php')
<?php
/** * 执行路由调度 * @access public * @return mixed */ public function run(): Response { if ($this->rule instanceof RuleItem && $this->request->method() == 'OPTIONS' && $this->rule->isAutoOptions()) { $rules = $this->rule->getRouter()->getRule($this->rule->getRule()); $allow = []; foreach ($rules as $item) { $allow[] = strtoupper($item->getMethod()); } return Response::create('', 'html', 204)->header(['Allow' => implode(', ', $allow)]); } // 此处调用的$this类,由调用者确定, 可能为url, callback, 或者controller // $data 为返回的response 类 $data = $this->exec(); return $this->autoResponse($data); }
4.5 run方法调用了exec
(此处调用的exec的类方法所在的文件,由调用者确定, 可能为url, callback, 或者controller, exec 类方法的文件地址为 tp6/vendor/topthink/framework/src/think/route/dispatch/Callback.php|Controller.php')
在exec方法中最终返回了data数据
4.6 然后调用了autoResponse方法,并传递4.5返回的data数据
(autoResponse 类方法的文件地址为 tp6/vendor/topthink/framework/src/think/route/Dispatch.php')
<?php protected function autoResponse($data): Response { if ($data instanceof Response) { $response = $data; } elseif (!is_null($data)) { // 默认自动识别响应输出类型 $type = $this->request->isJson() ? 'json' : 'html'; $response = Response::create($data, $type); } else { $data = ob_get_clean(); $content = false === $data ? '' : $data; $status = '' === $content && $this->request->isJson() ? 204 : 200; // 创建response类返回,使用html $response = Response::create($content, 'html', $status); } return $response; }
4.7 autoResponse 方法中执行了 Response::create方法
最终方法返回了response对象;
(Response::create 类方法的文件地址为 tp6/vendor/topthink/framework/src/think/Response.php')
<?php /** * 创建Response对象 * @access public * @param mixed $data 输出数据 * @param string $type 输出类型 * @param int $code 状态码 * @return Response */ public static function create($data = '', string $type = 'html', int $code = 200): Response { $class = false !== strpos($type, '\') ? $type : '\think\response\' . ucfirst(strtolower($type)); return Container::getInstance()->invokeClass($class, [$data, $code]); }
4.8 最终返回了 response Html类的对象实例
5 执行run方法
$response = $http->run();
6 response 执行send输出数据的操作;
(Html 类文件所在地址为 tp6/vendor/topthink/framework/src/think/response/Html.php )
6.1 执行send方法
(send方法 类文件所在地址为 tp6/vendor/topthink/framework/src/think/Response.php )
$response->send();
<?php /** * 发送数据到客户端 * @access public * @return void * @throws InvalidArgumentException */ public function send(): void { // 处理输出数据 $data = $this->getContent(); if (!headers_sent() && !empty($this->header)) { // 发送状态码 http_response_code($this->code); // 发送头部信息 foreach ($this->header as $name => $val) { header($name . (!is_null($val) ? ':' . $val : '')); } } if ($this->cookie) { $this->cookie->save(); } $this->sendData($data); if (function_exists('fastcgi_finish_request')) { // 提高页面响应 fastcgi_finish_request(); } }
6.1 在send方法中最终执行了 sendData 方法
(sendData方法 类文件所在地址为 tp6/vendor/topthink/framework/src/think/Response.php )
<?php /** * 输出数据 * @access protected * @param string $data 要处理的数据 * @return void */ protected function sendData(string $data): void { echo $data; }
7 执行 http对象中的end方法
<?php $http->end($response);
(http类文件地址为 tp6/vendor/topthink/framework/src/think/Http.php')
<?php /** * HttpEnd * @param Response $response * @return void */ public function end(Response $response): void { $this->app->event->trigger(HttpEnd::class, $response); //执行中间件 $this->app->middleware->end($response); // 写入日志 $this->app->log->save(); }
8整个程序结束
应用类App继承了Container容器类, 所有类的实例通过容器类进行统一管理,容器类为单例模式全局唯一;