Skip to content

v1.2.0

Latest

Choose a tag to compare

@xyj2156 xyj2156 released this 14 Sep 13:43
f3fa70d

未命名路由不再静默消失,严格模式一次报全「缺名字 / 缺层级」两类问题。

根因:RouteAnalyzerRouteRepository 都在 resolve() 之前把 name 为空的路由跳过,且告警条件只看原始显式 tier——于是「靠 config match 规则或 classifier 回调归入层级却没写名字」的路由既不进元信息、也不进 list 行、更不进 tier_counts,一条信号都不留;SPEC 承诺的 strict_mode 500(RF_BE_005)因此在生产里从不触发,是死代码。

修复

  • 未命名路由不再静默消失:现逐条探测层级归属并全量收集,被层级规则命中的未命名路由按层级分组进入 warnings(含 URI、HTTP 方法、中间件与命中来源,可直接 grep 定位)。
  • 历史文案兼容点:「显式 ->tier() 无名」的整句 Route (uri) has tier [x] but no route name assigned; … 原样保留(方法/中间件追加为句尾标签),适配包 Feature 测试与 CI 脚本的 substring / grep 断言不受影响。
  • RF_BE_005 不再是死代码:严格模式的「有层级归属却无路由名」由 StrictViolationScanner 在取数入口做全量预扫描接管(见下方「变更」)。

变更

破坏性提示:以下两项改变 strict_mode=true 项目的对外错误形态,适配包需按新文案同步 Feature 测试与 SPEC §3.2 / §6.1。

  • HTTP 端点strict_mode=true 时,任一读路径(层级端点 / 摘要 / 全量 / unassigned)取数前先全量扫描,发现「有层级归属却无路由名」或「命名路由未归级」即抛新错误码 RF_BE_009RouteStrictViolationException,HTTP 500),message 分组列出两类全量清单,violations() 另给结构化清单供页面高亮定位。取代原先「第一条 RF_BE_001 / RF_BE_005 就中断」的修一条刷一条模式;RF_BE_001 / RF_BE_005 两个异常类与错误码保留(TierResolver::resolve() 的单条语义不变)。层级名拼错、classifier 抛错等更精确的配置错误仍走各自错误码(RF_BE_002 / 004 / 006),只在聚合消息里作信息附录。
  • 命令行route:forge:list / typesstrict_mode=true 下不再因未归级路由直接以退出码 1 中断(命令本身在排查场景必须还能跑),改为经 analyze()['violations'] 全量输出「缺名字 / 缺层级 / 无法判定」三组,由命令层统一渲染成红色清单并决定退出码。宽松模式输出形态完全不变:表格、tier_counts--json 契约一字未动。
  • 未命名且不命中任何层级的路由不算违规:它压根不在 forge 管辖范围内(否则 vendor 与框架内部路由会把严格模式报错刷满)。

新增

  • Support\StrictViolationScanner:严格模式违规的唯一判定与措辞来源,scan() 返回 {missing_name, unassigned, unresolved}(按实例记忆,一次请求只扫一遍),count() 明确排除 unresolvedformat() 生成 HTTP 错误体与命令行红色清单共用的清单文本。
  • Exception\RouteStrictViolationExceptionRF_BE_009,500):fromViolations() 构造、violations() 输出结构化清单。
  • RouteAnalyzer::analyze() 输出新增 violations 键(仅严格模式非空);RouteRepositoryinfos() 入口挂预扫描。
  • Support\HttpMethods::withoutHead():HEAD 过滤的单点实现,RouteAnalyzer::withoutHead() 保留为委托入口,公开签名不变。
  • TierResolver::probe(RouteInfo):与 resolve() 共享同一套优先级求值(抽出 classifierTier() / matchedTier() 私有 helper 防口径漂移)的只读探测,返回 {level, source, requested, error}source ∈ explicit | classifier | match | none | explicit-unknown-level | classifier-unknown-tier | classifier-error。与 resolve() 的一致性由单测钉住。
  • TierResolver::configuredLevels() / isStrict():按配置顺序返回层级名、暴露严格模式开关,命令层与分析器不再各自读配置。
  • RouteAnalyzer::unnamedWarnings() / RouteAnalyzer::formatUnnamed():未命名路由的告警文本与 --unnamed 分组视图,文案在 common 单点生成,两个适配包不再各写一份。
  • RouteNameFilter 新增按 URI 前缀排除的第二维度(isUriExcluded() / 不可变 withUriPrefixes()):未命名路由没有名字可判,而 forge 自身的层级端点(如 _forge/routes/managemanage 中间件)会被 match 规则命中,不排除就是包把自己报成配置错误。URI 按段匹配,_forge/routes 不误伤 _forge/routeship

测试

  • 全量 109 tests / 291 assertions 通过(发布前 78 例 → 新增 31 例),CI 矩阵 PHP 8.2 / 8.3 双绿。
  • 新增 UnnamedRouteVisibilityTest(未命名路由归类、聚合文案、strict 不新增失败模式、计数口径、URI 排除、漂移守卫)与 StrictViolationScanTest(扫描器三分、RF_BE_009 聚合抛出、宽松静默、精确错误码不被冒名)。

版本

新增公开 API + 严格模式对外错误码变化(RF_BE_001/005RF_BE_009),宽松模式零变化,按 minor 发 1.2.0schemeVersion 保持 1。两个适配包(php-laravel / php-thinkphp)的 --unnamed 选项、红色 violations 渲染与 withUriPrefixes() 接线,以及 .docs/SPEC.md §3.1.4 / §3.2 / §6.1 的同步尚未跟进。

1.1.2...1.2.0 · 完整变更见 CHANGELOG.md