symfony/routing
Symfony Routing maps HTTP requests to routes and parameters, and generates URLs from route definitions. Define Route and RouteCollection, then use UrlMatcher to match paths and UrlGenerator to build links based on a RequestContext.
## Getting Started
### Minimal Setup in Laravel
1. **Installation**:
```bash
composer require symfony/routing:^8.1
Laravel already includes Symfony's routing under the hood, but this standalone package is useful for custom routing logic outside the framework's default router. Note: Version ^8.1 is now recommended to leverage new features and security fixes.
First Use Case:
Define a route manually and generate URLs with the updated UrlGenerator:
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Generator\UrlGenerator;
$route = new Route('/blog/{slug}', [
'_controller' => 'App\\Http\\Controllers\\BlogController@show',
]);
$routes = new RouteCollection();
$routes->add('blog_show', $route);
$generator = new UrlGenerator($routes);
$url = $generator->generate('blog_show', ['slug' => 'hello-world']);
// Outputs: "/blog/hello-world"
Where to Look First:
Illuminate\Routing\Router (for integration patterns).symfony/routing's source code for advanced use cases.$routes = new RouteCollection();
$routes->addCollection($this->loadAdminRoutes());
$routes->addCollection($this->loadApiRoutes());
// Load from YAML/JSON/PHP files (Symfony's loaders)
$loader = new YamlFileLoader($routes, new FileLocator(__DIR__.'/config'));
$loader->load('routes.yaml');
ContentLoaderTrait (now includes HostTrait) for host-based routing in custom loaders.// In a controller
$url = $generator->generate('blog_show', ['slug' => $post->slug], UrlGenerator::ABSOLUTE_URL);
// In Blade templates (pass generator to view)
<a href="{{ $url }}">Read Post</a>
UrlGenerator now validates regex requirements more strictly (see #cve-2026-45065).$form->action = $generator->generate('user_edit', ['id' => $user->id]);
UrlMatcher for complex requirements (e.g., multi-tenancy):
$matcher = new UrlMatcher($routes, $context);
$parameters = $matcher->match('/api/v1/users/123');
$tenantId = $parameters['_tenant'] ?? null;
$cliRoutes = new RouteCollection();
$cliRoutes->add('cli:backup', new Route('/backup', [], [], [], 'backup_command'));
$generator = new UrlGenerator($cliRoutes);
$command = $generator->generate('backup_command'); // Outputs: "/backup"
$route = new Route('/user/{id}', [], [], [], [], ['id' => '\d+']);
$matcher = new UrlMatcher([$route], $context);
$matcher->match('/user/123'); // Valid
$matcher->match('/user/abc'); // Throws Exception (now more secure)
a|b) are properly anchored to avoid injection risks.HostTrait (now included in ContentLoaderTrait):
$route = new Route('/{_locale}/blog/{slug}', [
'_locale' => 'en|fr',
]);
$context = new RequestContext();
$context->setParameter('_locale', 'fr');
$generator->generate('blog_show', ['slug' => 'bonjour'], [], UrlGenerator::ABSOLUTE_PATH);
// Outputs: "/fr/blog/bonjour"
Case Sensitivity in Host Matching:
api.example.com) are case-sensitive. Use HostTrait (now part of ContentLoaderTrait) or compile routes with case-insensitive flags.RequestContext or use Route::setHost() with lowercase hosts.Query Parameters in URL Generation:
?_query=value) are not preserved by default. Use the _query option explicitly:
$generator->generate('route_name', [], ['_query' => ['sort' => 'asc']]);
Circular Route References:
UrlGenerator::ABSOLUTE_URL or validate routes before generation.Parameter Overwriting:
UrlGenerator::INCLUDE_DEFAULT_VALUES:
$generator->generate('route_name', [], UrlGenerator::INCLUDE_DEFAULT_VALUES);
Performance with Large Route Collections:
$compiler = new RouteCompiler();
$compiledRoutes = $compiler->compile($routes);
$matcher = new CompiledUrlMatcher($compiledRoutes, $context);
Regex Requirement Validation (New in 8.1):
a|.*) can cause performance issues or injection. Fix:
^(a|b)$ instead of a|b.$this->assertTrue(preg_match('/^\d+$/', '123')); // Safe
$this->assertFalse(preg_match('/.*/', 'malicious')); // Rejected
Inspect Matched Parameters:
UrlMatcher::match() output:
try {
$params = $matcher->match('/some/path');
} catch (Exception $e) {
logger()->error('Route match failed:', ['exception' => $e, 'path' => '/some/path']);
}
Validate Route Definitions:
RouteCollection::getIterator() to validate routes:
foreach ($routes as $name => $route) {
if (empty($route->getDefault('_controller'))) {
logger()->warning("Route '$name' has no controller!");
}
}
Test URL Generation:
public function testUrlGeneration()
{
$this->assertEquals(
'/blog/hello-world',
$generator->generate('blog_show', ['slug' => 'hello-world'])
);
}
Custom Route Loaders:
FileLocator or RouteLoaderInterface to load routes from custom sources (e.g., databases):
class DatabaseRouteLoader implements RouteLoaderInterface
{
public function load($resource, string $type = null)
{
$routes = $this->fetchRoutesFromDatabase();
return new RouteCollection($routes);
}
}
ContentLoaderTrait for host-based routing in custom loaders.Middleware for Route Matching:
UrlMatcher in middleware:
$matcher = new UrlMatcher($routes, $context);
$matcher = new CustomMatcherDecorator($matcher);
class CustomMatcherDecorator
{
public function match(string $pathinfo): array
{
$start = microtime(true);
How can I help you explore Laravel packages today?