看看官网加粗的一句话:
At its core, Slim is a dispatcher that receives an HTTP request, invokes an appropriate callback routine, and returns an HTTP response. That’s it.
那么它是如何分发接收到的Request的呢,这几天就来研究这个事情。
先看看为了让请求进入到index.php 需要对Apache做什么。配置如下,其实也是通常配置而已:
# DirectoryIndex: sets the file that Apache will serve if a directory
# is requested.
#
<IfModule dir_module>
#DirectoryIndex index.html
# XAMPP
DirectoryIndex index.html index.html.var index.php index.php3 index.php4
</IfModule>
保证了访问目录是直接到index.xxx中来。
还有在项目下有个.htaccesss,这个文件的作用是判断请求的URL满足条件否,即是否满足RewriteCond的匹配。最后如果条件满足则执行RewriteRule 的内容,这里让它跳转到index.php。
$app->get(‘/forbase‘, function ($request, $response, $args){ Example\Module\Base::instance()->init($request,$response); return $response; })->add(Example\MiddleWare\MyMiddleware::instance(Example\Module\Base::instance()));
这篇先解决:get是怎么加进去的。在随后的文章里依次解决:route是怎么被调用的;route Middleware是如何加进去的。
在App.php里有变量container。这个变量的类型是Slim\Container,而Slim\Container 继承 PimpleContainer ,据官网说这个Pimple是一个DI Container,这个还不了解。
public function __construct($container = []) { if (is_array($container)) { $container = new Container($container); } if (!$container instanceof ContainerInterface) { throw new InvalidArgumentException(‘Expected a ContainerInterface‘); } $this->container = $container; }
class Container extends PimpleContainer implements ContainerInterface { /** * Create new container * * @param array $values The parameters or objects. */ public function __construct(array $values = []) { parent::__construct($values); $userSettings = isset($values[‘settings‘]) ? $values[‘settings‘] : []; $this->registerDefaultServices($userSettings); }
在Container构造方法中会注册相关的DefaultServices包括router,以后所有的route都存在于router中:
if (!isset($this[‘router‘])) { /** * This service MUST return a SHARED instance * of \Slim\Interfaces\RouterInterface. * * @return RouterInterface */ $this[‘router‘] = function () { return new Router; }; }
这个$this[‘router‘]其实是调用了Pimple\Container中的 offsetSet()方法,因为Pimple\Container实现了ArrayAccess,因此$this[‘router‘]是可用的。
public function offsetSet($id, $value) { if (isset($this->frozen[$id])) { throw new \RuntimeException(sprintf(‘Cannot override frozen service "%s".‘, $id)); } $this->values[$id] = $value; $this->keys[$id] = true; }
这样的所有默认设置都存放在values这个变量里,以后拿设置从values里拿就好了。
在app->get()时,APP类所做的工作:
一get()/post()函数接受route创建请求。二调用app->map();三调用router->map();四设置route的container和output buffer。
上代码:
/** * Add GET route * * @param string $pattern The route URI pattern * @param mixed $callable The route callback routine * * @return \Slim\Interfaces\RouteInterface */ public function get($pattern, $callable) { return $this->map([‘GET‘], $pattern, $callable); }
public function map(array $methods, $pattern, $callable) { if ($callable instanceof Closure) { $callable = $callable->bindTo($this->container); } $route = $this->container->get(‘router‘)->map($methods, $pattern, $callable); if (is_callable([$route, ‘setContainer‘])) { $route->setContainer($this->container); } if (is_callable([$route, ‘setOutputBuffering‘])) { $route->setOutputBuffering($this->container->get(‘settings‘)[‘outputBuffering‘]); } return $route; }
同时router类所做的工作:
当有get、post之类的需要加入时,会在router中的map()方法中生成新的route并且给每个route进行了编号,将这个route加入到router的routes数组中。
上代码:
/** * Add route * * @param string[] $methods Array of HTTP methods * @param string $pattern The route pattern * @param callable $handler The route callable * * @return RouteInterface * * @throws InvalidArgumentException if the route pattern isn‘t a string */ public function map($methods, $pattern, $handler) { if (!is_string($pattern)) { throw new InvalidArgumentException(‘Route pattern must be a string‘); } // Prepend parent group pattern(s) if ($this->routeGroups) { $pattern = $this->processGroups() . $pattern; } // According to RFC methods are defined in uppercase (See RFC 7231) $methods = array_map("strtoupper", $methods); // Add route $route = new Route($methods, $pattern, $handler, $this->routeGroups, $this->routeCounter); $this->routes[$route->getIdentifier()] = $route; $this->routeCounter++; return $route; }
这样的一个过程之后,某个添加的route就放入到router的routes数组中了。再以后APP接收到请求后,会在routes选择合适的route来处理处理请求。