Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Use FastRoute by default #133

Conversation

weierophinney
Copy link
Member

Hi,

I don't know where this discussion (#131) will bring us, but if we keep one default router, I think it should be FastRoute.

I've tried FastRoute and Aura, and FastRoute is definitely easier to use. See the comparaison:

Aura:

return [
    'routes' => [
        [
            'path' => '/users/:id',
            'options' => [
                'tokens' => [
                    'id' => '[0-9]+'
                ]
            ],
            'middleware' => 'Application\HelloWorld',
            'allowed_methods' => [ 'GET' ],
        ],
    ],
];

FastRoute:

return [
    'routes' => [
        [
            'path' => '/users/{id:[0-9]+}',
            'middleware' => 'Application\HelloWorld',
            'allowed_methods' => [ 'GET' ],
        ],
    ],
];

Aura reminds me a bit the verbosity of ZF2. FastRoute allows very compact definitions.

I don't know though if FastRoute lacks some important features that Aura could have, though.

@harikt
Copy link
Contributor

harikt commented Sep 17, 2015

Hi @bakura10 ,

Just to make clear in aura example. v2 don't use the :id format, but instead {id} . Regarding FastRoute and Aura, what I noticed is by default FastRoute don't provide a way to generate or use a named route. But I know this has been rectified via

public function generateUri($name, array $substitutions = [])
{
if (! array_key_exists($name, $this->routes)) {
throw new Exception\InvalidArgumentException(sprintf(
'Cannot generate URI for route "%s"; route not found',
$name
));
}
$route = $this->routes[$name];
$path = $route->getPath();
foreach ($substitutions as $key => $value) {
$pattern = sprintf('#\{%s(:[^}]+)?\}#', preg_quote($key));
$path = preg_replace($pattern, $value, $path);
}
return $path;
}
.

I am also not sure if there is more issues that you will run when using FastRoute . You may need a way to get all routes when you need to debug. I have been playing with FastRoute and find it a bit complex than I expected. I am sure if we can make the necessary changes in the FastRouteRouter class these issues can be resolved. That said I don't know if regex is always loved / used by people :-) .

@geerteltink
Copy link
Member

@harikt Looking at the internals, I understand what you mean (see example below). And yes, It would be easier to add a getInternalRouter function to the interface. And then the user can decide how to debug the chosen router.

@bakura10 And I'm not so sure if FastRoute is easier when routes get complex. And looking at the internals, FastRoute has a lot of overhead. I actually used FastRoute in a new project but digging into this, I'm swapping to Aura.Router again.

I have an example api route:

In Aura.Router this...

        [
            'name' => 'api',
            'path' => '/api/{resource}{/resource_id,relation,relation_id}',
            'options' => [
                'tokens' => [
                    'resource' => '[a-z]+',
                    'resource_id' => $regex,
                    'relation' => '[a-z]+',
                    'relation_id' => $regex
                ]
            ],
            'middleware' => App\Api\ApiMiddleware::class,
            'allowed_methods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
        ],

is stored as:

      'api' => 
        object(Aura\Router\Route)[36]
          protected 'name' => string 'api' (length=3)
          protected 'path' => string '/api/{resource}{/resource_id,relation,relation_id}' (length=50)
          protected 'params' => 
            array (size=6)
              'action' => string 'App\Api\ApiMiddleware' (length=21)
              'resource' => string 'ping' (length=4)
              'resource_id' => null
              'relation' => null
              'relation_id' => null
              'REQUEST_METHOD' => string 'GET' (length=3)
          protected 'regex' => 
            object(Aura\Router\Regex)[10]
              protected 'route' => null
              protected 'regex' => null
              protected 'matches' => null
          protected 'matches' => 
            object(ArrayObject)[51]
              private 'storage' => 
                array (size=4)
                  0 => string '/api/ping' (length=9)
                  'resource' => string 'ping' (length=4)
                  1 => string 'ping' (length=4)
                  'REQUEST_METHOD' => string 'GET' (length=3)
          protected 'debug' => 
            array (size=0)
              empty
          protected 'score' => int 7
          protected 'failed' => null
          protected 'tokens' => 
            array (size=4)
              'resource' => string '[a-z]+' (length=6)
              'resource_id' => string '\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}' (length=64)
              'relation' => string '[a-z]+' (length=6)
              'relation_id' => string '\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}' (length=64)
          protected 'server' => 
            array (size=1)
              'REQUEST_METHOD' => string 'GET|POST|PUT|PATCH|DELETE' (length=25)
          protected 'method' => 
            array (size=0)
              empty
          protected 'accept' => 
            array (size=0)
              empty
          protected 'values' => 
            array (size=5)
              'action' => string 'App\Api\ApiMiddleware' (length=21)
              'resource' => null
              'resource_id' => null
              'relation' => null
              'relation_id' => null
          protected 'secure' => null
          protected 'wildcard' => null
          protected 'routable' => boolean true
          protected 'is_match' => null
          protected 'generate' => null

