ข้ามไปยังเนื้อหา

ADR-005 - PSR-15 รูปแบบมิดเดิลแวร์

ใช้ PSR-15 HTTP ตัวจัดการคำขอของเซิร์ฟเวอร์ (มิดเดิลแวร์) เพื่อปรับปรุงไปป์ไลน์การประมวลผลคำขอ


เสนอ - อยู่ระหว่างการประเมินสำหรับรุ่น XOOPS 4.0


XOOPS 2.5 ใช้วิธีการจัดการคำขอแบบเสาหิน:

// Current: Sequential processing
require_once 'mainfile.php';
// → Kernel initialization
// → User authentication
// → Module loading
// → Page rendering
// All in one flow, mixed concerns
  1. ข้อกังวลแบบผสม - การรับรองความถูกต้อง การบันทึก และการกำหนดเส้นทางทั้งหมดเกี่ยวพันกัน
  2. ทดสอบได้ยาก - ทดสอบขั้นตอนการประมวลผลคำขอแต่ละรายการได้ยาก
  3. ขยายได้ยาก - โมดูลสามารถเชื่อมต่อได้ผ่านทางพรีโหลด/เหตุการณ์เท่านั้น
  4. การแยกกันไม่ดี - ตรรกะการประมวลผลคำขอกระจัดกระจายไปทั่วโค้ดเบส
  5. ไม่สามารถประกอบได้ - ไม่สามารถเชื่อมโยงหรือเรียงลำดับขั้นตอนการประมวลผลใหม่ได้อย่างง่ายดาย

PSR-15 กำหนดอินเทอร์เฟซมาตรฐานสำหรับมิดเดิลแวร์ HTTP:

<?php
interface RequestHandlerInterface {
public function handle(ServerRequestInterface $request): ResponseInterface;
}
interface MiddlewareInterface {
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface;
}

มิดเดิลแวร์เชน:

Request
[Logger] → logs request
[Auth] → validates user session
[CORS] → checks cross-origin
[Router] → dispatches to handler
[Handler] → generates response
Response

ใช้ไปป์ไลน์การประมวลผลคำขอที่ใช้มิดเดิลแวร์ตามมาตรฐาน PSR-15

mermaid
graph TD
subgraph "Request Processing Pipeline"
A["HTTP Request<br/>(PSR-7 ServerRequest)"]
B["Middleware Stack<br/>(PSR-15)"]
C["Logger Middleware"]
D["Session Middleware"]
E["Auth Middleware"]
F["CORS Middleware"]
G["Router Middleware"]
H["Handler<br/>(Controller/Action)"]
I["Response<br/>(PSR-7 Response)"]
end
A --> B
B --> C
C --> D
D --> E
E --> F
F --> G
G --> H
H --> I
<?php
declare(strict_types=1);
namespace XoopsCore;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class SessionMiddleware implements MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
// 1. Retrieve session (or start new)
$sessionId = $request->getCookieParams()['PHPSESSID'] ?? null;
$session = $this->sessionManager->load($sessionId);
// 2. Attach session to request
$request = $request->withAttribute('session', $session);
// 3. Pass to next middleware
$response = $handler->handle($request);
// 4. Set session cookie if needed
if ($session->isModified()) {
$response = $response->withAddedHeader(
'Set-Cookie',
'PHPSESSID=' . $session->getId() . '; HttpOnly; SameSite=Strict'
);
}
return $response;
}
}
<?php
class AuthMiddleware implements MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
// Get session from previous middleware
$session = $request->getAttribute('session');
// Authenticate user from session
$user = $this->authenticate($session);
// Attach user to request
$request = $request->withAttribute('user', $user);
return $handler->handle($request);
}
private function authenticate(?Session $session): User
{
if ($session && $session->has('uid')) {
return $this->userRepository->findById($session->get('uid'));
}
return new AnonymousUser();
}
}
<?php
class AuthorizationMiddleware implements MiddlewareInterface
{
public function __construct(private AuthorizationChecker $checker)
{
}
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
$user = $request->getAttribute('user');
$route = $request->getAttribute('route');
// Check if user has permission for this route
if (!$this->checker->isGranted($user, $route)) {
return new JsonResponse(
['error' => 'Unauthorized'],
403
);
}
return $handler->handle($request);
}
}
<?php
// Modules can provide their own middleware
class PublisherAccessMiddleware implements MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
$user = $request->getAttribute('user');
// Module-specific access control
if (!$user->hasPermission('publisher_view')) {
return new HtmlResponse('Access denied', 403);
}
return $handler->handle($request);
}
}
<?php
// bootstrap.php - Application setup
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Xoops\Core\Middleware\{
LoggerMiddleware,
SessionMiddleware,
AuthMiddleware,
CorsMiddleware,
ErrorHandlingMiddleware
};
// Create middleware pipeline
$middlewareStack = [
// 1. Error handling (outermost)
new ErrorHandlingMiddleware(),
// 2. Logging
new LoggerMiddleware($logger),
// 3. CORS handling
new CorsMiddleware($corsConfig),
// 4. Session management
new SessionMiddleware($sessionManager),
// 5. Authentication
new AuthMiddleware($userRepository),
// 6. Authorization
new AuthorizationMiddleware($authChecker),
// 7. Routing and dispatching
new RoutingMiddleware($router),
// 8. Module middleware (dynamic)
...$this->loadModuleMiddleware(),
];
// Process request through middleware stack
$request = ServerRequestFactory::fromGlobals();
$dispatcher = new MiddlewareDispatcher($middlewareStack);
$response = $dispatcher->dispatch($request);
// Send response
http_response_code($response->getStatusCode());
foreach ($response->getHeaders() as $name => $values) {
foreach ($values as $value) {
header("$name: $value", false);
}
}
echo $response->getBody();

