PHP中间件架构与管道模式实现
中间件是PHP框架的核心概念。请求经过一层层中间件处理,每层都可以决定放行还是拦截。今天说说中间件架构的原理和实现。
中间件的洋葱模型。请求从外到内经过每层中间件,响应从内到外再经过每层中间件。
```php
interface Middleware
{
public function handle(Request $request, callable $next): Response;
}
class Request
{
private array $attributes = [];
public function __construct(
private string $method = 'GET',
private string $uri = '/'
) {}
public function getMethod(): string { return $this->method; }
public function getUri(): string { return $this->uri; }
public function setAttribute(string $name, mixed $value): void
{
$this->attributes[$name] = $value;
}
public function getAttribute(string $name, mixed $default = null): mixed
{
return $this->attributes[$name] ?? $default;
}
}
class Response
{
public function __construct(
private string $body = '',
private int $statusCode = 200,
private array $headers = []
) {}
public function getBody(): string { return $this->body; }
public function getStatusCode(): int { return $this->statusCode; }
}
?>
具体的中间件实现。
```php
class AuthMiddleware implements Middleware
{
public function handle(Request $request, callable $next): Response
{
$token = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
if (empty($token)) {
return new Response('未授权', 401);
}
$request->setAttribute('user_id', 123);
return $next($request);
}
}
class LoggingMiddleware implements Middleware
{
public function handle(Request $request, callable $next): Response
{
$start = microtime(true);
$response = $next($request);
$duration = (microtime(true) - $start) * 1000;
error_log("{$request->getMethod()} {$request->getUri()} - {$duration}ms");
return $response;
}
}
class CorsMiddleware implements Middleware
{
public function handle(Request $request, callable $next): Response
{
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
if ($request->getMethod() === 'OPTIONS') {
return new Response('', 204);
}
return $next($request);
}
}
class RateLimitMiddleware implements Middleware
{
private int $maxRequests;
private array $counts = [];
public function __construct(int $maxRequests = 60)
{
$this->maxRequests = $maxRequests;
}
public function handle(Request $request, callable $next): Response
{
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
$this->counts[$ip] = ($this->counts[$ip] ?? 0) + 1;
if ($this->counts[$ip] > $this->maxRequests) {
return new Response('请求过多', 429);
}
return $next($request);
}
}
?>
中间件管道把多个中间件串联起来。
```php
class MiddlewarePipeline
{
private array $middlewares = [];
public function add(Middleware $middleware): self
{
$this->middlewares[] = $middleware;
return $this;
}
public function run(Request $request, callable $coreHandler): Response
{
$pipeline = $coreHandler;
foreach (array_reverse($this->middlewares) as $middleware) {
$pipeline = function (Request $request) use ($middleware, $pipeline) {
return $middleware->handle($request, $pipeline);
};
}
return $pipeline($request);
}
}
$pipeline = new MiddlewarePipeline();
$pipeline->add(new CorsMiddleware());
$pipeline->add(new LoggingMiddleware());
$pipeline->add(new AuthMiddleware());
$pipeline->add(new RateLimitMiddleware(100));
$response = $pipeline->run(new Request('GET', '/api/users'), function ($request) {
return new Response("Hello User #{$request->getAttribute('user_id')}");
});
echo $response->getBody() . "\n";
echo "状态码: {$response->getStatusCode()}\n";
?>
中间件让应用的功能模块化。每个中间件只负责自己的那点事,职责单一。想加缓存就加个CacheMiddleware,想加验证就加个ValidationMiddleware。这种架构让应用的扩展变得很方便。
PHP中间件架构与管道模式实现
张小明
前端开发工程师
从2G到5G:你的SIM卡文件系统是如何“膨胀”的?一份USIM文件结构演进史
从2G到5G:SIM卡文件系统的进化密码 当我们每天使用手机通话、上网时,很少有人会注意到那张小小的SIM卡内部正在发生的技术革命。从最初的2G时代仅存储基本鉴权信息,到今天5G时代承载着复杂的安全协议和网络配置,SIM卡文件系统的演…
Horos开源医学影像查看器:macOS上免费的DICOM处理终极指南
Horos开源医学影像查看器:macOS上免费的DICOM处理终极指南 【免费下载链接】horos Horos™ is a free, open source medical image viewer. The goal of the Horos Project is to develop a fully functional, 64-bit medical image viewer for OS X. Horos is base…
STC12到STC15单片机硬件迁移:引脚不兼容的挑战与解决方案
1. 项目概述:从STC12到STC15的“惊喜”与挑战作为一名常年泡在实验室和万用板上的硬件工程师,我对STC单片机系列一直抱有复杂的情感。它们价格亲民、资料丰富,是很多学生和电子爱好者入门、甚至小批量产品开发的“老朋友”。我手头的主力型号…
LinkSwift:打破网盘下载限制的浏览器脚本终极解决方案
LinkSwift:打破网盘下载限制的浏览器脚本终极解决方案 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼…
湖北碳价CEEMDAN分解MATLAB工具包:含完整代码、实测数据与IMF分量结果
本文还有配套的精品资源,点击获取 简介:一套开箱即用的CEEMDAN信号分解MATLAB实现,内置SAM_CEEMDAN.m核心算法及配套预处理(SAM_EMD.m)、后处理函数(AAPE.m、DispEn.m、DiffSymEn.m、SPT_ST.m࿰…
Android Studio 突然报 Duplicate class 别慌!用 gradlew dependencies 揪出真凶(以 TinyPinyin 为例)
Android Studio报Duplicate class错误?用gradlew dependencies精准定位依赖冲突正在愉快编码时,Android Studio突然抛出Duplicate class错误,而最近明明没有新增任何依赖——这种"灵异事件"几乎每个Android开发者都遇到过。上周三晚…