In FastRoute, this ...

        [
            'name' => 'api',
            'path' => '/api/{resource:[a-z]+}[/{resource_id:'.$regex.'}[/{relation:[a-z]+}[/{relation_id:'.$regex.'}]]]',
            'middleware' => App\Api\ApiMiddleware::class,
            'allowed_methods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
        ],

... causes a lot of extra entries:

  1 => 
    array (size=5)
      'GET' => 
        array (size=1)
          0 => 
            array (size=2)
              'regex' => string '~^(?|/api/([a-z]+)|/api/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})|/api/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/([a-z]+)|/api/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})|/api/([a-z]+)/search/([a-zA-Z]+)/([^/]+)()())$~' (length=394)
              'routeMap' => 
                array (size=5)
                  2 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=1)
                          'resource' => string 'resource' (length=8)
                  3 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=2)
                          'resource' => string 'resource' (length=8)
                          'resource_id' => string 'resource_id' (length=11)
                  4 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=3)
                          'resource' => string 'resource' (length=8)
                          'resource_id' => string 'resource_id' (length=11)
                          'relation' => string 'relation' (length=8)
                  5 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=4)
                          'resource' => string 'resource' (length=8)
                          'resource_id' => string 'resource_id' (length=11)
                          'relation' => string 'relation' (length=8)
                          'relation_id' => string 'relation_id' (length=11)
                  6 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}/search/{key:[a-zA-Z]+}/{value}' (length=53)
                      1 => 
                        array (size=3)
                          'resource' => string 'resource' (length=8)
                          'key' => string 'key' (length=3)
                          'value' => string 'value' (length=5)
      'POST' => 
        array (size=1)
          0 => 
            array (size=2)
              'regex' => string '~^(?|/api/([a-z]+)|/api/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})|/api/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/([a-z]+)|/api/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})|/api/([a-z]+)/search/([a-zA-Z]+)/([^/]+)()())$~' (length=394)
              'routeMap' => 
                array (size=5)
                  2 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=1)
                          'resource' => string 'resource' (length=8)
                  3 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=2)
                          'resource' => string 'resource' (length=8)
                          'resource_id' => string 'resource_id' (length=11)
                  4 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=3)
                          'resource' => string 'resource' (length=8)
                          'resource_id' => string 'resource_id' (length=11)
                          'relation' => string 'relation' (length=8)
                  5 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=4)
                          'resource' => string 'resource' (length=8)
                          'resource_id' => string 'resource_id' (length=11)
                          'relation' => string 'relation' (length=8)
                          'relation_id' => string 'relation_id' (length=11)
                  6 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}/search/{key:[a-zA-Z]+}/{value}' (length=53)
                      1 => 
                        array (size=3)
                          'resource' => string 'resource' (length=8)
                          'key' => string 'key' (length=3)
                          'value' => string 'value' (length=5)
      'PUT' => 
        array (size=1)
          0 => 
            array (size=2)
              'regex' => string '~^(?|/api/([a-z]+)|/api/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})|/api/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/([a-z]+)|/api/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})|/api/([a-z]+)/search/([a-zA-Z]+)/([^/]+)()())$~' (length=394)
              'routeMap' => 
                array (size=5)
                  2 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=1)
                          'resource' => string 'resource' (length=8)
                  3 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=2)
                          'resource' => string 'resource' (length=8)
                          'resource_id' => string 'resource_id' (length=11)
                  4 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=3)
                          'resource' => string 'resource' (length=8)
                          'resource_id' => string 'resource_id' (length=11)
                          'relation' => string 'relation' (length=8)
                  5 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=4)
                          'resource' => string 'resource' (length=8)
                          'resource_id' => string 'resource_id' (length=11)
                          'relation' => string 'relation' (length=8)
                          'relation_id' => string 'relation_id' (length=11)
                  6 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}/search/{key:[a-zA-Z]+}/{value}' (length=53)
                      1 => 
                        array (size=3)
                          'resource' => string 'resource' (length=8)
                          'key' => string 'key' (length=3)
                          'value' => string 'value' (length=5)
      'PATCH' => 
        array (size=1)
          0 => 
            array (size=2)
              'regex' => string '~^(?|/api/([a-z]+)|/api/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})|/api/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/([a-z]+)|/api/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})|/api/([a-z]+)/search/([a-zA-Z]+)/([^/]+)()())$~' (length=394)
              'routeMap' => 
                array (size=5)
                  2 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=1)
                          'resource' => string 'resource' (length=8)
                  3 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=2)
                          'resource' => string 'resource' (length=8)
                          'resource_id' => string 'resource_id' (length=11)
                  4 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=3)
                          'resource' => string 'resource' (length=8)
                          'resource_id' => string 'resource_id' (length=11)
                          'relation' => string 'relation' (length=8)
                  5 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=4)
                          'resource' => string 'resource' (length=8)
                          'resource_id' => string 'resource_id' (length=11)
                          'relation' => string 'relation' (length=8)
                          'relation_id' => string 'relation_id' (length=11)
                  6 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}/search/{key:[a-zA-Z]+}/{value}' (length=53)
                      1 => 
                        array (size=3)
                          'resource' => string 'resource' (length=8)
                          'key' => string 'key' (length=3)
                          'value' => string 'value' (length=5)
      'DELETE' => 
        array (size=1)
          0 => 
            array (size=2)
              'regex' => string '~^(?|/api/([a-z]+)|/api/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})|/api/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/([a-z]+)|/api/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/([a-z]+)/(\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})|/api/([a-z]+)/search/([a-zA-Z]+)/([^/]+)()())$~' (length=394)
              'routeMap' => 
                array (size=5)
                  2 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=1)
                          'resource' => string 'resource' (length=8)
                  3 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=2)
                          'resource' => string 'resource' (length=8)
                          'resource_id' => string 'resource_id' (length=11)
                  4 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=3)
                          'resource' => string 'resource' (length=8)
                          'resource_id' => string 'resource_id' (length=11)
                          'relation' => string 'relation' (length=8)
                  5 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}[/{resource_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}[/{relation:[a-z]+}[/{relation_id:\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}}]]]' (length=204)
                      1 => 
                        array (size=4)
                          'resource' => string 'resource' (length=8)
                          'resource_id' => string 'resource_id' (length=11)
                          'relation' => string 'relation' (length=8)
                          'relation_id' => string 'relation_id' (length=11)
                  6 => 
                    array (size=2)
                      0 => string '/api/{resource:[a-z]+}/search/{key:[a-zA-Z]+}/{value}' (length=53)
                      1 => 
                        array (size=3)
                          'resource' => string 'resource' (length=8)
                          'key' => string 'key' (length=3)
                          'value' => string 'value' (length=5)

