Chúng ta đã sử dụng và làm việc với một số hàm có sẵn trong php hay một số hàm mà ta tạo ra trong quá trình lập trình với PHP. Trong PHP còn một hàm đặc biệt nữa có tên gọi là hàm ẩn danh.

1. Hàm ẩn danh là gì?

Các hàm ẩn danh(anonymous function) là các hàm chỉ sử dụng duy nhất một lần. Giống như cái tên của nó là ẩn danh, ta có thể hiểu là nó là hàm không có tên vì vậy mà nó thường được gán vào một biến hoặc gán vào một hàm khác như một tham số và có thể định nghĩa vào bất cứ lúc nào. 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 qua ngoài phạm vi, thì hàm này cũng không còn nữa. Trong bài này chúng ta sẽ học 2 khái niệm là lambda và closure.

2. Hàm ẩn danh Lambda

2.1. Lambda là gì

Lambda chính là một hàm ẩn danh. Giống như khái niệm mình đã nêu ở trên, lambda có thể khai báo ở bất kỳ đâu, chỉ tồn tại trong phạm vi biến nó được định nghĩa và không có khả năng tái sử dụng. Lambda thường được dùng để gán vào biến, các hàm hay là gán vào các class như một tham số.

2.2. Khai báo Lambda

Ta dùng cú pháp sau:

function (thamso)
{
    //code
}

Hoặc ta có thể dùng hàm create_function()

create_function('', thamso);

Trong đó thamso chính là tham số mà bạn muốn truyền vào tham số ẩn danh.

Ví dụ: ta sẽ khai báo hàm ẩn sau:

// Hàm thường
function helloWorld()
{
  return "Hello world";
}

// Hàm ẩn danh
function ()
{
  return "Hello world";
}

Hoặc ta cũng có thể khai báo một hàm khác

function (){
    return 'Học lập trình online tại laptrinhtudau.com';
}

2.3. Sử dụng lambda

Bởi vì các hàm này không có tên nê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 vào một biến hoặc cho vào một hàm 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"

Và chúng ta có thể sử dụng lambla như sau:

<?php
    //gán biến bằng hàm ẩn danh.
    $lambda = function () {
        return 'Học lập trình online tại laptrinhtudau.com';
    };

    //gọi biến
    echo $lambda();
?>

Chú ý: Nếu gán hàm ẩn danh bạn phải chú ý dấu chấm phẩy ; phía sau định nghĩa hàm ẩn danh.

Ta có thể sử dụng hàm ẩn danh như một tham số trong hàm:

<?php
    function getRole($role)
    {
        return $role();
    }
    echo getRole(function () {
        return 'Học lập trình online tại laptrinhtudau.com';
    });
?>

Sử dụng hàm ẩn danh như một tham số trong class, phương thức.

<?php
    class Role
    {
        public $role;
        public function __construct($role)
        {
            $this->role = $role;
        }

        function getRole($role)
        {
            return $role();
        }
    }

    //khởi tạo class Role
    $role = new Role(function () {
        return 'Học lập trình PHP tại laptrinhtudau.com';
    });
    //gán data bằng thuộc tính role
    $data = $role->role;
    //hiển thị role
    echo $data();
    //Kết quả: Học lập trình PHP tại laptrinhtudau.com
    //gọi getRole
    echo $role->getRole(function () {
        return 'Học lập trình Nodejs tại laptrinhtudau.com';
    });
    //Kêt quả: Học lập trình Nodejs tại laptrinhtudau.com
?>

Và ta cũng có thể truyền tham số cho lambla.

<?php
    $content = function ($message) {
        return 'Content: ' . $message;
    };
    echo $content('Học Lập trình');
?>

3. Closure

Closure cũng là một hàm ẩn danh và giống như lambla. Nhưng closure có thêm chức năng là có thể sử dụng biến bên ngoài phạm vi mà nó được tạo ra.

3.1. Khai báo closure

Cú pháp:

function (argument) use (scope) {
    //code
}

Trong đó:

  • argument: là các tham số mà ta muốn truyền vào
  • scope: là danh sách các biến phía ngoài closure mà chúng ta muốn sử dụng trong closure.

Ví dụ:

<?php
    $scope = 'lập trình từ đầu';
    $name = function () use ($scope) {
        return $scope;
    };
    echo $name();
    //Kết quả: lập trình từ đầu
?>

3.2. Sử dụng closure

Vì closure giống với lambla nên ta sẽ có những phần giống với lambla, các bạn có thể tham khảo. Mình sẽ chỉ trình bày ra một số các dùng khác liên quan.

Tham chiếu trong closure

<?php

    $number = 1;

    $getNextNumber = function () use (&$number) {
        return ++$number;
    };

    $getPreNumber = function () use (&$number) {
        return --$number;
    };

    echo $getNextNumber();
    //kết quả: 2

    echo $number;
    // Kết quả: 2

    echo $getPreNumber();
    //Kết quả: 1

?>

Closure trong class

<?php
    class Cart{
        const PRICE_BUTTER = 1.00;
        const PRICE_MILK = 3.00;
        const PRICE_EGGS = 6.95;

        protected $products = array();

        public function add($product, $quantity)
        {
            $this->products[$product] = $quantity;
        }

        public function getQuantity($product)
        {
            return isset($this->products[$product]) ? $this->products[$product] : FALSE;
        }

        public function getTotal($tax)
        {
            $total = 0.00;

            $callback =
                function ($quantity, $product) use ($tax, &$total) {
                    $pricePerItem = constant(__CLASS__ . "::PRICE_" .
                        strtoupper($product));
                    $total += ($pricePerItem * $quantity) * ($tax + 1.0);
                };

            array_walk($this->products, $callback);
            return round($total, 2);
        }
    }

$my_cart = new Cart;

// Thêm sản phẩm
$my_cart->add('butter', 1);
$my_cart->add('milk', 3);
$my_cart->add('eggs', 6);

// In ra tổng số tiền khi đã sale 5%.
print $my_cart->getTotal(0.05) . "\n";
// Kết Quả: 54.29

?>

Ta cũng có thể truy cập các biến bên ngoài phạm vi 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;
});

4. Tại sao phải dùng hàm ẩn

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();