1、首先我们先来看看slim的run方法
public function run($silent = false)
{
$response = $this->container->get('response');
try {
ob_start();
$response = $this->process($this->container->get('request'), $response);
} catch (InvalidMethodException $e) {
$response = $this->processInvalidMethod($e->getRequest(), $response);
} finally {
$output = ob_get_clean();
}
// ....
// ....
}
其中$this->container->get('request')回去回去一个注册的request对象
// 在框架初始化时注册
if (!isset($container['request'])) {
$container['request'] = function ($container) {
return Request::createFromEnvironment($container->get('environment'))
};
}
public function get($id)
{
// ...
// ...
try {
return $this->offsetGet($id);
} catch (\InvalidArgumentException $exception) {
// ...
// ...
// ...
}
}
public function offsetGet($id)
{
if (!isset($this->keys[$id])) {
throw new UnknownIdentifierException($id);
}
if (
isset($this->raw[$id])
|| !\is_object($this->values[$id])
|| isset($this->protected[$this->values[$id]])
|| !\method_exists($this->values[$id], '__invoke')
) {
return $this->values[$id];
}
if (isset($this->factories[$this->values[$id]])) {
return $this->values[$id]($this);
}
$raw = $this->values[$id];
$val = $this->values[$id] = $raw($this);
$this->raw[$id] = $raw;
$this->frozen[$id] = true;
return $val;
}
框架初始化时会判断request有没有被设置,没有则会将一个方法赋值给container的request对象,而container的get方法最终会通过offsetGet()方法去获取,这里提一个container实现了ArrayAccess 接口,这个实现的好处就是可以像数组一样去访问对象。
我们重点来看一下offsetGet()方法,上面的几个if我们放到最后看,先看下面的赋值。$this->values['request']就是我们最开始那个注册的方法,我们将它赋值给$raw, 然后在执行这个方法将返回值赋值给 $val与$this->values['request'],在将$raw放到$this->raw['request'],然后将$this->frozen['request']设置为true,说明这个id已经初始化完成了。
这时候我们再反过来查看上面的if方法,
- isset($this->raw[$id])判断$this->raw中是否设置过该id
- !\is_object($this->values[$id])判断是否是个对象,
- !\method_exists($this->values[$id], '__invoke')判断对象中是否存在__invoke方法,即不能直接调用
- isset($this->protected[$this->values[$id]]) 是否需要将方法当做参数,不执行直接返回
- isset($this->factories[$this->values[$id]])是否是一个可直接调用的callable
即如果$this->values[$id]有如下情况,就会直接返回
- 是一个对象
- 之前调用过
- 设置了保护,需要直接返回callable
- 不可调用的对象实例
- 设置了factories
经过上面的步骤,我们得到了request对象,接下来进入process()方法
public function process(ServerRequestInterface $request, ResponseInterface $response)
{
// ...
// ...
try {
$response = $this->callMiddlewareStack($request, $response);
} catch (Exception $e) {
$response = $this->handleException($e, $request, $response);
} catch (Throwable $e) {
$response = $this->handlePhpError($e, $request, $response);
}
return $response;
}
public function callMiddlewareStack(ServerRequestInterface $request, ResponseInterface $response)
{
if (is_null($this->tip)) {
$this->seedMiddlewareStack();
}
/** @var callable $start */
$start = $this->tip;
$this->middlewareLock = true;
$response = $start($request, $response);
$this->middlewareLock = false;
return $response;
}
protected function seedMiddlewareStack(callable $kernel = null)
{
if (!is_null($this->tip)) {
throw new RuntimeException('MiddlewareStack can only be seeded once.');
}
if ($kernel === null) {
$kernel = $this;
}
$this->tip = $kernel;
}
这里我们就主要看看 $this->callMiddlewareStack($request, $response)这个方法就ok。开始时现将$this即app对象赋值给$this->tip,然后将$this->tip 赋值给$start, 接着以调用函数的方法调用对象,运行app的__invoke方法。
public function __invoke(ServerRequestInterface $request, ResponseInterface $response)
{
// Get the route info
$routeInfo = $request->getAttribute('routeInfo');
/** @var \Slim\Interfaces\RouterInterface $router */
$router = $this->container->get('router');
// If router hasn't been dispatched or the URI changed then dispatch
if (null === $routeInfo || ($routeInfo['request'] !== [$request->getMethod(), (string) $request->getUri()])) {
$request = $this->dispatchRouterAndPrepareRoute($request, $router);
$routeInfo = $request->getAttribute('routeInfo');
}
// 找到匹配的路由
if ($routeInfo[0] === Dispatcher::FOUND) {
$route = $router->lookupRoute($routeInfo[1]);
return $route->run($request, $response);
} elseif ($routeInfo[0] === Dispatcher::METHOD_NOT_ALLOWED) {
// 方法不允许访问
if (!$this->container->has('notAllowedHandler')) {
throw new MethodNotAllowedException($request, $response, $routeInfo[1]);
}
/** @var callable $notAllowedHandler */
$notAllowedHandler = $this->container->get('notAllowedHandler');
return $notAllowedHandler($request, $response, $routeInfo[1]);
}
// 没有找到对应的处理
if (!$this->container->has('notFoundHandler')) {
throw new NotFoundException($request, $response);
}
/** @var callable $notFoundHandler */
$notFoundHandler = $this->container->get('notFoundHandler');
return $notFoundHandler($request, $response);
}
在__invoke中,分为三种情况
- 找到匹配的路由,执行对应方法
- 方法不允许访问,判断有没有注册notAllowedHandler,注册就用该handle处理,没有则报异常
- 没有找到对应路由,判断有没有注册notFoundHandler,注册就用该handle处理,没有则报异常
网友评论