@weierophinney
Copy link
Member

The point here is not the implementation details internal to the routers, but the features they offer.

The internals are of interest if you're debugging and/or need to understand how it matches only. In the case of FastRouter, the seeming redundant structures are done internally to provide fine-grained comparisons and thus speed up the lookups when routing. While it may seem like overhead, when it comes to actual performance, it's much faster.

The reason @bakura10 (and @ezimuel) recommend using FastRoute is because of that speed, but also because of the out-of-the-box features present that do not require extra route configuration.

The example provided in the issue synopsis demonstrates this: to add a constraint to the identifier, aura (and ZF2) require you add configuration for the route. This is particularly problematic with Expressive currently because routes are injected into the underlying router implementation as soon as you call route(), get(), post(), or one of the other methods for mapping routes to handlers; as a result, any options you add at that time will be silently ignored; if you want to define constraints, you need to create a Route instance and pass it to the application. (Using the service factories "fixes" this issue, as it does that intermediary step for you.)

FastRoute, on the other hand, allows you to define those constraints inline in the routing definition string itself, making it more capable for programmatic route creation. While the definitions are more verbose, you can immediately see what data is valid for a given variable without needing to look elsewhere. (Any mention of these constraints being regex and thus "hard" is FUD, as far as I'm concerned; the constraints syntax for both Aura and ZF2 are also written as regex!)

I see a couple paths here.

First, I tend to agree with having FastRoute as the default; it requires far less configuration, making route definition easier. Second, however, I think the RouterInterface needs to stipulate that routes should be aggregated, but not injected until matching is invoked. This latter would allow users to manipulate the Route instances returned via route(), get(), post(), et al. and have those changes have effect. This would, of course, also require updating all implementations to do so.

I can potentially try my hand at this, but I'm wondering if anybody following this issue would be interested and/or willing to tackle it. I'll drop a note in this thread if/when I'm ready to begin if I do not hear from anybody before then.

@weierophinney
Copy link
Member

I've started work on this.

@weierophinney weierophinney force-pushed the feature/route-aggregation-and-defaults branch from 9da7413 to 923fa32 Compare October 10, 2015 20:32
@weierophinney
Copy link
Member

Pinging @bakura10 — more than your original intent, but also addresses an underlying issue I've observed with route configuration. If you have a chance, please take a look! (Planning on merging shortly anyways, as I'd like to get a 0.4.0 release out sooner rather than later.)

This updates the AppFactory and Application factory to use FastRoute
router by default.
- `addRoute()` now indicates it should aggregate Route instances, but
  not inject them into the underlying implementation until `match()`.
- Added verbiage to the `match()` docblock indicating expected
  implementation.
Per the changes in the `RouterInterface` spec, this patch updates the
router implementations to aggregate routes, and only inject them when
`match()` and/or `generateUri()` are invoked. Test expectations were updated,
and new tests added to validate the behavior.
Re-added `Route::setName()`, as it is useful once again.

Documented the change to the `RouterInterface`, and provided examples of setting
the route name after-the-fact.

Updated CHANGELOG.
@weierophinney weierophinney force-pushed the feature/route-aggregation-and-defaults branch from 923fa32 to 10c5dfe Compare October 10, 2015 21:09
@weierophinney weierophinney merged commit 10c5dfe into zendframework:develop Oct 10, 2015
@weierophinney weierophinney deleted the feature/route-aggregation-and-defaults branch October 10, 2015 21:13
weierophinney added a commit to weierophinney/zend-expressive-skeleton that referenced this pull request Oct 10, 2015
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants