Skip to content

The Routing Component

Warning: You are browsing the documentation for Symfony 3.x, which is no longer maintained.

Read the updated version of this page for Symfony 7.2 (the current stable version).

The Routing component maps an HTTP request to a set of configuration variables.

Installation

1
$ composer require symfony/routing:^3.4

Note

If you install this component outside of a Symfony application, you must require the vendor/autoload.php file in your code to enable the class autoloading mechanism provided by Composer. Read this article for more details.

Usage

See also

This article explains how to use the Routing features as an independent component in any PHP application. Read the Routing article to learn about how to use it in Symfony applications.

In order to set up a basic routing system you need three parts:

  • A RouteCollection, which contains the route definitions (instances of the Route class)
  • A RequestContext, which has information about the request
  • A UrlMatcher, which performs the mapping of the request to a single route

Here is a quick example. Notice that this assumes that you've already configured your autoloader to load the Routing component:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

$route = new Route('/foo', ['_controller' => 'MyController']);
$routes = new RouteCollection();
$routes->add('route_name', $route);

$context = new RequestContext('/');

$matcher = new UrlMatcher($routes, $context);

$parameters = $matcher->match('/foo');
// ['_controller' => 'MyController', '_route' => 'route_name']

Note

The RequestContext parameters can be populated with the values stored in $_SERVER, but it's easier to use the HttpFoundation component as explained below.

You can add as many routes as you like to a RouteCollection.

The RouteCollection::add() method takes two arguments. The first is the name of the route. The second is a Route object, which expects a URL path and some array of custom variables in its constructor. This array of custom variables can be anything that's significant to your application, and is returned when that route is matched.

The UrlMatcher::match() returns the variables you set on the route as well as the wildcard placeholders (see below). Your application can now use this information to continue processing the request. In addition to the configured variables, a _route key is added, which holds the name of the matched route.

If no matching route can be found, a ResourceNotFoundException will be thrown.

Defining Routes

A full route definition can contain up to eight parts:

  1. The URL pattern. This is matched against the URL passed to the RequestContext. It is not a regular expression, but can contain named wildcard placeholders (e.g. {slug}) to match dynamic parts in the URL. The component will create the regular expression from it.
  2. An array of default parameters. This contains an array of arbitrary values that will be returned when the request matches the route. It is used by convention to map a controller to the route.
  3. An array of requirements. These define constraints for the values of the placeholders in the pattern as regular expressions.
  4. An array of options. These contain advanced settings for the route and can be used to control encoding or customize compilation. See The Routing Component below. You can learn more about them by reading setOptions() implementation.
  5. A host. This is matched against the host of the request. See How to Match a Route Based on the Host for more details.
  6. An array of schemes. These enforce a certain HTTP scheme (http, https).
  7. An array of methods. These enforce a certain HTTP request method (HEAD, GET, POST, ...).
  8. A condition, using the The Expression Syntax. A string that must evaluate to true so the route matches. See How to Restrict Route Matching through Conditions for more details.

Take the following route, which combines several of these ideas:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$route = new Route(
    '/archive/{month}', // path
    ['_controller' => 'showArchive'], // default values
    ['month' => '[0-9]{4}-[0-9]{2}', 'subdomain' => 'www|m'], // requirements
    [], // options
    '{subdomain}.example.com', // host
    [], // schemes
    [], // methods
    'context.getHost() matches "/(secure|admin).example.com/"' // condition
);

// ...

$parameters = $matcher->match('/archive/2012-01');
// [
//     '_controller' => 'showArchive',
//     'month' => '2012-01',
//     'subdomain' => 'www',
//     '_route' => ...
// ]

$parameters = $matcher->match('/archive/foo');
// throws ResourceNotFoundException

In this case, the route is matched by /archive/2012-01, because the {month} wildcard matches the regular expression wildcard given. However, /archive/foo does not match, because "foo" fails the month wildcard.

When using wildcards, these are returned in the array result when calling match. The part of the path that the wildcard matched (e.g. 2012-01) is used as value.

A placeholder matches any character except slashes / by default, unless you define a specific requirement for it. The reason is that they are used by convention to separate different placeholders.

