跳到主要内容
我的站点
首页 归档 分类 标签 案例 服务 关于 聊聊需求

用 PHP 8.5 从零构建零依赖 Web 框架:路由与容器

零依赖不是目的,可控才是。当框架的每一行代码你都能读懂,线上排障就不再是“玄学”。 为什么选择零依赖 Composer 生态很繁荣,但也带来三个现实问题: 问题 表现 零依赖的收益 --- --- --- 依赖膨胀 一个博客装进 40+ 个包 部署包只有几百 KB 版本地狱 升级…

用 PHP 8.5 从零构建零依赖 Web 框架:路由与容器 封面

零依赖不是目的,可控才是。当框架的每一行代码你都能读懂,线上排障就不再是“玄学”。

#为什么选择零依赖

Composer 生态很繁荣,但也带来三个现实问题:

问题表现零依赖的收益
依赖膨胀一个博客装进 40+ 个包部署包只有几百 KB
版本地狱升级一个包牵连一片只有自己的代码要维护
排障困难堆栈 90% 在 vendor断点直接落在业务代码

依赖越少,不可控的部分就越少。

#路由:从正则到编译缓存

最朴素的路由就是一个 path => handler 映射表,但动态参数必须编译成正则:

php
public static function get(string $path, mixed $action): RouteItem
{
    $pattern = preg_replace('/\{(\w+)\}/', '(?P<$1>[^/]+)', $path);
    return self::collection()->add(['GET'], $path, $action, $pattern);
}

几个必须处理的细节:

  • 参数默认约束为 [^/]+,需要数字时用 ->where('id', '\d+')
  • 静态路由优先匹配,避免动态路由“吃掉” /posts/new
  • 生产环境把编译结果缓存成 PHP 数组文件

#容器:只做两件事

一个够用的容器只需要支持绑定与自动解析:

php
public function make(string $class): object
{
    if (isset($this->bindings[$class])) {
        return $this->bindings[$class]($this);
    }

    $reflection = new ReflectionClass($class);
    $constructor = $reflection->getConstructor();

    if ($constructor === null) {
        return new $class();
    }

    $arguments = [];
    foreach ($constructor->getParameters() as $parameter) {
        $type = $parameter->getType();
        $arguments[] = $type instanceof ReflectionNamedType && !$type->isBuiltin()
            ? $this->make($type->getName())
            : null;
    }

    return $reflection->newInstanceArgs($arguments);
}

#中间件洋葱模型

中间件用闭包层层包裹,注意反向遍历:

php
foreach (array_reverse($middleware) as $entry) {
    $handler = fn (Request $request): Response => $instance->handle($request, $handler);
}

这样第一个中间件在最外层,既能前置处理,也能后置处理响应。

#小结

  • 路由编译结果要可缓存
  • 容器保持最小实现,够用就好
  • 中间件顺序决定请求生命周期
最后更新: 本文采用 CC BY-NC-SA 4.0 许可,转载请注明出处与作者。
related

相关文章

评论

还没有评论,来说点什么吧。

发表评论

评论需站长审核后展示 · 最多 1000 字