How does Laravel "keep" the routes?

1

In Laravel , in both 4 and 5, we can use a class called Route to create certain routes.

My curiosity is: how do Laravel (or other frameworks) do to "know" that a given route corresponds to a url?

Is this all saved in some array and then, when the request is made, is the route called?

Where is the list of these routes?

This is important to know, especially for those who want to learn how to make a framework.

    
asked by anonymous 15.04.2016 / 20:46

3 answers

1

I do not understand the question, but I will assume that they use $_SERVER['PATH_INFO'] or $_SERVER['REQUEST_URI'] , in Laravel / Symfony .htaccess you should not use PATH_INFO because it looks like this:

RewriteRule ^ index.php [L]

Path_info is only generated in php if you do something similar to this:

RewriteRule (*.?) index.php/$1 [L]

In the PATH_INFO case it does not work very well on some different Apache servers, sometimes the result is different, but REQUEST_URI works identically on Apache, Ngnix, Lighttpd and IISExpress (I could not test on IIS standard but I believe be the same thing.)

The PHP structure should be something like (this is the structure I used in a personal framework, however this simplified, without POST, GET, PUT, etc.):

class Route
{
    private static $currentPath;
    private static $routes = array();

    public function path()
    {
        if (self::$currentPath) {
            return $currentPath;
        }

        //Pega o nome do script atual
        $sname  = $_SERVER['SCRIPT_NAME'];

        //Remove a query string da url
        $reqUri = empty($_SERVER['REQUEST_URI']) ? null : preg_replace('#\?(.*)$#', '', $_SERVER['REQUEST_URI']);

        //Subtrai o nome do script (se necessário)
        $pathInfo = substr(urldecode($reqUri), strlen(substr($sname, 0, -9)));
        $pathInfo = '/' . ($pathInfo === false ? '' : $pathInfo);

        return self::$currentPath = $pathInfo;
    }

    public function add($path, $controller)
    {
        self::$routes[$path] = $controller;
    }

    public function exec()
    {
         if (empty(self::$routes[$path])) {
              return false;
         }

         return self::$routes[$path];
    }
}

There are many ways to "save" such routes, in case of my class I keep in array in the same class:

private static $routes = array();

But you can even save in separate places, it will depend on the end use goal.

The usage would look something like:

require 'route.php';

Route::add('/', 'ClasseController@indexaction');
Route::add('/blog', 'ClasseController@blogaction');
Route::add('/admin', 'AdminClasse@action');

echo 'Path: ', Route::path(), '<br>', PHP_EOL; //Retorna o PATH equivalente ao PATH_INFO
echo 'Controller: ', Route::exec(), '<br>', PHP_EOL; //Retorna o controler

I can not say that it works exactly this way in Laravel / Symfony, but this explanation is to understand how to use $_SERVER['REQUEST_URI'] or PATH_INFO .

Laravel and the Routes cache

In Laravel there is a cache structure for the routes (which you mentioned to me) that after the command (does not work with anonymous functions):

php artisan route:cache

A file is generated in projeto/bootstrap/cache/routes.php , it contains a php variable "serialized" (and encoded in base64) and uses the setRoutes method to define all routes that are in cache for "Collection", an example of cache :

app('router')->setRoutes(
    unserialize(base64_decode('Código base64'))
);

This can be reasonably advantageous for production servers.

    
15.04.2016 / 21:38
0

Well, let's explain in parts:

Laravel, when we use the call of class Route , you are actually calling a facade of class Illuminate\Routing\Router .

A Router is used to facilitate the creation of routes ( Illuminate\Routing\Route ), which in turn will be created within a collection of predefined routes ( Illuminate\Routing\RouteCollection ).

When you invoke Route::get('/', 'HomeController@getIndex') for example, you are actually doing the following:

 app('route')->get('/', 'HomeController@getIndex');

What is the same thing as:

app('route')->addRoute(['GET', 'HEAD'], '/', 'HomeController@getIndex');

So we have to understand that:

1 - Route is a facade for class Router .

2 - Router is a class responsible for creating routes within a collection.

3 - RouteCollection is the list of routes. Each time it is called Route::get or Route::post , this route is inserted into that class.

4 - Route (not the facade, but Illuminate\Routing\Route ) is the route itself. It is the class that represents an entity that is a route.

Internally, Laravel has a method in RouteCollection that takes the first route that hits the regular expression of URI of the route.

When none is found, it returns an exception (which is error 404).

See the RouteCollection::match method, which does this service.

/**
 * 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->check($routes, $request);

    if ( ! is_null($route))
    {
        return $route->bind($request);
    }

    // If no route was found, we will check if a matching is route is specified on
    // 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->getOtherMethodsRoute($request, $others);
    }

    throw new NotFoundHttpException;
}

So we can conclude, as we asked, that routes are saved in a collection, which is responsible for returning a route registered according to the content of the request (the http and uri method).

    
15.04.2016 / 21:30
-1

Laravel uses the autoloader to identify routes. You can also opt for pre-defined routes. For those cases where there is no pre-definition of routes and Laravel identifies them automatically, you are most likely applying the PSR4 , which can be set in the composer

 "autoload": {
         "psr-4": {
             "App\": "app/",
              "Mylib\": "src/"
        }
    },

illustrative example

link

Whenever a route is invoked and found, it is cached.

I'm not saying that Laravel uses only the PSR4. This is optional in the settings.

This route cache is something that can be good or bad. Because you will always load a file containing all the routes for each request or each time it fires, for example. It's loading some kb there that can be heavier than checking the route at runtime. In a small project of 10, 20 routes, it may be advantage, but above 100, I believe not.

To optimize, there is the route group where the route files can be divided, leaving them in smaller sizes. Therefore, those who use many routes should use this feature.

    
15.04.2016 / 21:04