0

Giải mã "Vụ nổ Big Bang" của Laravel: Mổ xẻ hàm Request::capture()

Chào anh em cộng đồng Viblo!

Đa số anh em làm Laravel hàng ngày đều rất quen thuộc với việc tiêm (inject) class Request vào Controller để lấy dữ liệu: $request->input('name'). Mọi thứ chạy trơn tru như có phép thuật.

Nhưng anh em có bao giờ tự hỏi: Cái object $request đó từ đâu sinh ra? Ai đã lấy dữ liệu từ trình duyệt của người dùng, nhào nặn nó và trao tận tay cho anh em ở Controller?

Hôm nay, chúng ta sẽ mở file public/index.php - cánh cửa đầu tiên của mọi ứng dụng Laravel - và cùng giải phẫu một trong những hàm quan trọng nhất của Framework: Request::capture(). Đây chính là nơi bắt đầu "Vụ nổ Big Bang" tạo ra vòng đời của một Request. Lên xe thôi!

1. Bối cảnh: Cánh cửa public/index.php

Nếu bạn mở file public/index.php trong một project Laravel, bạn sẽ thấy một đoạn code trông như thế này:

$app = require_once __DIR__.'/../bootstrap/app.php';

$kernel = $app->make(Kernel::class);

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture() // <--- CHÚNG TA ĐANG Ở ĐÂY
)->send();

$kernel->terminate($request, $response);

Dòng $request = Illuminate\Http\Request::capture() chính là khoảnh khắc Laravel chuyển giao từ thế giới PHP thuần (với các biến toàn cục xấu xí) sang thế giới Lập trình hướng đối tượng (OOP) thanh lịch.

2. Mổ xẻ nội tạng: Bên trong capture() có gì?

Hãy nhìn vào source code gốc của hàm capture() mà chúng ta đang bàn tới:

/**
 * Create a new Illuminate HTTP request from server variables.
 *
 * @return static
 */
public static function capture()
{
    static::enableHttpMethodParameterOverride();

    return static::createFromBase(SymfonyRequest::createFromGlobals());
}

Chỉ có 2 dòng code ngắn gọn, nhưng hàm chứa toàn bộ tinh hoa kiến trúc. Chúng ta bóc tách từng dòng nhé.

Dòng 1: static::enableHttpMethodParameterOverride(); - Cú lừa ngoạn mục của HTML

Anh em làm web đều biết một sự thật cay đắng: Các form HTML tiêu chuẩn <form method="..."> cho đến tận bây giờ vẫn chỉ hỗ trợ 2 method là GETPOST.

Vậy làm sao Laravel biết được bạn đang muốn gọi Route PUT, PATCH hay DELETE để update/xóa data? Chính là nhờ dòng code này! Nó bật tính năng Method Spoofing (Giả mạo method).

Khi bạn viết @method('PUT') trong Blade template, Laravel thực chất chèn một input ẩn: <input type="hidden" name="_method" value="PUT">. Dòng lệnh enableHttpMethodParameterOverride() sẽ báo cho hệ thống biết: "Này, nếu thấy một request POST gửi lên mà có chứa biến _method, hãy coi như nó là method đó nhé!".

Dòng 2: SymfonyRequest::createFromGlobals() - Người khổng lồ đứng sau Laravel

Nhiều anh em không biết rằng, phần ruột (Core) xử lý HTTP của Laravel thực chất được xây dựng dựa trên các Component của framework Symfony.

SymfonyRequest::createFromGlobals() là một cỗ máy gom rác và tinh chế. Nó gom tất cả các biến toàn cục của PHP thuần bao gồm:

  • $_GET (Query string)
  • $_POST (Form data)
  • $_SERVER (Headers, IP, thông tin môi trường)
  • $_COOKIE (Cookies)
  • $_FILES (File upload)

Thay vì để anh em phải gọi thủ công $_POST['username'] hay $_FILES['avatar']['name'] (vừa thiếu an toàn, vừa khó test), Symfony đóng gói toàn bộ đống lộn xộn này thành một Object cực kỳ chuẩn mực và an toàn.

Dòng cuối: static::createFromBase(...) - Khoác chiếc áo Laravel Symfony Request rất tốt, nhưng Taylor Otwell (cha đẻ Laravel) muốn nó phải "ngon" hơn nữa.

Ông dùng hàm createFromBase() để bọc (wrap) cái Object của Symfony lại, và tạo ra class Illuminate\Http\Request của riêng Laravel. Nhờ khoác chiếc áo này, cái $request của anh em mới có thêm những "siêu năng lực" đặc quyền của Laravel như:

  • $request->user(): Lấy user đang đăng nhập.
  • $request->validate(): Chạy validation trực tiếp.
  • Hỗ trợ Macroable: Cho phép anh em tự định nghĩa thêm hàm mới vào Request.

3. Bài học thực chiến từ cấp độ Core

Việc hiểu hàm capture() không chỉ để "chém gió" cho vui, nó trực tiếp ảnh hưởng đến cách anh em xử lý bug ở các hệ thống lớn.

Bài học 1: Đừng bao giờ dùng $_GET, $_POST hay $_SERVER trong Laravel

Một khi vòng đời đã đi qua hàm capture(), state của ứng dụng đã được đóng gói vào object $request. Nếu bạn gọi $_POST trực tiếp ở Controller, bạn đã phá vỡ hoàn toàn kiến trúc OOP, làm mất khả năng tự động Validate, Sanitization của Laravel, và quan trọng nhất: Không thể viết Unit Test. Khi test, chúng ta tạo ra các fake Request objects, chứ không ai đi giả lập biến global cả!

Bài học 2: Cạm bẫy khi đứng sau Load Balancer / Nginx Reverse Proxy

Ở các hệ thống chịu tải cao, server Laravel của bạn thường đứng sau một Load Balancer (AWS ALB, HAProxy) hoặc Nginx.

Lúc này, cái biến $_SERVER['REMOTE_ADDR'] mà hàm capture() túm được thực chất là IP của con Load Balancer, chứ không phải IP thật của khách hàng! Nếu bạn dùng nó để Rate Limit (như bài viết trước mình chia sẻ), toàn bộ hệ thống sẽ khóa nhầm mọi người dùng.

Cách giải quyết: Vì bạn đã hiểu capture() dựa trên Symfony, bạn chỉ cần vào middleware TrustProxies.php của Laravel, khai báo dải IP của Load Balancer. Lập tức, Symfony/Laravel sẽ tự động thông minh đọc Header X-Forwarded-For để lấy IP thật của người dùng gán vào $request->ip().

Lời kết

Illuminate\Http\Request::capture() chỉ là 2 dòng code, nhưng nó đại diện cho một tư duy thiết kế phần mềm kinh điển: Đóng gói sự phức tạp, cung cấp sự đơn giản. Nó là minh chứng cho việc Laravel biết đứng trên vai người khổng lồ (Symfony) để tạo ra trải nghiệm Developer Experience (DX) tốt nhất thế giới PHP.

Anh em có hay vọc vạch vào thư mục vendor/laravel/framework để đọc source code không? Tin mình đi, đọc mã nguồn của Framework là cách nhanh nhất để thăng cấp từ Fresher lên Senior đấy!

Nếu thấy bài mổ xẻ này thú vị, đừng quên Upvote để mình có động lực đào sâu thêm các lõi khác của Laravel như Service Container hay Eloquent nhé!


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.