# http **Repository Path**: fiberphp/http ## Basic Information - **Project Name**: http - **Description**: 🌐 FiberPHP HTTP 组件 —— PSR-7/PSR-15 兼容的请求/响应封装,支持中间件、路由分发,基于 Workerman 高性能 HTTP 服务。 - **Primary Language**: PHP - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-22 - **Last Updated**: 2026-09-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # FiberPHP HTTP FiberPHP 框架的 HTTP 子系统。基于 Workerman 协议层实现请求/响应/中间件,集成 Pipeline 协程管道与 Context 协程隔离,每个请求在独立 Fiber 中执行,中间件内可安全挂起做异步 I/O。 ## 特性 - **协程隔离**:每个请求 `startScope` 开启独立 Context,`endScope` 必清理,防跨请求泄漏 - **Pipeline 管道**:全局中间件按声明顺序串接,支持 `Fiber::suspend()` 挂起/恢复 - **中间件双源**:`config/http.php` 显式声明 + `#[Package(middleware: [...])]` 子包声明,自动合并去重 - **异常兜底**:`Handler` 优先 `App\ExceptionHandler`,`renderHttp()` 按方法约定(`getHttpCode()` / `isMessageSafe()` / `getErrors()`)分层渲染,不依赖具体异常类 - **响应策略**:keep-alive / chunked / close 自动判定,支持 If-Modified-Since 304 协商缓存 - **URI 安全**:拦截路径穿越(`/../`、`\`、`\0`)、空路径、反斜杠 - **辅助函数**:`request()` / `input()` / `response()` / `json()` / `xml()` / `redirect()` / `errcode()` / `route()` - **统一路由表**:约定式扫描与显式注册(注解/路由文件)合并为一张表,统一分发、DI 与中间件管线 ## 环境要求 - PHP >= 8.3 - `ext-json` - `workerman/workerman` ^5.1 - 依赖 `fiberphp/framework`(含包发现机制与 `#[Package]` 特性)、`psr/log` ## 安装 ```bash composer require fiberphp/http ``` 安装后通过 Composer 钩子自动注册。`config/http.php` 作为包默认配置自动合并(装包即用,应用在自己的 `config/http.php` 放同名键覆盖);`config/process/http.php` 属进程配置,由安装钩子幂等发布到应用 `config/process/`。 ## 配置 ### config/http.php — 中间件 ```php return [ 'middleware' => [ // 全局中间件类列表(对所有请求生效,按声明顺序串接) 'global' => [ // \App\Middleware\Cors::class, // \App\Middleware\Auth::class, ], // 中间件别名映射(路由/控制器中间件字符串先查别名再当类名) 'aliases' => [], ], ]; ``` 中间件类需实现 `FiberPHP\Http\Contract\MiddlewareInterface` 的 `handle(Request, Closure): Response` 方法。 ### config/process/http.php — Worker 进程 ```php use FiberPHP\Http\HttpWorkerHandler; use FiberPHP\Http\Request; // 监听地址与 Worker 进程数支持环境变量覆盖(.env): // SERVER_LISTEN=http://0.0.0.0:8080 SERVER_COUNT=auto(auto=按 CPU 核心数) $listen = (string) env('SERVER_LISTEN', 'http://0.0.0.0:8787'); $count = env('SERVER_COUNT', 'auto'); return [ 'http' => [ 'enable' => true, 'handler' => HttpWorkerHandler::class, 'listen' => $listen, 'count' => $count === 'auto' ? max(1, cpu_count()) : (int) $count, 'constructor' => [ 'requestClass' => Request::class, ], ], ]; ``` ## 使用 ### 请求参数 ```php // 获取全部参数 $params = request()->all(); // 单个参数(先 GET 后 POST,缺失返回默认值) $id = request()->input('id', 0); // 仅取 / 排除指定键 $data = request()->only(['name', 'email']); $data = request()->except(['password']); ``` ### 响应构造 ```php // 纯文本 return response('hello'); // JSON return json(['code' => 0, 'data' => $list]); // XML return xml($xmlElement); // 重定向 return redirect('/login'); // 链式调用 return response('OK') ->withHeaders(['X-Trace-Id' => $traceId]) ->withStatus(200); ``` ### 文件响应 ```php // 静态文件(命中 If-Modified-Since 返回 304) return response()->file('/path/to/file.txt'); ``` ### 上传文件 ```php $file = request()->file('avatar'); if ($file->isValid()) { $tmpPath = $file->getFileName(); // 临时文件路径 $originalName = $file->getUploadName(); // 客户端原始文件名 $extension = $file->getUploadExtension(); // 扩展名 // 业务侧自行处理 move_uploaded_file / SplFileObject } ``` ### 客户端 IP ```php // 直连 IP $ip = request()->getRemoteIp(); // 真实 IP(safeMode 下仅信任内网代理头) $ip = request()->getRealIp(); // safeMode=true(默认) $ip = request()->getRealIp(false); // 信任所有代理头 ``` ### 请求判定 ```php request()->isGet(); request()->isPost(); request()->isAjax(); request()->isPjax(); request()->expectsJson(); // AJAX 非 PJAX 或 Accept: json ``` ### 中间件 ```php namespace App\Middleware; use Closure; use FiberPHP\Http\Contract\MiddlewareInterface; use FiberPHP\Http\Request; use FiberPHP\Http\Response; class Auth implements MiddlewareInterface { public function handle(Request $request, Closure $next): Response { $token = $request->header('Authorization'); if (!$token) { return json(['code' => 401, 'msg' => 'Unauthorized'], 401); } return $next($request); // 传递给下一层 } } ``` 注册到 `config/http.php`: ```php 'middleware' => [ \App\Middleware\Auth::class, ], ``` ### 路由 handler `HttpProvider` 默认将 `RequestHandlerInterface` 绑定到内置 `Router`(统一路由表)。可在应用 Provider 中覆盖绑定实现自定义调度: ```php // App\Provider 中 $container->singleton(RequestHandlerInterface::class, MyHandler::class); ``` 安装 `fiberphp/router` 后,注解与 `route/*.php` 路由文件注册进同一张路由表。 ## 约定式路由 约定式路由始终启用,控制器目录固定为 `app/Controller`: - 文件名映射 URI:`Index.php` → `/index`,`Admin/Role.php` → `/admin/role` - 方法名前缀映射 HTTP 方法:`getList()` → GET `/index/list`,`postCreate()` → POST `/index/create` - 无前缀方法(含 `index`)接受任意 HTTP 方法 - `Index` 控制器作为目录默认控制器:`Index::index()` 同时映射 `/` 和 `/index` - 类级 `#[\FiberPHP\Http\Attribute\NoDefaultRoute]` 注解可关闭该类的约定路由 - 显式路由(注解/路由文件)与同 method+path 的约定条目冲突时,显式条目胜出 ## 异常处理 业务/传输层异常统一使用 `FiberPHP\Http\Exception` 下两个类(继承框架异常基类,消息默认透传): ```php use FiberPHP\Http\Exception\HttpException; use FiberPHP\Http\Exception\NotFoundHttpException; // 404:URL 指向的资源不存在(路由未命中时框架内部也抛这个类) throw new NotFoundHttpException('商品不存在'); // 任意 4xx/5xx:业务规则违反、限流、认证失败等 throw new HttpException(400, '分类编码已存在'); throw new HttpException(429, '请求过于频繁', headers: ['Retry-After' => '60']); // 字段级校验失败由 fiberphp/validate 抛 ValidateException(422,自动携带 errors 明细) ``` | 异常类 | HTTP 状态码 | 说明 | |--------|-------------|------| | `NotFoundHttpException` | 404 | 资源/路由不存在,消息透传 | | `HttpException($code, ...)` | 传入码 | 业务拒绝、限流等,消息透传,headers 随响应下发 | | `ValidateException`(validate 包) | 422 | 字段校验失败,响应体自动携带 `errors` 明细 | | 其他异常 | 500 | 生产环境统一收敛为 `Server Error`,debug 模式透出细节 | ### errcode() 助手 —— 配置驱动的业务错误码 需要稳定业务码(监控聚合、前后端联调)时,用助手按 `config/error.php` 消息字典抛出(HTTP 恒 400): ```php errcode(10401); // 消息查配置,未配置回退 '业务错误' errcode(10401, '分类【水果】已存在'); // 显式覆盖消息 ``` ```php // config/error.php return [ 'codes' => [ 10401 => '分类编码已存在', ], ]; ``` > 约定:`errcode()` 只表达业务拒绝(400);404 用 `NotFoundHttpException`,422 用 validate 校验。 应用可创建 `App\ExceptionHandler` 继承 `FiberPHP\Http\Exception\Handler`,覆盖 `renderHttp()` 实现自定义渲染。 ## License [MIT](LICENSE)