Router

This PHP class matches url against predefined route patterns. Routes follow simple DSL syntax, Router converts them to regular expressions and performs match on a first-match basis.

Short codes

Router uses the following short codes for the common patterns:

Examples

$router = new Router();

$router->add(
    'controller-only',
    '/$controller'
);

$result = $router->match('/news');

Result:

Array
(
    [url] => Array
        (
            [controller] => news
        )

    [id] => controller-only
    [data] => Array
        (
            [controller] => news
        )

)

Named placeholders should consist from uppercase and lowercase letters and underscores only, without spaces, hyphens etc. e.g. $controller, :action, #some_id

$router = new Router();

$router->add(
    'controller-and-action',
    '/$my_controller/$action'
);

$result = $router->match('/news/add');

Result:

Array
(
    [url] => Array
        (
            [my_controller] => news
            [action] => add
        )

    [id] => controller-and-action
    [data] => Array
        (
            [my_controller] => news
            [action] => add
        )

)

Optional parameters example


$router = new Router();

$router->add(
    'optional-controller-and-action',
    '/($controller(/$action))',
    array(
        'controller' => 'index',
        'action' => 'index'
    )
);

$result = $router->match('/news/add');

Result:

Array
(
    [url] => Array
        (
            [controller] => news
            [action] => add
        )

    [id] => optional-controller-and-action
    [data] => Array
        (
            [controller] => news
            [action] => add
        )

)

$result = $router->match('/news');

Result:

Array
(
    [url] => Array
        (
            [controller] => news
        )

    [id] => optional-controller-and-action
    [data] => Array
        (
            [controller] => news
            [action] => index
        )

)

$result = $router->match('/');

Result:

Array
(
    [url] => Array
        (
        )

    [id] => optional-controller-and-action
    [data] => Array
        (
            [controller] => index
            [action] => index
        )

)

When using optional parameters, it makes sense to provide an array of default values as a third parameter to Router::add method, as in the above example

If url contains query string, it will be appended to the resulting data array:

$router = new Router();

$router->add(
    'optional-controller-and-action',
    '/($controller)(/$action)',
    array(
        'controller' => 'index',
        'action' => 'index'
    )
);

$result = $router->match('/news/add?slug=some-slug&id=12');

Result:

Array
(
    [url] => Array
        (
            [controller] => news
            [action] => add
        )

    [id] => optional-controller-and-action
    [data] => Array
        (
            [slug] => some-slug
            [id] => 12
            [controller] => news
            [action] => add
        )

)

It is possible to use in place regular expressions:

$router = new Router();

$router->add(
    'in-place-regex',
    '/($controller<[A-Z]{2}>)(/$action)',
    array(
        'controller' => 'index',
        'action' => 'index'
    )
);

$result = $router->match('/news/add');

Result (empty array - no match):

Array
(
)

$result = $router->match('/AB/add');

Array
(
    [url] => Array
        (
            [controller] => AB
            [action] => add
        )

    [id] => in-place-regex
    [data] => Array
        (
            [controller] => AB
            [action] => add
        )

)

Other examples

$router = new Router();

$router->add(
    'article-with-slug',
    '/$controller-$action(/^slug)',
    array(
        'controller' => 'index',
        'action' => 'index',
        'format' => 'html'
    )
);

$result = $router->match('/news-add/some-article-title');

Result:

Array
(
    [url] => Array
        (
            [controller] => news
            [action] => add
            [slug] => some-article-title
        )

    [id] => article-with-slug
    [data] => Array
        (
            [controller] => news
            [action] => add
            [format] => html
            [slug] => some-article-title
        )

)
$router = new Router();

$router->add(
    'article-with-date-and-slug',
    '(/$controller)(/$action(.~format))(/#year-#month-#day)(/^slug)',
    array(
        'controller' => 'index',
        'action' => 'index',
        'format' => 'html'
    )
);

$result = $router->match('/articles/2009-01-01/some-slug-for-article');

Result:

Array
(
    [url] => Array
        (
            [controller] => articles
            [year] => 2009
            [month] => 01
            [day] => 01
            [slug] => some-slug-for-article
        )

    [id] => article-with-date-and-slug
    [data] => Array
        (
            [controller] => articles
            [action] => index
            [format] => html
            [year] => 2009
            [month] => 01
            [day] => 01
            [slug] => some-slug-for-article
        )

)

