Nhảy tới nội dung

Request Lifecycle

Tổng quan

Hiểu rõ cách PHP Framework xử lý một HTTP request từ đầu đến cuối sẽ giúp bạn nắm vững framework và xây dựng ứng dụng hiệu quả hơn. Vietiso được xây dựng trên Swoole - một PHP extension hỗ trợ async I/O, nên lifecycle có một số điểm khác biệt so với các PHP framework truyền thống.

Khởi động ứng dụng

1. Entry Point

Mọi thứ bắt đầu từ file vietiso ở thư mục gốc - đây là CLI entry point của framework:

<?php
// vietiso

// Load core
require_once 'core/constants.php';
require_once 'core/functions.php';
require_once 'vendor/autoload.php';

// Khởi tạo App singleton
App::getInstance()
->setBasePath(__DIR__)
->withMiddleware(static function (MiddlewareCollection $middlewares): void {
$middlewares->use([
LogRequest::class,
HandleCors::class,
HandleException::class,
TrimStrings::class,
ConvertEmptyStringsToNull::class,
]);
})
->start();

2. Boot Service Providers

Khi start() được gọi, framework sẽ boot tất cả Service Providers đã đăng ký trong core/providers.php. Quá trình này gồm 2 pha:

  1. Register phase: Mỗi provider đăng ký các services vào Container
  2. Boot phase: Sau khi tất cả services đã đăng ký, mỗi provider thực hiện khởi tạo (ví dụ: load config, kết nối database)
EnvironmentServiceProvider → Load .env
ConfigServiceProvider → Load config files
DatabaseServiceProvider → Setup database connections
RoutingServiceProvider → Register routes
ModuleServiceProvider → Discover & install modules
... (33 providers)

3. Swoole HTTP Server

Sau khi boot xong, Swoole HTTP Server bắt đầu lắng nghe connections:

// core/Http/Server.php
$swooleServer = new \Swoole\Http\Server($host, $port);
$swooleServer->start();

Server chạy với nhiều worker processes, mỗi worker xử lý nhiều requests đồng thời nhờ coroutines.

Xử lý Request

4. Nhận Request

Khi có HTTP request đến, Swoole gọi event handler onRequest:

#[Event('Request')]
public function onRequest(SwooleRequest $swooleRequest, SwooleResponse $swooleResponse): void
{
// Đăng ký cleanup sau khi request kết thúc
Coroutine::defer([$this, 'terminateRequest']);

// Khởi tạo Request object
$request = $this->app->get('request');
$request->initRequest($swooleRequest);

// Dispatch qua routing và middleware
$response = $this->app->get('route.dispatcher')->handle($request);

// Gửi response
$response->prepare($request);
$response->send($swooleResponse);
}

5. Request Initialization

Request object được tạo mới cho mỗi request (scoped binding) và parse dữ liệu từ Swoole:

  • Headers: Từ $swooleRequest->header
  • Query Parameters: Từ $swooleRequest->get
  • POST Data: Từ $swooleRequest->post
  • JSON Body: Tự động parse nếu Content-Type: application/json
  • Files: Từ $swooleRequest->files
  • Cookies: Từ $swooleRequest->cookie

6. Route Matching

RouteDispatcher sử dụng FastRoute để match URI với routes đã đăng ký:

$routeInfo = $this->dispatch($request->method(), $request->uri());

switch ($routeInfo[0]) {
case Dispatcher::NOT_FOUND:
// 404 Not Found
break;
case Dispatcher::METHOD_NOT_ALLOWED:
// 405 Method Not Allowed
break;
default:
// Route found → lấy controller action
$route = $routeInfo[1];
$params = $routeInfo[2];
break;
}

7. Middleware Pipeline

Request đi qua chuỗi middleware trước khi đến controller. Middleware được thực thi theo thứ tự từ ngoài vào trong (request) và từ trong ra ngoài (response):

Request →  LogRequest
→ HandleCors
→ HandleException
→ TrimStrings
→ ConvertEmptyStringsToNull
→ [Route Middleware]
→ Controller Action
← [Route Middleware]
← ConvertEmptyStringsToNull
← TrimStrings
← HandleException
← HandleCors
← LogRequest
Response ←

