static fn() Trong Laravel/PHP: Đừng Viết Arrow Function Nếu Chưa Hiểu Rõ Điều Này!
Chào anh em! Từ khi PHP 7.4 ra mắt Arrow Function (fn() => ...), cú pháp của chúng ta trở nên ngắn gọn và thanh lịch hơn rất nhiều. Trong Laravel, anh em xài nó rào rào ở khắp mọi nơi, đặc biệt là khi làm việc với Collection.
Nhưng nếu bạn hay đọc mã nguồn (source code) của chính framework Laravel hoặc các thư viện lớn, bạn sẽ thấy các "pháp sư" cốt cán (core team) hiếm khi viết fn(). Thay vào đó, họ luôn viết thêm một chữ static vào đằng trước: static fn().
Tại sao phải rườm rà thêm một chữ static? Nó dùng để làm gì? Hôm nay, chúng ta sẽ mổ xẻ "bí mật đen tối" về bộ nhớ ẩn sau cái Arrow Function nhỏ bé kia.
1. Bí mật đen tối của Arrow Function (fn)
Để hiểu tại sao cần static, ta phải hiểu bản chất của fn.
Khác với hàm vô danh truyền thống (function() use ($var)), Arrow Function sinh ra với đặc quyền Tự động bắt (auto-capture) toàn bộ biến ở phạm vi bên ngoài. Điều đáng sợ là, nếu bạn khai báo fn() bên trong một class (ví dụ: Controller hoặc Model), nó sẽ ngầm định mang theo luôn biến $this vào bên trong closure. Hãy xem ví dụ kinh điển khi query Database trong Laravel:
class ReportController extends Controller
{
// Giả sử Controller này chứa rất nhiều dependency nặng nề ở __construct
public function exportUsers()
{
$users = User::all();
// Arrow function tự động nhét toàn bộ class ReportController ($this) vào trong
$names = $users->map(fn($user) => strtoupper($user->name));
return $names;
}
}
Ở dòng code trên, bạn chỉ muốn lấy cái tên để viết hoa (strtoupper($user->name)). Bạn hoàn toàn không hề sử dụng $this ở bên trong fn().
Nhưng PHP không biết điều đó! Nó vẫn âm thầm đóng gói toàn bộ instance của ReportController (kèm theo mọi thứ nặng nề mà Controller này đang chứa) đính kèm vào cái closure đó cho từng vòng lặp của Collection.
2. static fn() là gì? Thanh gươm chặt đứt sự ràng buộc
Từ khóa static đặt trước fn chính là một lời tuyên bố đanh thép với trình biên dịch PHP: "Này, cái hàm này của tao hoàn toàn độc lập! Tao không cần dùng biến $this, nên mài đừng có tự động ném cái class hiện tại vào trong bộ nhớ của nó!"
// Cắt đứt hoàn toàn với $this, nhẹ nhàng, thanh thoát
$names = $users->map(static fn($user) => strtoupper($user->name));
Bằng cách thêm chữ static:
- Closure này không còn bị ràng buộc với đối tượng cha (Object binding).
- Không có biến thừa thãi nào bị nhét vào bộ nhớ.
- Nó chính thức trở thành một Pure Function (Hàm thuần khiết) mà chúng ta đã bàn ở bài viết trước: Chỉ nhận input và trả về output, tuyệt đối không chạm vào thế giới bên ngoài.
3. Tại sao điều này lại cực kỳ quan trọng trong thực chiến?
Nếu bạn chỉ viết code chạy các web request bình thường (vòng đời sống ngắn), sự khác biệt về RAM giữa fn và static fn là rất nhỏ, gần như không đáng kể.
Tuy nhiên, nó sẽ biến thành thảm họa Memory Leak (Rò rỉ bộ nhớ) nếu bạn làm việc trong các hệ thống sau:
- Laravel Octane / Swoole: Các tiến trình PHP chạy liên tục trên RAM, không bị tiêu diệt sau mỗi request. Nếu closure của bạn vô tình giữ lại tham chiếu (reference) của
$this, Garbage Collector (trình dọn rác) của PHP sẽ không thể xóa cái Controller đó đi. RAM cứ thế tăng dần cho đến khi sập server. - Xử lý Collection Khổng Lồ: Khi bạn dùng
.map()hay.filter()cho hàng trăm ngàn bản ghi, việc tránh khởi tạo và binding$thischo hàng trăm ngàn cái closure sẽ tiết kiệm được một lượng thời gian tính toán và bộ nhớ đáng kinh ngạc. - Xử lý Collection Khổng Lồ: Khi bạn dùng
.map()hay.filter()cho hàng trăm ngàn bản ghi, việc tránh khởi tạo và binding$thischo hàng trăm ngàn cái closure sẽ tiết kiệm được một lượng thời gian tính toán và bộ nhớ đáng kinh ngạc. - Event Listeners / Background Jobs: Khi bạn truyền closure vào các Job chạy ngầm, việc tách bạch trạng thái bằng
static fngiúp bạn tránh được những lỗi tâm linh do serialize nhầm nguyên một đống object không liên quan.
Quy Tắc Vàng Dành Cho Senior
Chỉ tốn thêm 6 ký tự để gõ chữ static, nhưng nó thể hiện bạn là một người cực kỳ làm chủ bộ nhớ của hệ thống. Hãy ghim chặt quy tắc này:
Nếu bên trong Arrow Function của bạn KHÔNG sử dụng $this, hãy LUÔN LUÔN khai báo nó là static fn().
Trình duyệt IDE hiện đại như PhpStorm thậm chí có thể tự động cảnh báo và gợi ý bạn thêm từ static vào nếu nó phát hiện bạn không dùng $this. Hãy bật tính năng đó lên nhé!
Việc tối ưu những closure nhỏ xíu này đã đưa chúng ta đến rất gần với bức tranh tối ưu hóa sâu dưới tầng Framework. Nếu tiếp tục series này, bạn có muốn chúng ta đi thẳng vào một con quái vật tối ưu hiệu năng của Laravel mang tên Laravel Octane (Swoole/RoadRunner) để xem cách nó thay đổi hoàn toàn vòng đời của ứng dụng PHP không?
All Rights Reserved