zendframework / zend-router

Standalone routing implementation for HTTP and console requests
BSD 3-Clause "New" or "Revised" License
32 stars 20 forks source link

Strange behaviour with constraints and segment child routes. #27

Closed vbogoev closed 7 years ago

vbogoev commented 7 years ago

I want to create routes with an optional [lang] parameter that would be used to set the application language as follows:

Example with default lang param(EN for example)

domain.tld/ domain.tld/users domain.tld/contacts etc.

Example witt new lang param(DE for examle)

domain.tld/de domain.tld/de/users domain.tld/de/contacts etc.

This is my route configuration:

 'router' => [
        'routes' => [
            'site' => [
                'type' => Segment::class,
                'options' => [
                    'route' => '[/:lang]',
                    'constraints' => [
                        'lang' => '[a-z]{2}',
                    ],
                    'defaults' => [
                        'controller' => Controller\IndexController::class,
                        'action' => 'index',
                        'lang' => 'en',
                    ],
                ],
                'may_terminate' => true,
                'child_routes' => [
                    'home' => [
                        'type' => Literal::class,
                        'options' => [
                            'route' => '/',
                            'defaults' => [
                                'controller' => Controller\IndexController::class,
                                'action' => 'index',
                            ],
                        ],
                        'may_terminate' => true,
                    ],
                    'application' => [
                        'type' => Segment::class,
                        'options' => [
                            'route' => '/application[/:action]',
                            'defaults' => [
                                'controller' => Controller\IndexController::class,
                                'action' => 'index',
                            ],
                        ],
                        'may_terminate' => true,
                    ],
                ],
            ],
        ],
    ],

When I have lang param in the url everything is fine. But when I try to open "default" lang without specifying it into the path (ex. example.tld/application/test) it gives me 404 error.

What I found is that the final regex after transformation from Segment class is (\G(?:/(?P<param1>([a-z]{2})))?) and path is /application/test. When preg_match is executed on Segment.php:385 it return match with following values:

 [
      '0' => '/ad',
      'param1' => 'ad',
      '1' => 'ad',
],

which is obviously the wrong behavior. My language is set to "ad" instead of open application/test action. I tested around 10 more regex but without success... (ex. ^[a-z]{2}$).

weierophinney commented 7 years ago

The problem you are seeing here is that your URI has to match the parent route before it will attempt to match child routes; this includes matching any and all optional segments of the parent route. This is by design; the number of edge cases we'd need to account for if we allowed omitting optional segments in parent routes would make matching unpredictable.

If you want to be able to access a route without the initial segment, you will need to re-declare it without the parent.