Lambda và Closures trong PHP
Vừa qua mình gặp 1 tình huống là phải dùng hàm call back để xử lý vấn đề, thế là tìm hiểu luôn cái lý thuyết về php closure xem nó là như thế nào. trước hết là tìm hiểu Lambda. Lambda là gì? Các hàm lambda là các hàm ẩn danh (anonymous function), sử dụng một lần, có thể được định nghĩa vào bất ...
Vừa qua mình gặp 1 tình huống là phải dùng hàm call back để xử lý vấn đề, thế là tìm hiểu luôn cái lý thuyết về php closure xem nó là như thế nào. trước hết là tìm hiểu Lambda.
Lambda là gì?
Các hàm lambda là các hàm ẩn danh (anonymous function), sử dụng một lần, có thể được định nghĩa vào bất cứ lúc nào, và thường gắn với một biến hoặc gán vào 1 hàm khác như một tham số . Các hàm này chỉ tồn tại trong phạm vi của biến mà nó được định nghĩa, vì vậy khi biến đó vượt ra ngoài phạm vi, thì hàm này cũng không còn nữa.
Hàm ẩn danh
Một hàm ẩn danh chỉ đơn giản là một hàm không có tên.
Ví dụ
// Hàm thường function helloWorld() { return "Hello world"; } // Hàm ẩn danh function () { return "Hello world"; }
Sử dụng hàm ẩn danh
Bởi vì các hàm này không có tên, chúng ta không thể gọi nó như một chức năng thường xuyên. Thay vào đó ta phải gán nó vào một biến hoặc cho vào 1 hàm khác khác như là một tham số.
Ví dụ
// Hàm bình thường echo helloWorld(); // "Hello world" // Hàm ẩn danh // gán cho 1 biến $hello = function () { return "Hello world"; } // gọi hàm ẩn danh echo $hello(); // "Hello world"
Để sử dụng các hàm ẩn danh, chúng ta gán nó vào một biến và sau đó gọi là biến như là một function như trên. Hoặc sử dụng Lambda như thế này:
// Pass Lambda to function function shout ($message) { echo $message(); } // Call function shout(function() { return "Hello world"; });
Tại sao dùng Lambda
Lambdas rất hữu dụng, bởi vì chúng ta không cần phải tạo hẳn 1 hàm cho 1 lần sử dụng duy nhất.
Thông thường, chúng ta sẽ cần một hàm để làm một công việc, nhưng nó không có nghĩa là chúng ta sẽ dùng nó trong phạm vi global. Thay vì có một hàm sử dụng một lần và sau đó bỏ đi để nó ở đó, chúng ta có thể sử dụng một Lambda để thay thế.
Tất nhiên, chúng ta có thể sử dụng chức năng create_function trong PHP. Điều này về cơ bản là giống nhau:
// Use create_function $hello = create_function(', 'echo "Hello World!";'); // Call function $hello();
Closure là gì?
Một Closure (bao đóng :-s) về cơ bản giống như một Lambda, ngoài ra nó có thể truy cập các biến bên ngoài phạm vi mà nó được tạo ra.
Ví dụ:
// Create a user $user = "Thỏ 7 màu"; // Create a Closure $hello = function() use ($user) { echo "Hello $user"; }; // Greet the user $hello(); // Returns "Hello Thỏ 7 màu"
Như chúng ta có thể thấy ở trên, Closure có thể truy cập biến $user. Bởi vì nó đã được khai báo trong các điều khoản sử dụng (use ($user)) của định nghĩa hàm Closure.
Nếu chúng ta thay đổi biến $user ban đầu trong Closure, nó sẽ không ảnh hưởng đến các biến ban đầu. Để cập nhật các biến ban đầu, chúng ta có thể thêm một dấu &. Một dấu & trước một biến có nghĩa đây là một reference và vì vậy các biến ban đầu cũng được cập nhật.
// Set counter $i = 0; // Increase counter within the scope // of the function $closure = function () use ($i) { $i++; }; // Run the function $closure(); // The global count hasn't changed echo $i; // Returns 0 // Reset count $i = 0; // Increase counter within the scope // of the function but pass it as a reference $closure = function () use (&$i) { $i++; }; // Run the function $closure(); // The global count has increased echo $i; // Returns 1
Closure cũng rất hữu ích khi sử dụng các hàm PHP mà chấp nhận hàm call back như array_map, array_filter, array_reduce hoặc array_walk.
Ví dụ:
// An array of names $users = ['Thỏ 7 màu', 'Đậu đỏ', 'Gấu AK', 'Bé đội xô']; // Pass the array to array_walk array_walk($users, function ($name) { echo "Hello $name<br>"; }); // Returns // -> Hello Thỏ 7 màu // -> Hello Đậu đỏ // -> Hello Gấu AK // -> ..
Ngoài ra, chúng ta có thể truy cập các biến bên ngoài phạm vi của Closure bằng cách sử dụng use
// Set a multiplier $multiplier = 3; // Create a list of numbers $numbers = [1, 2, 3, 4]; // Use array_walk to iterate // through the list and multiply array_walk($numbers, function ($number) use ($multiplier) { echo $number * $multiplier; });
Sử dụng Closure trong trường hợp cụ thể
Ví dụ trong Laravel
Route::get('user/(:any)', function ($name) { return "Hello " . $name; });
Như vậy là có 1 link user/Tho7mau thì sẽ có "Hello Tho7mau" :v
Đó là ví dụ, còn tình huống của mình là thế này
class A { public static function f($query, $callback = null) { $query->where(...); //code $query->where(function ($currentQuery) use ($keyword, $callback) { $currentQuery->orWhere(...); if ($callback && is_callable($callback)) { $currentQuery = $callback($currentQuery); } }); $query->someWhere(...); return $query->paginate($xxx); } } class B extends A { public static function f1($yyy) { //code $query->where(...); //code return parent::f($query, __CLASS__ . '::f2'); } public static function f2($query) { return $query->orWhere(...); } }
Kết luận
Tài liệu:
- Google results
- PHP docs
Viết bài trên Viblo thật là 1 cực hình, auto save chạy liên tục đơ cả máy, gõ xong 1 dòng phải đợi mấy mùa trăng mới gõ tiếp được dòng tiếp theo