การสร้าง Middleware ใน PHP
ในบทก่อนหน้านี้ เราได้เรียนรู้เกี่ยวกับ Dependency Injection (DI) ซึ่งเป็นเทคนิคสำคัญในการสร้างแอปพลิเคชัน PHP ที่มีความยืดหยุ่นและทดสอบได้ง่าย ในบทนี้ เราจะมาเรียนรู้เกี่ยวกับอีกหนึ่งแนวคิดที่สำคัญไม่แพ้กัน นั่นคือ Middleware ซึ่งเป็นส่วนประกอบที่ช่วยจัดการกับ Request และ Response ก่อนที่แอปพลิเคชันของเราจะประมวลผล
What is Middleware?
Middleware เปรียบเสมือน "ท่อ" (pipe) หรือ "ตัวกรอง" (filter) ที่อยู่ระหว่าง Request ที่เข้ามายังแอปพลิเคชันของเรา และ Response ที่จะส่งออกไป Middleware สามารถใช้เพื่อ:
- ตรวจสอบสิทธิ์การเข้าถึง (Authentication)
- ตรวจสอบการอนุญาต (Authorization)
- บันทึกข้อมูล (Logging)
- ปรับเปลี่ยน Request หรือ Response
- จัดการกับ Error
- และอื่นๆ อีกมากมาย
แนวคิดของ Middleware ช่วยให้เราสามารถแยกส่วนของโค้ดที่เกี่ยวข้องกับการจัดการ Request และ Response ออกจากส่วนของโค้ดหลักของแอปพลิเคชัน ทำให้โค้ดของเรามีความสะอาด, เป็นระเบียบ และง่ายต่อการดูแลรักษา
เหตุผลเชิงออกแบบ: การใช้ Middleware ช่วยลดความซับซ้อนของ Controller หรือ Handler หลัก เพราะไม่ต้องใส่ logic ที่เกี่ยวข้องกับการจัดการ Request/Response ไว้ทั้งหมด ทำให้ Controller/Handler เน้นที่ Business Logic หลักได้เต็มที่ นอกจากนี้ยังช่วยให้ logic ที่เกี่ยวข้องกับการจัดการ Request/Response สามารถนำกลับมาใช้ใหม่ได้ง่ายในหลายๆ Route
Implementing Middleware
ในการสร้าง Middleware ใน PHP เรามักจะสร้าง Class ที่มี method หนึ่ง method ที่ทำหน้าที่เป็น "ตัวกลาง" ในการจัดการ Request และ Response ตัวอย่างเช่น:
<?php
namespace App\Middleware;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Server\RequestHandlerInterface as Handler;
class AuthenticationMiddleware
{
public function __invoke(Request $request, Handler $handler): Response
{
// ตรวจสอบว่ามี User Login หรือยัง
if (!isset($_SESSION['user_id'])) {
// ถ้ายังไม่มี ให้ redirect ไปหน้า Login
$response = new \Laminas\Diactoros\Response\RedirectResponse('/login');
return $response;
}
// ถ้ามี User Login แล้ว ให้ส่งต่อไปยัง Handler ถัดไป
$response = $handler->handle($request);
return $response;
}
}
ในตัวอย่างนี้ เราได้สร้าง Middleware ที่ชื่อ AuthenticationMiddleware ซึ่งทำหน้าที่ตรวจสอบว่ามี User Login หรือยัง ถ้ายังไม่มี User Login จะทำการ Redirect ไปยังหน้า Login ถ้ามี User Login แล้ว จะส่ง Request และ Response ไปยัง Handler ถัดไป
คำอธิบาย:
- เราใช้ Interface
Psr\Http\Message\ServerRequestInterfaceและPsr\Http\Message\ResponseInterfaceเพื่อให้โค้ดของเราเป็นไปตามมาตรฐาน PSR-7 ซึ่งเป็นมาตรฐานสำหรับการจัดการ HTTP Message ใน PHP - Method
__invokeเป็น Magic Method ที่จะถูกเรียกใช้เมื่อเราเรียก Class ในฐานะ Function $handler->handle($request)เป็นการส่ง Request ไปยัง Handler ถัดไปใน Pipeline
แนวปฏิบัติที่ดี: ควรใช้ Interface สำหรับ Request และ Response เพื่อให้โค้ดมีความยืดหยุ่นและสามารถเปลี่ยน implementation ได้ง่ายในอนาคต
Middleware Pipeline
Middleware Pipeline คือการนำ Middleware หลายๆ ตัวมาเรียงต่อกัน เพื่อให้แต่ละ Middleware ทำงานตามลำดับที่กำหนดไว้ ตัวอย่างเช่น:
AuthenticationMiddleware: ตรวจสอบสิทธิ์การเข้าถึงLoggingMiddleware: บันทึกข้อมูลการเข้าถึงErrorHandlerMiddleware: จัดการกับ Error ที่เกิดขึ้น
ในการสร้าง Middleware Pipeline เราสามารถใช้ Library ต่างๆ ที่มีอยู่ เช่น middlewares/utils หรือ zend-stratigility ซึ่ง Library เหล่านี้จะช่วยให้เราสามารถสร้างและจัดการ Middleware Pipeline ได้อย่างง่ายดาย
ตัวอย่างการใช้ zend-stratigility:
<?php
use Laminas\Stratigility\MiddlewarePipe;
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
use Laminas\Diactoros\ServerRequestFactory;
use App\Middleware\AuthenticationMiddleware;
use App\Middleware\LoggingMiddleware;
use App\Handler\HomepageHandler;
require 'vendor/autoload.php';
$pipeline = new MiddlewarePipe();
// เพิ่ม Middleware เข้าไปใน Pipeline
$pipeline->pipe(new LoggingMiddleware());
$pipeline->pipe(new AuthenticationMiddleware());
// เพิ่ม Handler หลัก
$pipeline->pipe(new HomepageHandler());
// สร้าง Request จาก Global Variables
$request = ServerRequestFactory::fromGlobals();
// Run Pipeline
$response = $pipeline->handle($request);
// ส่ง Response ออกไป
(new SapiEmitter())->emit($response);
คำอธิบาย:
- เราสร้าง instance ของ
MiddlewarePipeซึ่งเป็น Class ที่ใช้สำหรับจัดการ Middleware Pipeline - เราใช้ method
pipe()เพื่อเพิ่ม Middleware เข้าไปใน Pipeline ตามลำดับที่ต้องการ HomepageHandlerคือ Handler หลักที่จะประมวลผล Request เมื่อ Middleware ทั้งหมดทำงานเสร็จสิ้นServerRequestFactory::fromGlobals()สร้าง Request จาก Global Variables เช่น$_GET,$_POST,$_SERVERSapiEmitterส่ง Response กลับไปยัง Browser
ข้อดีของการใช้ Middleware Pipeline:
- Separation of Concerns: แยก logic ที่เกี่ยวข้องกับการจัดการ Request/Response ออกจาก Business Logic หลัก
- Reusability: สามารถนำ Middleware กลับมาใช้ใหม่ได้ในหลายๆ Route
- Testability: สามารถทดสอบ Middleware แต่ละตัวได้อย่างอิสระ
- Maintainability: โค้ดมีความสะอาด, เป็นระเบียบ และง่ายต่อการดูแลรักษา
Trade-offs: การใช้ Middleware อาจทำให้ Request มี Overhead เพิ่มขึ้นเล็กน้อยเนื่องจากต้องผ่านหลายขั้นตอน แต่โดยทั่วไปแล้วผลประโยชน์ที่ได้รับจะคุ้มค่ากว่า
สรุป: Middleware เป็นแนวคิดที่สำคัญในการสร้างแอปพลิเคชัน PHP ที่มีความยืดหยุ่น, ทดสอบได้ง่าย และง่ายต่อการดูแลรักษา โดยการใช้ Middleware Pipeline ช่วยให้เราสามารถจัดการกับ Request และ Response ได้อย่างมีประสิทธิภาพ
ในบทถัดไป เราจะเรียนรู้เกี่ยวกับการใช้ Queues และ Workers ใน PHP ซึ่งเป็นเทคนิคที่ช่วยให้เราสามารถประมวลผล Task ที่ใช้เวลานานได้อย่างมีประสิทธิภาพ