If you want a placeholder to match anything, it must be the last of the route:

1
2
3
4
5
$route = new Route(
    '/start/{required}/{anything}',
    ['required' => 'default'], // should always be defined
    ['anything' => '.*'] // explicit requirement to allow "/"
);

Learn more about it by reading How to Allow a "/" Character in a Route Parameter.

Using Prefixes and Collection Settings

You can add routes or other instances of RouteCollection to another collection. This way you can build a tree of routes. Additionally you can define a prefix and default values for the parameters, requirements, options, schemes and the host to all routes of a subtree using methods provided by the RouteCollection class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$rootCollection = new RouteCollection();

$subCollection = new RouteCollection();
$subCollection->add(...);
$subCollection->add(...);
$subCollection->addPrefix('/prefix');
$subCollection->addDefaults([...]);
$subCollection->addRequirements([...]);
$subCollection->addOptions([...]);
$subCollection->setHost('{subdomain}.example.com');
$subCollection->setMethods(['POST']);
$subCollection->setSchemes(['https']);
$subCollection->setCondition('context.getHost() matches "/(secure|admin).example.com/"');

$rootCollection->addCollection($subCollection);

Set the Request Parameters

The RequestContext provides information about the current request. You can define all parameters of an HTTP request with this class via its constructor:

1
2
3
4
5
6
7
8
9
10
public function __construct(
    $baseUrl = '',
    $method = 'GET',
    $host = 'localhost',
    $scheme = 'http',
    $httpPort = 80,
    $httpsPort = 443,
    $path = '/',
    $queryString = ''
)

Normally you can pass the values from the $_SERVER variable to populate the RequestContext. But if you use the HttpFoundation component, you can use its Request class to feed the RequestContext in a shortcut:

1
2
3
4
use Symfony\Component\HttpFoundation\Request;

$context = new RequestContext();
$context->fromRequest(Request::createFromGlobals());

Generate a URL

While the UrlMatcher tries to find a route that fits the given request you can also build a URL from a certain route with the UrlGenerator:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use Symfony\Component\Routing\Generator\UrlGenerator;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

$routes = new RouteCollection();
$routes->add('show_post', new Route('/show/{slug}'));

$context = new RequestContext('/');

$generator = new UrlGenerator($routes, $context);

$url = $generator->generate('show_post', [
    'slug' => 'my-blog-post',
]);
// /show/my-blog-post

Note

If you have defined a scheme, an absolute URL is generated if the scheme of the current RequestContext does not match the requirement.

Check if a Route Exists

In highly dynamic applications, it may be necessary to check whether a route exists before using it to generate a URL. In those cases, don't use the getRouteCollection() method because that regenerates the routing cache and slows down the application.

Instead, try to generate the URL and catch the RouteNotFoundException thrown when the route doesn't exist:

1
2
3
4
5
6
7
8
9
use Symfony\Component\Routing\Exception\RouteNotFoundException;

// ...

try {
    $url = $generator->generate($dynamicRouteName, $parameters);
} catch (RouteNotFoundException $e) {
    // the route is not defined...
}

Load Routes from a File

You've already seen how you can add routes to a collection right inside PHP. But you can also load routes from a number of different files.

The Routing component comes with a number of loader classes, each giving you the ability to load a collection of route definitions from an external file of some format. Each loader expects a FileLocator instance as the constructor argument. You can use the FileLocator to define an array of paths in which the loader will look for the requested files. If the file is found, the loader returns a RouteCollection.

If you're using the YamlFileLoader, then route definitions look like this:

1
2
3
4
5
6
7
8
# routes.yml
route1:
    path:     /foo
    defaults: { _controller: 'MyController::fooAction' }

route2:
    path:     /foo/bar
    defaults: { _controller: 'MyController::foobarAction' }

To load this file, you can use the following code. This assumes that your routes.yml file is in the same directory as the below code:

1
2
3
4
5
6
7
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Routing\Loader\YamlFileLoader;

// looks inside *this* directory
$fileLocator = new FileLocator([__DIR__]);
$loader = new YamlFileLoader($fileLocator);
$routes = $loader->load('routes.yml');

Besides YamlFileLoader there are two other loaders that work the same way:

