Symfony Routing 组件 CHANGELOG 全解析:从 2.1 到 8.2 的路由引擎演进路线图
Symfony Routing 组件 CHANGELOG 全解析:从 2.1 到 8.2 的路由引擎演进路线图
本文以 CHANGELOG.md 为主线,系统梳理 Symfony Routing 组件(symfony/routing,项目描述为 "Maps an HTTP request to a set of configuration variables")从 2.1 到 8.2 的全部版本演进:包括 RouterBundle 与框架解耦、#[Route] 属性路由的引入、编译型匹配器/生成器、Requirement 通用约束常量库、条件表达式增强以及多次 BC Break 的完整破坏性变更明细。读者将获得一份可直接用于版本升级评估、迁移决策和配置选型的实战参考手册。
组件定位与版本脉络总览
Routing 组件是 Symfony 中负责"把 HTTP 请求映射为一组配置变量"的底层组件:UrlMatcher 负责将传入的 path info 匹配到具体路由,UrlGenerator 负责根据路由名与参数反向生成 URL。它不依赖完整的 Symfony 框架即可独立使用,README.md 中给出了最小可用示例:
use Symfony\Component\Routing\Generator\UrlGenerator;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
$route = new Route('/blog/{slug}', ['_controller' => BlogController::class]);
$routes = new RouteCollection();
$routes->add('blog_show', $route);
$context = new RequestContext();
$matcher = new UrlMatcher($routes, $context);
$parameters = $matcher->match('/blog/lorem-ipsum');
// ['_controller' => 'App\Controller\BlogController', 'slug' => 'lorem-ipsum', '_route' => 'blog_show']
$generator = new UrlGenerator($routes, $context);
$url = $generator->generate('blog_show', ['slug' => 'my-blog-post']);
// '/blog/my-blog-post'
从当前仓库的 composer.json 可见,本组件要求 PHP >=8.4.1,生产依赖仅 symfony/deprecation-contracts,与 Config、Console、HttpFoundation 等均为可选开发依赖,体现了其高度的独立性。
CHANGELOG 记录的时间跨度涵盖 2.1 ~ 8.2。按主题可归纳为四条主线:
- 配置格式与声明方式:XML 配置从支持到弃用再到移除,PHP/YAML/属性(Attribute)成为主流;
- 匹配与生成架构:从解释式
UrlMatcher/UrlGenerator演进出编译型CompiledUrlMatcher/CompiledUrlGenerator; - 属性路由体系:从 Doctrine 注解(annotations)全面切换到原生 PHP 属性(attributes);
- 能力扩展:条件表达式、国际化路由、通用 Requirement 约束、框架集成解耦(
RouterBundle)。
8.x:框架解耦与路由能力大扩展
8.2:RouterBundle 诞生——路由能力从 FrameworkBundle 整体迁移
8.2 是组件近期最重要的架构变更。此前 router 配置与相关服务由 FrameworkBundle 的 framework.router 提供;8.2 起新增 RouterBundle,把这些能力整体收编进组件自身。对应实现见 RouterBundle.php,其文档注释明确写道:"Provides the router and the services that load and generate routes"(提供路由器以及加载和生成路由的服务)。
RouterBundle::configure() 定义了 router 根节点的配置结构(RouterBundle.php):
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enabled |
bool | false |
是否启用路由器;空配置不会自动启用(RouterBundleTest.php 专门测试了这一点) |
resource |
scalar | null |
主路由资源,启用时必须配置,否则抛出 The child config "resource" under "router" must be configured. |
type |
scalar | — | 资源的加载类型(如 xml、yaml、php) |
default_uri |
scalar | null |
非 HTTP 环境下生成 URL 使用的默认 URI |
http_port |
int | 80 |
HTTP 端口 |
https_port |
int | 443 |
HTTPS 端口 |
strict_requirements |
bool/null | true |
true 时参数不满足 requirement 抛异常;false 时不抛异常并返回 null;null 时完全跳过参数校验。开发环境推荐 true,生产环境推荐 false 或 null |
utf8 |
bool | true |
是否启用 UTF-8 匹配 |
RouterBundle::build() 注册了两个编译器相关的自动装配(RouterBundle.php):带 AsRouteLoader 属性的类自动打上 routing.route_loader 标签,带 AsRoutingConditionService 属性的类自动打上 routing.condition_service 标签(含 alias、priority 参数);同时注册 LocalizedRoutesPass 编译 pass。loadExtension() 则会导入 Resources/config/routing.php 与 Resources/config/console.php,并把 router.request_context.host/scheme 参数内联进 router.request_context 服务参数,避免运行时通过 ParameterBag::all() 急切解析 env 变量。
AsRouteLoader:声明式服务路由加载器
#<a href="https://link.gitcode.com/i/02d19a6f248e79fd699e20dd9b546eb9" target="_blank">AsRouteLoader] 属性([Attribute/AsRouteLoader.php)取代了 FrameworkBundle 的 RouteLoaderInterface,用于把服务标记为路由加载器,使其方法可被路由资源引用:
#[AsRouteLoader]
class SomeRouteLoader
{
public function loadRoutes(): RouteCollection
{
// ...
}
}
随后在路由配置中引用它:
some_routes:
resource: 'some_route_loader::loadRoutes'
type: service
RouterBundleTest.php 验证了该属性会为自动配置的服务打上 routing.route_loader 标签。
AsRoutingConditionService:条件表达式中调用服务
#<a href="https://link.gitcode.com/i/83932d78e530b7123295969853f28ced" target="_blank">AsRoutingConditionService] 属性([Attribute/AsRoutingConditionService.php)用于自动配置路由条件服务,使服务可以在路由 condition 表达式中被调用。构造函数接受 ?string $alias(在条件表达式中使用的别名)和 int $priority = 0(允许同名别名覆盖优先级):
#[AsRoutingConditionService('foo')]
class SomeFooService
{
public function bar(): bool
{
// ...
}
}
然后即可在路由条件中通过 service('foo').bar() 调用:
#[Route('/page', condition: "service('foo').bar()")]
public function page(): Response
{
// ...
}
CHANGELOG 特别指出:该属性不再继承 AutoconfigureTag,因此它只标记自身携带该属性的类,而不会波及这些类的子类。
router:match 命令:命令行调试路由匹配
Command\RouterMatchCommand(Command/RouterMatchCommand.php)是 FrameworkBundle 曾经提供的 router:match 命令的组件级实现,用于模拟一次 path info 匹配、定位"为什么这条路由没匹配上"。其支持的参数如下:
| 参数/选项 | 类型 | 说明 |
|---|---|---|
path_info |
必填参数 | 要匹配的路径 |
--method |
选项 | 设置 HTTP 方法 |
--scheme |
选项 | 设置 URI scheme(通常是 http 或 https) |
--host |
选项 | 设置 URI 主机 |
--verbose |
选项 | 显示"未匹配"路由的详细原因 |
典型用法(来自命令自带的 help 文本):
php bin/console router:match /foo
php bin/console router:match /foo --method POST --scheme https --host symfony.com --verbose
该命令内部使用 TraceableUrlMatcher(Matcher/TraceableUrlMatcher.php)逐条追踪路由匹配痕迹。TraceableUrlMatcher 定义了三种匹配级别常量:ROUTE_DOES_NOT_MATCH = 0、ROUTE_ALMOST_MATCHES = 1、ROUTE_MATCHES = 2,能精确区分"完全没匹配""差一点就匹配(如路径对了但方法/条件不符)""成功匹配"三种状态。命令输出时对"几乎匹配"的路由给出原因说明,对成功匹配的路由还会联动调用 debug:router 输出该路由详情。
8.2 的其他路由能力扩展
firewall选项:可在路由定义中声明,以_firewall路由默认值暴露。从 Attribute/Route.php 可见,#<a href="https://link.gitcode.com/i/4464652a9a13228a8abdb59c98c56770" target="_blank">Route]构造参数?string $firewall会被写入$this->defaults['_firewall'];YAML 加载同样支持([YamlFileLoaderTest.php 断言$route->getDefault('_firewall') === 'api')。RouteCompilerTest.php 还验证了_firewall不能被用作路径或主机参数名。_query默认查询参数:允许通过_query路由默认值定义默认查询参数,也支持在生成 URL 时传入查询参数(见下文 7.4 条目)。RequestContext::runWith():在不泄漏修改的前提下,为另一个 host、scheme 或 base URL 生成和匹配 URL。实现见 RequestContext.php:它克隆当前上下文、临时应用传入值、执行回调,并在finally中恢复全部字段,即使回调抛异常也能还原。Route::addCondition()/RouteCollection::addCondition()与add_conditionimport 键:把新条件与已有条件用and组合而非覆盖。Route::addCondition()实现于 Route.php:空条件直接忽略,否则以(%s) and (%s)拼接;RouteCollection::addCondition()(RouteCollection.php)则遍历集合内所有路由逐条组合。YAML 侧的add_condition键与 import 用法见测试夹具 Tests/Fixtures/add_condition.yml:
route_with_conditions:
path: /route
condition: 'context.getMethod() == "GET"'
add_condition: 'request.isSecure()'
import_with_add_condition:
resource: validpattern.yml
name_prefix: added_
add_condition: 'request.isSecure()'
PHP 配置器侧则通过 RouteConfigurator::addCondition()(Loader/Configurator/Traits/RouteTrait.php)实现,对应的完整示例见 Tests/Fixtures/php_dsl_add_condition.php。
8.1:prefix() 的 trailingSlashOnRoot 参数
8.1 为 CollectionConfigurator::prefix() 新增 $trailingSlashOnRoot 参数(默认 true),用于关闭根路由上的尾部斜杠。实现细节见 Loader/Configurator/CollectionConfigurator.php 与 Loader/Configurator/Traits/PrefixTrait.php:当 $trailingSlashOnRoot 为 false 时,路径恰好等于前缀加 / 的路由会被去尾斜杠。该能力在 YAML import 中对应 trailing_slash_on_root 键(见 Loader/Configurator/RoutesReference.php 的 ImportConfig 类型定义),测试覆盖见 YamlFileLoaderTest.php(/slash/ 保留、/no-slash 去斜杠)。
8.0:破坏性变更集中移除
8.0 移除了一批已弃用能力,升级时需重点排查:
- 移除 XML 配置格式支持(5.x 起弃用),改用 YAML、PHP 或属性;
- 移除
Annotation命名空间下的类别名(6.4 起加入、7.4 起弃用),一律使用属性; - 移除属性类中的 getter/setter,改为公共属性(7.4 起弃用);
- 移除受保护的
AttributeClassLoader::$routeAnnotationClass属性与setRouteAnnotationClass()方法(7.2 起弃用),改用AttributeClassLoader::setRouteAttributeClass(); - PHP 配置文件中禁止访问 loader 的内部作用域,只能使用其公共 API(7.4 起弃用);
- 给
UrlGenerator传非数组_query参数会抛InvalidParameterException。这一点在 Generator/UrlGenerator.php 中有两处校验:默认_query非数组抛异常、参数_query非数组抛异常。
7.x:属性路由体系的成熟与通用约束库扩充
7.4:属性自动注册路由服务与 PHP 数组 DSL
AttributeServicesLoader与RoutingControllerPass:允许从服务上的属性自动注册路由,将路由定义与控制器服务注册解耦。_query查询参数:UrlGenerator支持通过_query参数生成查询字符串。UrlGeneratorTest.php 有大量用例:_query中的键值会拼接为 URL 查询串,值null会被省略,非数组则抛InvalidParameterException。#<a href="https://link.gitcode.com/i/af880051a9e433a6b6468bbc08182ca2" target="_blank">Route]属性支持多个 env 名称:env参数可传字符串或数组(如"dev"、["dev", "test"]),见 [Attribute/Route.php。RequestContext构造函数新增$parameters参数:可在构造时直接注入自定义参数(RequestContext.php),配合getParameter()/setParameter()/hasParameter()使用。- 支持用 PHP 数组声明路由,形状与对应 YAML 文件一致;配套新增
RoutesReference(Loader/Configurator/RoutesReference.php),它以@psalm-type的形式为RouteConfig、ImportConfig、AliasConfig、RoutesConfig提供完整的数组形状文档。ImportConfig的字段即 import 键的权威清单:resource、type、exclude、prefix、name_prefix、trailing_slash_on_root、controller、methods、requirements、defaults、options、host、schemes、condition、add_condition、locale、format、utf8、stateless、firewall。
7.3:属性路由的别名与弃用声明
#<a href="https://link.gitcode.com/i/7b8658a0cecec94d685469afa07ddb28" target="_blank">Route] 属性支持 alias 参数(字符串、DeprecatedAlias 或二者的数组,见 [Attribute/Route.php)与 deprecated 声明。DeprecatedAlias 类位于 Attribute/DeprecatedAlias.php,用于为迁移中的路由名提供兼容别名并标记弃用。同时新增 Requirement::MONGODB_ID(<a href="https://link.gitcode.com/i/6622de70ed1432027bd34b787bb6781d" target="_blank">0-9a-f]{24}),用于校验十六进制格式的 MongoDB ObjectID([Requirement/Requirement.php)。
7.2:UID_RFC9562 常量
新增 Requirement::UID_RFC9562,校验 RFC 9562 格式的 UUID。实现上它直接复用 UID_RFC4122 的正则 <a href="https://link.gitcode.com/i/c86f9876c0c77f989d2e8f439be56fd6" target="_blank">0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}([Requirement/Requirement.php),并开始弃用 AttributeClassLoader::$routeAnnotationClass 属性。
7.1:{foo:bar} 参数映射语法
新增 {foo:bar} 语法,用于定义路由参数与其对应请求属性之间的映射。这条语法的解析逻辑位于 Route::extractInlineDefaultsAndRequirements()(Route.php):模式 {name:arg.path?} 中冒号后是控制器参数名、可选 .property 是属性路径,解析结果写入 _route_mapping 默认值。
7.0:彻底移除 Doctrine 注解
- 移除 Doctrine annotations 支持,全面改用原生属性;
AnnotationClassLoader→AttributeClassLoader、AnnotationDirectoryLoader→AttributeDirectoryLoader、AnnotationFileLoader→AttributeFileLoader(同名类在 6.4 起已加入、6.4 起弃用,7.0 删除);UrlMatcher::handleRouteRequirements()新增$routeParameters参数(对应 6.1 起"不传第四个参数"的弃用)。
6.x:Requirement 常量库与条件表达式增强
6.2:通用路由约束常量库
6.2 是约束体系的大版本:新增 Requirement 枚举类,提供一组通用正则表达式常量,可直接用作路由参数 requirement。当前仓库 Requirement/Requirement.php 收录的常量完整清单如下:
| 常量 | 正则 | 用途 |
|---|---|---|
ASCII_SLUG |
[A-Za-z0-9]+(?:-[A-Za-z0-9]+)* |
ASCII slug(对应 symfony/string AsciiSlugger 默认实现) |
CATCH_ALL |
.+ |
通配任意内容 |
DATE_YMD |
`[0-9]{4}-(?:0[1-9] | 1[012])-(?:0[1-9] |
DIGITS |
[0-9]+ |
纯数字 |
MONGODB_ID |
[0-9a-f]{24} |
MongoDB ObjectID(7.3 加入) |
POSITIVE_INT |
[1-9][0-9]* |
正整数(常见的 id 与分页参数) |
UID_BASE32 |
[0-9A-HJKMNP-TV-Z]{26} |
32 进制 UID |
UID_BASE58 |
[1-9A-HJ-NP-Za-km-z]{22} |
58 进制 UID |
UID_RFC4122 |
[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12} |
RFC 4122 UUID |
UID_RFC9562 |
同 UID_RFC4122 |
RFC 9562 UUID(7.2 加入) |
ULID |
[0-7][0-9A-HJKMNP-TV-Z]{25} |
ULID |
UUID |
[0-9a-f]{8}-[0-9a-f]{4}-[13-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12} |
通用 UUID(含 v1/v3/v4/v5) |
UUID_V1~UUID_V8 |
各版本专属正则 | 按版本区分的 UUID |
配套新增 EnumRequirement(Requirement/EnumRequirement.php),用于从 \BackedEnum 自动生成路由 requirement。构造函数接受枚举类名或枚举 case 列表,内部通过 preg_quote 转义每个 case 的 value 后用 | 连接;传入非 BackedEnum 类或混入不同枚举的 case 会抛 InvalidArgumentException。典型用法:
use Symfony\Component\Routing\Requirement\EnumRequirement;
$route = new Route('/article/{status}', [], ['status' => new EnumRequirement(ArticleStatus::class)]);
6.2 还带来:MissingMandatoryParametersException 新增 getMissingParameters() 与 getRouteName() 方法;允许 UTF-8 参数名;注解加载器支持 attribute 类型(annotation 的别名);生成 URL 时已编码的斜杠不再被解码或二次编码;条件表达式新增 params 变量。
6.4:FQCN 别名与表达式语言提供者
- 路由从属性/注解加载时,在适用情况下自动生成 FQCN 与
FQCN::method别名,便于以类名直接引用路由; AnnotationClassLoader::setResolver()增加原生返回类型;- 加入
Annotation命名空间下所有类到Attribute的别名(7.4 起弃用、8.0 移除); AddExpressionLanguageProvidersPass从FrameworkBundle移入组件(见 DependencyInjection/AddExpressionLanguageProvidersPass.php)。
6.1:Requirement 库的开端
Requirement 与 EnumRequirement 在 6.1 首次登场;Requirement::POSITIVE_INT 则在 6.2 加入。6.1 还支持在注解加载器中声明 attribute 类型,并弃用不传 $routeParameters 给 handleRouteRequirements() 的用法。
5.x:属性路由的开端与配置器革命
5.2:#[Route] 属性与内联约束
- 新增
#<a href="https://link.gitcode.com/i/438a39f2056afe5648edc6a724b9ad36" target="_blank">Route]属性支持([Attribute/Route.php),这是路由声明方式的重大转折点。当前该属性支持path(字符串或按 locale 的数组)、name、requirements、options、defaults、host、methods、schemes、condition、priority、locale、format、utf8、stateless、firewall、env、alias等全部参数,且标记为IS_REPEATABLE | TARGET_CLASS | TARGET_METHOD,可在同一目标上重复声明多条路由; - 支持
\A和\z作为路由 requirement 的正则起始/结束锚点,对应Route::sanitizeRequirement()中剥离^/\A前缀与$/\z后缀的逻辑(Route.php); - 支持 host 的内联 requirement 与 defaults 定义,即
{subdomain}.example.com形式的 host 模式同样可内联约束与默认值。
5.1:Configurator 配置器与优先级体系
- 新增
PhpFileLoader::callConfigurator()受保护方法,作为自定义路由配置的扩展点; - 弃用
RouteCollectionBuilder,改用RoutingConfigurator配置器体系——后者成为 PHP 配置的主流入口,配套CollectionConfigurator、RouteConfigurator、AliasConfigurator、ImportConfigurator等类(见 Loader/Configurator/); - 属性路由新增
priority选项,RouteCollection::add()增加$priority参数——优先级决定了同路径多条路由的匹配先后; - 弃用
RouteCompiler::REGEX_DELIMITER常量; - 新增
ExpressionLanguageProvider(Matcher/ExpressionLanguageProvider.php),为路由条件表达式暴露额外函数; - 支持
stateless关键字:PHP、YAML、XML 配置中均可将路由声明为无状态(_stateless默认值); - 新增
hosts选项,可为每个 locale 单独配置 host; - 新增
RequestContext::fromUri(),便于从 URI 构建默认上下文(RequestContext.php,内部通过parse_url解析 scheme/host/port/path)。
5.3:按环境配置与已编码斜杠
- 已编码斜杠在生成 URL 时不再被解码或二次编码(针对路径段;6.1 扩展至查询参数);
- XML 与 YAML 加载器支持按环境(per-env)配置;
- 新增
RoutingConfigurator::env()获取当前环境; - 弃用通过参数数组创建
Route注解类实例的方式。
4.x:编译型匹配/生成架构与 UTF-8 规范化
4.3:CompiledUrlMatcher / CompiledUrlGenerator 诞生
4.3 引入了组件至今仍在使用的编译型架构核心:
CompiledUrlMatcher与CompiledUrlMatcherDumper(Matcher/Dumper/):CompiledUrlMatcherDumper把RouteCollection编译为可直接include的 PHP 类,CompiledUrlMatcherTrait提供高性能的编译后匹配逻辑;CompiledUrlGenerator与CompiledUrlGeneratorDumper(Generator/Dumper/):同样把 URL 生成逻辑编译为 PHP 类,CompiledUrlGeneratorDumper生成的类可被CompiledUrlGenerator直接使用;- 相应地弃用
PhpGeneratorDumper、PhpMatcherDumper以及generator_base_class、generator_cache_class、matcher_base_class、matcher_cache_class等 router 选项(5.0 正式移除); Route与CompiledRoute的Serializable实现方法被标记为@internal、@final,建议改用__serialize/__unserialize扩展点(Route.php 即使用这两个方法);- 暴露
utf8Route 选项;配置加载器与配置器中暴露默认的locale、format; - 支持可调用(invokable)服务路由加载器。
4.4:资源导入的 exclude 与加载器迁移
import()方法支持通过exclude模式排除导入资源;- 弃用
ServiceRouterLoader(改用ContainerLoader)与ObjectRouteLoader(改用ObjectLoader)。
4.2 与 4.0:locale 回退与 UTF-8 强制
- 4.2:国际化路由新增对无文化后缀 locale(cultureless locale)的回退;
- 4.0(BC Break):不使用
utf8选项就不能再使用 UTF-8 路由模式与 UTF-8 requirement,即 UTF-8 能力必须显式开启。
3.x:异常体系与配置关键字完善
3.4:新异常与配置能力
- 新增
NoConfigurationException(Exception/NoConfigurationException.php); - 支持通过
@Route(name="prefix_")为控制器下所有路由定义前缀; - 支持带优先级的路由加载器(prioritized routing loaders);
- 将匹配参数与默认参数加入重定向响应;
- YAML/XML 配置支持
controller关键字。
3.3 与 3.2:参数弃用与类型化默认值
- 3.3:弃用一批 class 参数:
router.options.generator_class、generator_base_class、generator_dumper_class、matcher_class、matcher_base_class、matcher_dumper_class、matcher.cache_class、generator.cache_class(4.0 移除); - 3.2:XML 配置支持
bool、int、float、string、list、map类型的默认值;支持 UTF-8 requirement。
2.x:从树形集合到扁平集合的架构转折
2.x 是组件奠基并发生两次重大 BC Break 的阶段,CHANGELOG 为此给出了完整迁移代码,是升级老项目最值得细读的部分。
2.2:pattern → path,_scheme/_method → schemes/methods
路由设置多项重命名(旧名将在 3.0 移除):
pattern弃用,改用path;_scheme、_methodrequirement 迁移为独立的schemes、methods设置。
YAML 迁移对照:
# Before
article_edit:
pattern: /article/{id}
requirements: { '_method': 'POST|PUT', '_scheme': 'https', 'id': '\d+' }
# After
article_edit:
path: /article/{id}
methods: [POST, PUT]
schemes: https
requirements: { 'id': '\d+' }
XML 迁移对照:
<!-- Before -->
<route id="article_edit" pattern="/article/{id}">
<requirement key="_method">POST|PUT</requirement>
<requirement key="_scheme">https</requirement>
<requirement key="id">\d+</requirement>
</route>
<!-- After -->
<route id="article_edit" pattern="/article/{id}" methods="POST PUT" schemes="https">
<requirement key="id">\d+</requirement>
</route>
PHP 迁移对照:
// Before
$route = new Route();
$route->setPattern('/article/{id}');
$route->setRequirement('_method', 'POST|PUT');
$route->setRequirement('_scheme', 'https');
// After
$route = new Route();
$route->setPath('/article/{id}');
$route->setMethods(['POST', 'PUT']);
$route->setSchemes('https');
2.2:RouteCollection 扁平化 BC Break
RouteCollection 不再以树形结构工作,而是扁平的路由数组。用 PHP 构建集合时必须先向子集合添加路由、再将其加入父集合(YAML/XML 不受影响)。这是本次 BC Break 中影响面最大的一处:
// Before(错误顺序:先挂载子集合,再往里加路由)
$rootCollection = new RouteCollection();
$subCollection = new RouteCollection();
$rootCollection->addCollection($subCollection);
$subCollection->add('foo', new Route('/foo'));
// After(正确顺序:先填充子集合,再挂载)
$rootCollection = new RouteCollection();
$subCollection = new RouteCollection();
$subCollection->add('foo', new Route('/foo'));
$rootCollection->addCollection($subCollection);
并且必须自底向上逐层 addCollection:
$childCollection->addCollection($grandchildCollection);
$rootCollection->addCollection($childCollection);
同版本的其余变更:
- 弃用
RouteCollection::getParent()、getRoot()(2.3 移除树概念); - 空前缀调用
addPrefix不再允许:addPrefix('', $defaultsArray, $requirementsArray, $optionsArray)这类用法必须改用addDefaults()、addRequirements()、addOptions(); - 弃用
addPrefix()的$options参数与getPrefix()方法(前缀优化改由 dumper 在匹配时完成,且 2.2 中进一步增强了分组能力); addCollection()只应传单个参数,其余参数($prefix、$default、$requirements、$options)弃用,改用addPrefix():
// Before
$parentCollection->addCollection($collection, '/prefix', [...], [...]);
// After
$collection->addPrefix('/prefix', [...], [...]);
$parentCollection->addCollection($collection);
@Route支持方法默认参数值;相邻占位符无需分隔符(如/{x}{y}{z}.{_format});占位符间起分隔作用的字符改为白名单机制,修复/prefix{var}suffix这类文本环绕变量的路由;- 变量默认 requirement 微调:原先禁止变量前后字符,现改为只禁止
/与其后字符——旧规则下/index.{_format}会被/index.ht/ml匹配到;默认 requirement 改用占有型量词(possessive quantifiers),避免不必要的回溯,匹配性能提升最高约 20%; ConfigurableRequirementsInterface::setStrictRequirements(null)可完全关闭生成 URL 时的 requirement 校验,生产环境建议这样配置(参数必然满足约束,否则链接本身就坏了);- 路由名不再受限,非字母数字字符同样允许;
RouteCompilerInterface::compile(Route $route)改为静态(仅影响自定义 RouteCompiler 的实现者);UrlGenerator支持生成相对路径与网络路径(如../parent-file、//example.com/dir/file)。generate()第三个参数$referenceType推荐使用UrlGeneratorInterface常量,布尔参数旧写法仍兼容。
2.8:referenceType 常量与资源加载扩展
- 弃用
$referenceType参数的硬编码布尔值,改用UrlGeneratorInterface常量:
// Before
$router->generate('blog_show', ['slug' => 'my-blog-post'], true);
// After
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
$router->generate('blog_show', ['slug' => 'my-blog-post'], UrlGeneratorInterface::ABSOLUTE_URL);
- 支持指定目录并递归加载其中全部路由配置文件;
- 新增
ObjectRouteLoader与ServiceRouteLoader,允许通过调用对象/服务上的方法加载路由(4.4 分别被ObjectLoader/ContainerLoader取代)。
2.1 与 2.5、2.3:奠基与清理
- 2.1:新增
RequestMatcherInterface、RequestContext::fromRequest()(RequestContext.php,从 HttpFoundation 的Request同步 base URL、path info、method、host、scheme、端口与查询串);UrlMatcher在 scheme 不符时不再抛\LogicException;新增TraceableUrlMatcher;prefix(含导入路由)支持为占位符定义 options、默认值与 requirements;新增RouterInterface::getRouteCollection;UrlMatcher对路由参数只做一次 URL 解码(此前解码两次),并用rawurldecode()支持输入路径中的+;RouteCollection::remove()也会从父集合移除路由;新增ConfigurableRequirementsInterface,允许在生成无效参数值的 URL 时禁用异常(改为生成空 URL)。 - 2.3:新增
RequestContext::getQueryString()。 - 2.5:弃用
ApacheMatcherDumper与ApacheUrlMatcher(性能收益有限且难以复刻 PHP 实现行为,3.0 移除)。
升级速查:破坏性变更一览
| 版本 | 变更 | 迁移动作 |
|---|---|---|
| 8.0 | 移除 XML 配置格式 | 改用 YAML / PHP / 属性 |
| 8.0 | 移除 Annotation 命名空间别名 |
改用 Attribute 命名空间 |
| 8.0 | 移除属性类 getter/setter | 直接使用公共属性 |
| 8.0 | 移除 $routeAnnotationClass 属性 |
用 setRouteAttributeClass() |
| 8.0 | _query 非数组抛异常 |
保证 _query 传数组 |
| 7.0 | 移除 Doctrine 注解 | 全部改用原生属性 |
| 5.0 | 移除 PhpGeneratorDumper/PhpMatcherDumper 与相关 router 选项 |
使用编译型 dumper |
| 4.0 | UTF-8 路由必须显式 utf8 选项 |
涉及 UTF-8 时开启该选项 |
| 2.2 | pattern → path,_scheme/_method → schemes/methods |
按上文对照表迁移 |
| 2.2 | RouteCollection 树→扁平 |
自底向上填充子集合后再挂载 |
| 2.2 | addPrefix 空前缀用法移除 |
用 addDefaults/addRequirements/addOptions |
结语
从 CHANGELOG 可以清晰地看到 Symfony Routing 组件十余年的设计走向:声明方式从 XML 走向 YAML/PHP/属性,匹配与生成从解释执行走向编译型 PHP 类,集成方式从 FrameworkBundle 内置走向独立的 RouterBundle,同时通过 Requirement 常量库、EnumRequirement、条件表达式与 runWith() 等机制不断降低路由配置的复杂度。对使用者而言,当前版本(8.x)应优先采用属性路由 + RouterBundle 配置 + 编译型匹配器/生成器的组合;对升级者而言,本文的破坏性变更速查表与 2.x 迁移对照代码,可作为逐版本升级的最小检查清单。