โมดูลสามารถให้มิดเดิลแวร์:

<?php
// Publisher module - xoops_version.php
$modversion['middleware'] = [
'PublisherAccessMiddleware' => true, // Auto-load
'PublisherLogMiddleware' => true,
];
// Or custom:
$modversion['middleware_factory'] = function() {
return [
new PublisherCacheMiddleware(),
new PublisherPermissionMiddleware(),
];
};

  1. การแยกข้อกังวล - มิดเดิลแวร์แต่ละตัวจะจัดการความรับผิดชอบเดียว
  2. ความสามารถในการทดสอบ - ง่ายต่อการทดสอบส่วนประกอบมิดเดิลแวร์แต่ละรายการ
  3. ความสามารถในการประกอบ - มิดเดิลแวร์สามารถผสมและจัดลำดับใหม่ได้
  4. เป็นไปตามมาตรฐาน - ใช้มาตรฐาน PSR-15 และ PSR-7
  5. ความสามารถในการขยาย - โมดูลสามารถเพิ่มมิดเดิลแวร์ที่กำหนดเองได้อย่างง่ายดาย
  6. การดีบัก - ล้างโฟลว์คำขอผ่านไปป์ไลน์
  7. ประสิทธิภาพ - สามารถเพิ่มประสิทธิภาพเลเยอร์มิดเดิลแวร์เฉพาะได้
  8. การทำงานร่วมกัน - สามารถใช้มิดเดิลแวร์ PSR-15 ของบุคคลที่สามได้
  1. เส้นโค้งแห่งการเรียนรู้ - นักพัฒนาซอฟต์แวร์ต้องเข้าใจ PSR-15
  2. ค่าใช้จ่ายด้านประสิทธิภาพ - การเรียกใช้ฟังก์ชันเพิ่มเติมในไปป์ไลน์
  3. ความซับซ้อน - ชิ้นส่วนที่เคลื่อนไหวมากกว่าแนวทางเสาหิน
  4. ความพยายามในการโยกย้าย - ต้องมีการปรับโครงสร้างโค้ดที่มีอยู่ใหม่
  5. การพึ่งพา - ต้องใช้ไลบรารี PSR-7 HTTP
