这篇文章写得比较详细。有兴趣的朋友可以点击 这个链接 深入浅出 Laravel 路由执行原理 查看。
话不多说直接上Laravel底层代码:
vendor\laravel\framework\src\Illuminate\Routing\Route.php
<?php
/**
* Create a new Route instance. 创建一个Route实例。
*
* @param array|string $methods
* @param string $uri
* @param \Closure|array $action
* @return void
*/
public function __construct($methods, $uri, $action)
{
$this->uri = $uri;
$this->methods = (array) $methods;
$this->action = $this->parseAction($action);
if (in_array('GET', $this->methods) && ! in_array('HEAD', $this->methods)) {
$this->methods[] = 'HEAD';
}
if (isset($this->action['prefix'])) {
$this->prefix($this->action['prefix']);
}
}
/**
* Parse the route action into a standard array. 将路线动作解析成数组。
*
* @param callable|array|null $action
* @return array
*
* @throws \UnexpectedValueException
*/
protected function parseAction($action)
{
return RouteAction::parse($this->uri, $action);
}
/**
* Run the route action and return the response. 运行route操作并返回响应。
*
* @return mixed
*/
public function run()
{
$this->container = $this->container ?: new Container;
try {
if ($this->isControllerAction()) {
return $this->runController();
}
return $this->runCallable();
} catch (HttpResponseException $e) {
return $e->getResponse();
}
}
/**
* Checks whether the route's action is a controller.
*
* @return bool
*/
protected function isControllerAction() //确定动作是否是控制器
{
return is_string($this->action['uses']);
}
/**
* Run the route action and return the response.
*
* @return mixed
*/
protected function runCallable() //执行动作并且返回 响应
{
$callable = $this->action['uses'];
return $callable(...array_values($this->resolveMethodDependencies(
$this->parametersWithoutNulls(), new ReflectionFunction($this->action['uses'])
)));
}
/**
* Run the route action and return the response.
*
* @return mixed
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
protected function runController() //执行路由动作并且返回响应
{
return $this->controllerDispatcher()->dispatch( // 将请求分配给指定的控制器和方法。
$this, $this->getController(), $this->getControllerMethod()
);
}
/**
* Get the controller instance for the route.
*
* @return mixed
*/
public function getController() // 获取执行的控制器
{
if (! $this->controller) {
$class = $this->parseControllerCallback()[0];
$this->controller = $this->container->make(ltrim($class, '\\'));
}
return $this->controller;
}
/**
* Get the controller method used for the route.
*
* @return string
*/
protected function getControllerMethod() // 获取控制器执行的方法
{
return $this->parseControllerCallback()[1];
}
/**
* Parse the controller.
*
* @return array
*/
protected function parseControllerCallback() //解析控制器, 分解成控制器类以及操作的方法
{
return Str::parseCallback($this->action['uses']);
}
/**
* Get the dispatcher for the route's controller.
*
* @return \Illuminate\Routing\Contracts\ControllerDispatcher
*/
public function controllerDispatcher() //获取路由控制器的调度程序。
{
if ($this->container->bound(ControllerDispatcherContract::class)) {
return $this->container->make(ControllerDispatcherContract::class);
}
return new ControllerDispatcher($this->container);
}
以上只列出底层的几个方法,想要具体了解实现步骤可以打dd()断点自行体会,要注意打完断点记得清理掉!!!
具体分析:
参考: laravel 源码分析-路由是如何调用到控制器
index.php
// 得到应用核心实例
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
// 对请求处理后获得响应
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture() // 获得请求参数
);
// 发送响应
$response->send();
// 终止
$kernel->terminate($request, $response);
我们想知道路由在哪分配调用就在 $kernel->handle() 这里,其中调用了 $this-sendRequestThroughRouter(),这里就将请求发送给了中间件和路由。
vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php
/**
* Send the given request through the middleware / router.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
protected function sendRequestThroughRouter($request)
{
$this->app->instance('request', $request);
Facade::clearResolvedInstance('request');
$this->bootstrap();
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter());
}
/**
* Get the route dispatcher callback.
*
* @return \Closure
*/
protected function dispatchToRouter()
{
return function ($request) {
$this->app->instance('request', $request);
return $this->router->dispatch($request);
};
}
$this->dispatchToRouter() 中 $this->router->dispatch($request) 就将请求传入到 Router 类中了。
vendor\laravel\framework\src\Illuminate\Routing\Router.php
/**
* Dispatch the request to the application.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*/
public function dispatch(Request $request)
{
$this->currentRequest = $request;
return $this->dispatchToRoute($request);
}
/**
* Dispatch the request to a route and return the response.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*/
public function dispatchToRoute(Request $request)
{
return $this->runRoute($request, $this->findRoute($request));
}
/**
* Find the route matching a given request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Routing\Route
*/
protected function findRoute($request)
{
$this->current = $route = $this->routes->match($request);
$this->container->instance(Route::class, $route);
return $route;
}
然后再 Router 类的 dispatch() 的方法中调用了 Router 类的 dispatchToRoute() 方法将请求向下传递。
vendor\laravel\framework\src\Illuminate\Routing\RouteCollection.php
/**
* Find the first route matching a given request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Routing\Route
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
public function match(Request $request)
{
$routes = $this->get($request->getMethod());
// First, we will see if we can find a matching route for this current request
// method. If we can, great, we can just return it so that it can be called
// by the consumer. Otherwise we will check for routes with another verb.
$route = $this->matchAgainstRoutes($routes, $request);
if (! is_null($route)) {
return $route->bind($request);
}
// If no route was found we will now check if a matching route is specified by
// another HTTP verb. If it is we will need to throw a MethodNotAllowed and
// inform the user agent of which HTTP verb it should use for this route.
$others = $this->checkForAlternateVerbs($request);
if (count($others) > 0) {
return $this->getRouteForMethods($request, $others);
}
throw new NotFoundHttpException;
}
在dispatchToRoute()中你会发现 $route = $this->findRoute($request);的调用。
findRoute() 中调用 RouteCollection 类中的 match() 方法去匹配在初始化框架时读取到内存中的开发者在路由定义文件里定义的路由,如果未匹配到就抛出 NotFoundHttpException 异常。如匹配到就返回了 Route 类的实例。
Router.php
/**
* Run the given route within a Stack "onion" instance.
*
* @param \Illuminate\Routing\Route $route
* @param \Illuminate\Http\Request $request
* @return mixed
*/
protected function runRouteWithinStack(Route $route, Request $request)
{
$shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
$this->container->make('middleware.disable') === true;
$middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route);
return (new Pipeline($this->container))
->send($request)
->through($middleware)
->then(function ($request) use ($route) {
return $this->prepareResponse(
$request, $route->run()
);
});
}
vendor\laravel\framework\src\Illuminate\Routing\Controller.php
/**
* Execute an action on the controller.
*
* @param string $method
* @param array $parameters
* @return \Symfony\Component\HttpFoundation\Response
*/
public function callAction($method, $parameters)
{
return call_user_func_array([$this, $method], $parameters);
}
然后这个实例在 Router 类的 dispatchToRoute() 方法中实例有被传入到 runRouteWithinStack() 方法。这个方法中又调用多个方法将匹配到的开发者定义的 URL 的映射的控制器实例化,去调用控制器的 callAction() 方法,callAction() 写在控制器的基类中,就是我们写一个控制器都要继承的那个 Conreoller 类中。方法很简单,就是讲传入的方法名和参数用 PHP 函数 call_user_func_array() 进行调用。
拆分路由
laravel 的路由主要是放在 项目根目录下的routes文件夹,laravel 已经为我们分配好了api.php 和 web.php 两个路由文件,这显然在我们开发过程中是不够用的。所以我们需要拆分出单独的路由文件,以便我们管理维护路由。
以下都是以拆分web.php为例,API路由同理。
方法一:
在 web.php 同目录下新建 web/user.php
2. 在 web.php 里面 require_once (‘web/user.php’); 引入该文件
3.然后就可以直接在user.php 写入路由了
方法二:
打开文件: app\Providers\RouteServiceProvider.php
2.修改:boot()方法
public function boot()
{
//匹配通用路由地址
$pattern = __DIR__ . '/../Http/Routes/*.php';
foreach (glob($pattern) as $route) {
require_once $route;
}
parent::boot();
}
然后新建文件夹 app\Http\Routes
在 app\Http\Routes下建立路由文件 user.php
这样也可以实现路由。
访问: 你的域名/user/login