Laravel source code analysis of the use of routing

Time:2021-3-2

Laravel source code analysis of the use of routing

preface

My analysis article is not a deep-seated multi domain analysis strategy. But reading such articles with reference to the development documents will help you to a higher level in your daily development.

No more nonsense. Let’s start this chapter.

entrance

After laravel starts, it will first load the service provider, middleware and other components. Before finding the route, because we are using the facade, we need to find the route entity class first.

register

The first step, of course, is through the service provider, because this is the key to starting laravel RouteServiceProvider Load the routing file in.

protected function mapApiRoutes()
{
    Route::prefix('api')
         ->middleware('api')
         ->Namespace ($this - > namespace) // set the namespace
         ->group(base_ path('routes/ api.php '); // the absolute path of the obtained routing file
}

First of all, require is indispensable. Because there is no namespace in the routing file. Illuminate\Routing\RouterNext method

protected function loadRoutes($routes)
{
    if ($routes instanceof Closure) {
        $routes($this);
    } else {
        $router = $this;

        require $routes;
    }
}

Then find the specified method through the route, which is still the same Illuminate\Routing\Router There are all the routing related methods you use, such as get, post, put, patch and so on. They all call the unified method addRoute

public function addRoute($methods, $uri, $action)
{
    return $this->routes->add($this->createRoute($methods, $uri, $action));
}

After that, it was passed Illuminate\Routing\RouteCollection The addtocollections method is added to the collection

protected function addToCollections($route)
{
    $domainAndUri = $route->getDomain().$route->uri();

    foreach ($route->methods() as $method) {
        $this->routes[$method][$domainAndUri] = $route;
    }

    $this->allRoutes[$method.$domainAndUri] = $route;
}

The result after adding is shown in the figure below
Laravel source code analysis of the use of routing

call

adopt Illuminate\Routing\Router Method to start running the logic of route instantiation

protected function runRoute(Request $request, Route $route)
{
    $request->setRouteResolver(function () use ($route) {
        
        return $route;
    });
    $this->events->dispatch(new Events\RouteMatched($route, $request));

    return $this->prepareResponse($request,
        $this->runRouteWithinStack($route, $request)
    );
}
....
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() // call the run method here
                        );
                    });
}

stay Illuminate\Routing\Route The method of executing controller in run side

public function run()
{
    $this->container = $this->container ?: new Container;

    try {
        if ($this->isControllerAction()) { 
            Return $this - > runcontroller(); // run a route and respond
        }
            
        return $this->runCallable();
    } catch (HttpResponseException $e) {
        return $e->getResponse();
    }
}

It can be seen from the above method that runcontroller is the key to run routing. A scheduler is run in the method to run the controller $this->getController() And controller method $this->getControllerMethod() Incoming to dispatch In scheduling method

protected function runController()
{
    
    return $this->controllerDispatcher()->dispatch(
        $this, $this->getController(), $this->getControllerMethod()
    );
}

Notice heregetController()Is the real way to instantiate the controller

public function getController()
{
    
    if (! $this->controller) {
        $class = $this - > parsecontrollercallback() [0]; // 0 = > controller xxcontroller 1 = > method name index
        $this - > controller = $this - > container - > make (ltrim ($class, '\ \'); // give it to the container for reflection
    }

    return $this->controller;
}

instantiation

The controller specified by the route is still loaded by reflection. At this time, the parameter $concrete of build is used= App\Api\Controllers\XxxController

public function build($concrete)
{
    // If the concrete type is actually a Closure, we will just execute it and
    // hand back the results of the functions, which allows functions to be
    // used as resolvers for more fine-tuned resolution of these objects.
    if ($concrete instanceof Closure) {
        return $concrete($this, $this->getLastParameterOverride());
    }
    
    $reflector = new ReflectionClass($concrete);
    // If the type is not instantiable, the developer is attempting to resolve
    // an abstract type such as an Interface of Abstract Class and there is
    // no binding registered for the abstractions so we need to bail out.
    if (! $reflector->isInstantiable()) {
        return $this->notInstantiable($concrete);
    }
    
        
    $this->buildStack[] = $concrete;

    $constructor = $reflector->getConstructor();
    // If there are no constructors, that means there are no dependencies then
    // we can just resolve the instances of the objects right away, without
    // resolving any other types or dependencies out of these containers.
    if (is_null($constructor)) {
    
            array_pop($this->buildStack);
    
            return new $concrete;
    }

    $dependencies = $constructor->getParameters();
    // Once we have all the constructor's parameters we can create each of the
    // dependency instances and then use the reflection instances to make a
    // new instance of this class, injecting the created dependencies in.
    $instances = $this->resolveDependencies(
        $dependencies
    );

    array_pop($this->buildStack);
    
    return $reflector->newInstanceArgs($instances);
}

At this time, the instance of the controller will be returned. Next, the specified method will be accessed through the URL. Generally, the controller will inherit the parent class Illuminate\Routing\Controller , laravel sets the alias basecontroller for it

public function dispatch(Route $route, $controller, $method)
{
    
    $parameters = $this->resolveClassMethodDependencies(
        $route->parametersWithoutNulls(), $controller, $method
    );

    if (method_exists($controller, 'callAction')) {

            return $controller->callAction($method, $parameters);
    }
        
    return $controller->{$method}(...array_values($parameters));
}

Laravel calls the specified method of the subclass through the callaction inherited from the controller, which is the custom method we want to call.

public function callAction($method, $parameters)
{
    return call_user_func_array([$this, $method], $parameters);
}

thank

Thank you for reading here, source code analysis of this article depends on personal understanding. If there is any discrepancy, please make bricks.

I hope this article can help you. thank you