As the Router works on a first-match basis, it's recommended to define routes in order of specificity, from most specific to general ones.

$router = new Router();

$router->add(
    'controller-action-id',
    '/$controller/$action/#id'
);

$router->add(
    'controller-action',
    '/$controller/$action'
);

$router->add(
    'controller',
    '/$controller'
);

Predefined routes

It's possible to have predefined routes in some php file, and provide them to Router as a parameter for constructor:

[routes.php]

<?php
return array(
    'controller-action-id' => array(
        'route'     => '/$controller/$action/#id'
    ),
    'controller-action' => array(
        'route'     => '/$controller/$action'
    ),
    'general' => array(
        'route'     => '(/$controller)(/$action(.:format<[a-z]{2,4}>))(/#id)(/:slug<[A-Za-z0-9\-]+>)',
        'defaults'  => array(
            'controller' => 'index',
            'action' => 'index',
            'format' => 'html',
            'id' => 1,
            'slug' => 'default-slug'
        )
    )
);
?>

[index.php]

<?php
$routes = include 'routes.php';

$router = new Router($routes);

$result = $router->match('/news/add.xml/12/some-slug');
$result = $router->match('/news/add/12');
$result = $router->match('/news/add');
?>

Using Router compile method it's possible to get php code of routes array with compiled regular expressions, so Router will skip route to regex conversion

$router->compile();

[output]

<?php return array (
  'controller-action-id' =>
    array (
    'route' => '/$controller/$action/#id',
    'regex' => '/^\/(?P<controller>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\/(?P<action>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\/(?P<id>[0-9]+)$/D',
    ),
    'controller-action' =>
    array (
    'route' => '/$controller/$action',
    'regex' => '/^\/(?P<controller>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\/(?P<action>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)$/D',
        ),
        'general' =>
        array (
        'route' => '(/$controller)(/$action(.:format<[a-z]{2,4}>))(/#id)(/:slug<[A-Za-z0-9\-]+>)',
        'defaults' =>
        array (
        'controller' => 'index',
        'action' => 'index',
        'format' => 'html',
        'id' => 1,
        'slug' => 'default-slug',
        ),
        'regex' => '/^(?:\/(?P<controller>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*))?(?:\/(?P<action>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)(?:\.(?P<format>[a-z]{2,4}))?)?(?:\/(?P<id>[0-9]+))?(?:\/(?P<slug>[A-Za-z0-9\-]+))?$/D',
            ),
); ?>

URL generation

URLs can be generated using Router::url() method, which accepts 3 arguments:

For example, Router matched the following:

$route = array(
    'id' => 'article-with-date-and-slug',
    'url' => array(
        'controller' => 'articles',
        'action' => 'index',
        'year' => '2009'
    ),
    'data' => array(
        'controller' => 'articles',
        'action' => 'index',
        'format' => 'html',
        'year' => '2009',
        'month' => '01',
        'day' => '01',
        'slug' => 'some-slug-for-article'
    )
);

URL generated with 'full' type set:

var_dump(
    $router->url(
        array(
            'controller' => 'test',
            'action' => 'view',
            'format' => 'json',
            'year' => '2010',
            'month' => '02',
            'asdf' => 'asd' // non-existing parameter, will be filtered
        ),
        $route,
        'full'
    )
);

Result: '/test/view.json/2010-02-01/some-slug-for-article'

URL generated with 'url' type set:

var_dump(
    $router->url(
        array(
            'controller' => 'test',
            'action' => 'view',
            'format' => 'json',
            'asdf' => 'asd' // non-existing parameter, will be filtered
        ),
        $route,
        'url'
    )
);

Result: '/test/view.json/2009'

URL generated with 'self' type set:

var_dump(
    $router->url(
        array(
            'controller' => 'test',
            'action' => 'view',
            'format' => 'json',
            'asdf' => 'asd' // non-existing parameter, will be filtered
        ),
        $route,
        'self'
    )
);

Result: '/test/view.json'