Pipeline được xây dựng bằng pattern nested closures:

$response = $this->stackMiddleware
->send($request)
->through($middlewares)
->then($controllerCallback);

8. Parameter Resolution

Trước khi gọi controller method, framework sử dụng Reflection để tự động resolve parameters.

Phân biệt Constructor và Method injection
  • Constructor: inject được mọi service từ Container (UserService, CacheManager...)
  • Controller method: chỉ inject được Request, ValidatedDTO, Model binding, Enum và route params
class UserController
{
// Constructor injection - inject services
public function __construct(
private UserService $userService,
) {}

#[Post('/users')]
public function store(Request $request, CreateUserDTO $dto)
{
// $request: inject Request instance hiện tại
// $dto: tự động validate từ request data
// Services dùng qua $this->userService
return $this->userService->create($dto);
}

#[Get('/users/{id}')]
public function show(User $user)
{
// $user: Route Model Binding - tự động query findOrFail($id)
return $user;
}
}

Các loại parameter hỗ trợ trong controller method:

TypeHành vi
RequestInject Request instance hiện tại
ValidatedDTOValidate request data và tạo DTO
ModelRoute Model Binding - query findOrFail() theo route param
EnumCast route parameter sang PHP Enum
Scalar typesLấy từ route parameters (int $id, string $slug)

9. Controller Execution

Controller được instantiate qua Container (hỗ trợ constructor injection cho services), sau đó method được gọi với các parameters đã resolve.

10. Response

Controller trả về Response object. Framework hỗ trợ nhiều kiểu return:

// Array → tự động chuyển thành JSON
return ['status' => 'success'];

// Response object
return Response::json($data, 201);

// Responsable interface
return $user; // Model implement Responsable

Kết thúc Request

11. Prepare & Send

Response được chuẩn bị (thêm headers cần thiết) và gửi qua Swoole:

$response->prepare($request);  // RFC 2616 compliance
$response->send($swooleResponse); // Gửi headers + content

12. Terminate & Cleanup

Sau khi response được gửi, Coroutine::defer() thực hiện cleanup:

public function terminateRequest(): void
{
// Chạy terminate callbacks
$terminateRequest = $this->app->getTerminateRequest();
if (!empty($terminateRequest)) {
$terminateRequest($this->app);
}

// Xóa scoped instances (Request, RouteDispatcher...)
$this->app->forgetScopedInstances();

// Xóa context data
Context::delete();
}

Điểm quan trọng: Server không restart sau mỗi request (khác với PHP-FPM). Swoole giữ application state trong memory, chỉ scoped instances được cleanup.

Tổng kết Lifecycle

┌─────────────────────────────────────┐
│ Swoole nhận Request │
└──────────────┬──────────────────────┘

┌─────────────────────────────────────┐
│ Request::initRequest() │
│ Parse headers, body, files │
└──────────────┬──────────────────────┘

┌─────────────────────────────────────┐
│ RouteDispatcher::handle() │
│ FastRoute matching │
└──────────────┬──────────────────────┘

┌─────────────────────────────────────┐
│ Middleware Pipeline │
│ Global → Module → Route │
└──────────────┬──────────────────────┘

┌─────────────────────────────────────┐
│ Parameter Resolution │
│ DI, DTO validation, Model binding│
└──────────────┬──────────────────────┘

┌─────────────────────────────────────┐
│ Controller Action │
└──────────────┬──────────────────────┘

┌─────────────────────────────────────┐
│ Response::prepare() & send() │
└──────────────┬──────────────────────┘

┌─────────────────────────────────────┐
│ Cleanup: scoped instances, │
│ context, terminate callbacks │
└─────────────────────────────────────┘

Khác biệt với PHP truyền thống

PHP-FPMVietiso (Swoole)
Mỗi request khởi tạo lại toàn bộApplication state giữ trong memory
Không có connection poolingConnection pool cho DB, Redis
Blocking I/ONon-blocking async I/O
Process-per-requestCoroutine-per-request
Autoload mỗi requestAutoload một lần khi boot