ความเสี่ยงความรุนแรงการบรรเทาผลกระทบ
เชนมิดเดิลแวร์ที่ซับซ้อนปานกลางเอกสารชัดเจน ตัวอย่าง
ประสิทธิภาพลดลงปานกลางเกณฑ์มาตรฐาน เพิ่มประสิทธิภาพเส้นทางลัด
การใช้นักพัฒนาในทางที่ผิดปานกลางการตรวจสอบโค้ด คู่มือแนวทางปฏิบัติที่ดีที่สุด
การเปลี่ยนแปลงที่ทำลายการโยกย้ายสูงระยะเวลาการเลิกใช้ ผู้ช่วยเหลือ
ปัญหาการสั่งซื้อมิดเดิลแวร์ปานกลางล้างกราฟการพึ่งพา

  • ใช้ PSR-7 HTTP กระดาษห่อข้อความ
  • สร้าง MiddlewareDispatcher
  • ใช้มิดเดิลแวร์หลัก (เซสชัน, การรับรองความถูกต้อง)
  • อัปเดตเคอร์เนลเพื่อใช้มิดเดิลแวร์
  • ย้ายฟังก์ชันการทำงานที่มีอยู่ไปยังมิดเดิลแวร์
  • เพิ่มการสนับสนุนมิดเดิลแวร์โมดูล
  • สร้างยูทิลิตี้ทดสอบมิดเดิลแวร์
  • เขียนเอกสารที่ครอบคลุม
  • จัดเตรียมเลเยอร์ความเข้ากันได้สำหรับโค้ดเก่า
  • โมดูลช่วยเหลืออัปเดตเป็นมิดเดิลแวร์ใหม่
  • การเพิ่มประสิทธิภาพการทำงาน
  • การตรวจสอบความปลอดภัย
  • XOOPS 4.0 เปิดตัวพร้อมมิดเดิลแวร์
  • เลิกใช้ระบบพรีโหลด/ขอเกี่ยวแบบเก่า
  • ข้อเสนอแนะและการอัปเดตของชุมชน

  • ฟังก์ชันหลักทั้งหมดถูกย้ายไปยังมิดเดิลแวร์
  • ครอบคลุมการทดสอบ 90%+ สำหรับมิดเดิลแวร์
  • เอกสารพร้อมตัวอย่าง
  • ประสิทธิภาพภายใน 10% ของเวอร์ชันก่อนหน้า
  • โมดูลใช้ระบบมิดเดิลแวร์ใหม่ได้สำเร็จ
  • อัตราการรับเลี้ยงบุตรบุญธรรมของชุมชน >80%

  • ให้ความสำคัญกับมิดเดิลแวร์ (ความรับผิดชอบเดียว)
  • ใช้ความไม่เปลี่ยนรูป (สร้างคำขอ/ตอบกลับใหม่)
  • จัดการกับข้อผิดพลาดอย่างสง่างาม
  • การพึ่งพาเอกสาร
  • เพิ่มคำแนะนำประเภท
  • เขียนการทดสอบสำหรับมิดเดิลแวร์
  • ใช้อินเทอร์เฟซ PSR-15 มาตรฐาน
  • ห้ามแก้ไขออบเจ็กต์คำขอ/ตอบกลับที่ใช้ร่วมกัน
  • อย่าเข้าถึง globals โดยตรง
  • อย่าสร้างการพึ่งพาลำดับมิดเดิลแวร์
  • อย่าจับข้อยกเว้นทั้งหมด
  • อย่าผสมตรรกะทางธุรกิจกับมิดเดิลแวร์
  • อย่าให้มิดเดิลแวร์ทำอะไรมากเกินไป

<?php
// Example: Rate limiting middleware
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class RateLimitMiddleware implements MiddlewareInterface
{
public function __construct(
private RateLimiter $limiter,
private int $limit = 100,
private int $window = 3600
) {
}
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
$user = $request->getAttribute('user');
$identifier = $user->getId() ?? $request->getClientIp();
// Check rate limit
$remaining = $this->limiter->check($identifier, $this->limit, $this->window);
if ($remaining < 0) {
return new JsonResponse(
['error' => 'Rate limit exceeded'],
429
);
}
// Add rate limit headers
$response = $handler->handle($request);
return $response
->withAddedHeader('X-RateLimit-Limit', (string)$this->limit)
->withAddedHeader('X-RateLimit-Remaining', (string)$remaining);
}
}

  • ADR-001: สถาปัตยกรรมโมดูลาร์ - รากฐาน
  • ADR-004: ระบบความปลอดภัย - ใช้มิดเดิลแวร์สำหรับการตรวจสอบสิทธิ์
  • ADR-006: การตรวจสอบสิทธิ์แบบสองปัจจัย - สามารถเป็นมิดเดิลแวร์ได้

  • Slim Framework - ตัวอย่างมิดเดิลแวร์
  • Zend Expressive - PSR-15 กรอบงาน
  • Guzzle - HTTP มิดเดิลแวร์ไคลเอ็นต์
  • RelayPHP - ไลบรารีมิดเดิลแวร์
  • PSR-15 Middleware - คอลเล็กชันของมิดเดิลแวร์

เวอร์ชั่นวันที่การเปลี่ยนแปลง
1.0.02024-01-28ข้อเสนอเบื้องต้น

#xoops #adr #psr-15 #มิดเดิลแวร์ #สถาปัตยกรรม #psr-7