preflow/routing 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

preflow/routing

Composer 安装命令:

composer require preflow/routing

包简介

Preflow routing — file-based and attribute-based hybrid router

README 文档

README

Hybrid file-based + attribute-based router for Preflow. Implements RouterInterface from preflow/core.

Installation

composer require preflow/routing

Requires PHP 8.4+.

What's included

Component Description
FileRouteScanner Maps app/pages/ directory structure to Component routes
AttributeRouteScanner Scans PHP classes for #[Route], #[Get], #[Post], etc.
Router Combines both scanners, implements RouterInterface
RouteMatcher Matches requests with priority: static > dynamic > catch-all
RouteCompiler Caches the route collection to a PHP file for production

File-based routing

Files in the pages directory map directly to URLs. The scanner handles three file conventions:

File URL pattern
index.twig / (or parent directory path)
about.twig /about
[slug].twig /{slug} — dynamic segment
[...path].twig /{path} — catch-all (matches slashes too)
_layout.twig excluded (underscore prefix)

Example structure:

app/pages/
  index.twig           → GET /
  about.twig           → GET /about
  blog/
    index.twig         → GET /blog
    [slug].twig        → GET /blog/{slug}
  docs/
    [...path].twig     → GET /docs/{path}  (catches /docs/a/b/c)
  _layout.twig         → (ignored)

File routes resolve to RouteMode::Component — the handler value is the relative template path (e.g. blog/[slug].twig).

Attribute-based routing

Controllers use #[Route] on the class for a path prefix, then HTTP method attributes on methods. These resolve to RouteMode::Action with the handler ClassName@methodName.

use Preflow\Routing\Attributes\Delete;
use Preflow\Routing\Attributes\Get;
use Preflow\Routing\Attributes\Middleware;
use Preflow\Routing\Attributes\Post;
use Preflow\Routing\Attributes\Put;
use Preflow\Routing\Attributes\Route;

#[Route('/api/posts')]
#[Middleware(ApiAuthMiddleware::class)]
final class PostController
{
    #[Get('/')]
    public function index(): ResponseInterface { /* ... */ }

    #[Get('/{id}')]
    public function show(): ResponseInterface { /* ... */ }

    #[Post('/')]
    public function create(): ResponseInterface { /* ... */ }

    #[Put('/{id}')]
    #[Middleware(OwnerMiddleware::class)]   // stacks on top of class middleware
    public function update(): ResponseInterface { /* ... */ }

    #[Delete('/{id}')]
    public function destroy(): ResponseInterface { /* ... */ }
}

Method paths are appended to the class prefix: #[Get('/{id}')] on #[Route('/api/posts')] becomes /api/posts/{id}. A method path of '/' resolves to the prefix alone.

#[Middleware] is repeatable on both class and method. Method middleware merges on top of class middleware.

Catch-all params in attribute routes

Append ... inside the braces to create a catch-all segment that matches slashes. Useful for nested path parameters in controllers.

#[Route('/files')]
final class FileController
{
    #[Get('/{path...}')]
    public function show(ServerRequestInterface $request): ResponseInterface
    {
        $path = $request->getAttribute('path'); // e.g. 'images/2026/photo.jpg'
        // ...
    }
}

{path...} compiles to a .+ regex, matching one or more characters including /.

Router setup

use Preflow\Routing\Router;

$router = new Router(
    pagesDir:    __DIR__ . '/app/pages',      // file-based routes (optional)
    controllers: [PostController::class],      // attribute-based routes (optional)
    cachePath:   __DIR__ . '/storage/routes.php', // null = no cache
);

// Pass to Application
$app->setRouter($router);

Either pagesDir or controllers (or both) can be omitted. When cachePath is set and the cache file exists, the scanner is bypassed entirely.

Route cache (production)

use Preflow\Routing\RouteCompiler;

$compiler = new RouteCompiler();

// Generate cache
$compiler->compile($router->getCollection(), __DIR__ . '/storage/routes.php');

// Invalidate
$compiler->clear(__DIR__ . '/storage/routes.php');

The compiled file is a plain PHP return statement — no eval, OPcache-friendly.

Matching priority

For a given HTTP method, RouteMatcher tests routes in three passes:

  1. Static — exact string match, no parameters
  2. Dynamic — has {param} segments, matched by regex
  3. Catch-all — has {...param}, matches across slashes

The first match wins. Throws NotFoundHttpException if nothing matches.

preflow/routing 适用场景与选型建议

preflow/routing 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 44 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 04 月 10 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 preflow/routing 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 preflow/routing 我们能提供哪些服务?
定制开发 / 二次开发

基于 preflow/routing 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 44
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 25
  • 依赖项目数: 3
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-04-10