If you use the PhpFileLoader you have to provide the name of a PHP file which returns a RouteCollection:

1
2
3
4
5
6
7
8
9
10
11
12
// RouteProvider.php
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

$routes = new RouteCollection();
$routes->add(
    'route_name',
    new Route('/foo', ['_controller' => 'ExampleController'])
);
// ...

return $routes;

Routes as Closures

There is also the ClosureLoader, which calls a closure and uses the result as a RouteCollection:

1
2
3
4
5
6
7
8
use Symfony\Component\Routing\Loader\ClosureLoader;

$closure = function () {
    return new RouteCollection();
};

$loader = new ClosureLoader();
$routes = $loader->load($closure);

Routes as Annotations

Last but not least there are AnnotationDirectoryLoader and AnnotationFileLoader to load route definitions from class annotations. The specific details are left out here.

Note

In order to use the annotation loader, you should have installed the doctrine/annotations and doctrine/cache packages with Composer.

Tip

Annotation classes aren't loaded automatically, so you must load them using a class loader like this:

1
2
3
4
5
6
7
8
9
use Composer\Autoload\ClassLoader;
use Doctrine\Common\Annotations\AnnotationRegistry;

/** @var ClassLoader $loader */
$loader = require __DIR__.'/../vendor/autoload.php';

AnnotationRegistry::registerLoader([$loader, 'loadClass']);

return $loader;

The all-in-one Router

The Router class is an all-in-one package to use the Routing component. The constructor expects a loader instance, a path to the main route definition and some other settings:

1
2
3
4
5
6
7
public function __construct(
    LoaderInterface $loader,
    $resource,
    array $options = [],
    RequestContext $context = null,
    LoggerInterface $logger = null
);

With the cache_dir option you can enable route caching (if you provide a path) or disable caching (if it's set to null). The caching is done automatically in the background if you want to use it. A basic example of the Router class would look like:

1
2
3
4
5
6
7
8
9
10
11
$fileLocator = new FileLocator([__DIR__]);
$requestContext = new RequestContext('/');

$router = new Router(
    new YamlFileLoader($fileLocator),
    'routes.yml',
    ['cache_dir' => __DIR__.'/cache'],
    $requestContext
);
$parameters = $router->match('/foo/bar');
$url = $router->generate('some_route', ['parameter' => 'value']);

Note

If you use caching, the Routing component will compile new classes which are saved in the cache_dir. This means your script must have write permissions for that location.

Unicode Routing Support

3.2

UTF-8 support for route paths and requirements was introduced in Symfony 3.2.

The Routing component supports UTF-8 characters in route paths and requirements. Thanks to the utf8 route option, you can make Symfony match and generate routes with UTF-8 characters:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// src/AppBundle/Controller/DefaultController.php
namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;

class DefaultController extends Controller
{
    /**
     * @Route("/category/{name}", name="route1", options={"utf8": true})
     */
    public function categoryAction()
    {
        // ...
    }

In this route, the utf8 option set to true makes Symfony consider the . requirement to match any UTF-8 characters instead of just a single byte character. This means that so the following URLs would match: /category/日本語, /category/فارسی, /category/한국어, etc. In case you are wondering, this option also allows to include and match emojis in URLs.

You can also include UTF-8 strings as routing requirements:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// src/AppBundle/Controller/DefaultController.php
namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;

class DefaultController extends Controller
{
    /**
     * @Route(
     *     "/category/{name}",
     *     name="route2",
     *     defaults={"name": "한국어"},
     *     options={"utf8": true}
     * )
     */
    public function defaultAction()
    {
        // ...
    }

Tip

In addition to UTF-8 characters, the Routing component also supports all the PCRE Unicode properties, which are escape sequences that match generic character types. For example, \p{Lu} matches any uppercase character in any language, \p{Greek} matches any Greek character, \P{Han} matches any character not included in the Chinese Han script.

Note

In Symfony 3.2, there is no need to explicitly set the utf8 option. As soon as Symfony finds a UTF-8 character in the route path or requirements, it will automatically turn on the UTF-8 support. However, this behavior is deprecated and setting the option will be required in Symfony 4.0.

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version