Xây dựng và sử dụng hàm (function) trong PHP

    2

    Hàm là một khái niệm đặc biệt quan trọng trong PHP (cũng như trong bất kỳ ngôn ngữ lập trình nào).

    Hàm trong PHP là một nhóm code được đặt tên nhằm thực hiện một nhiệm vụ xác định. Thông thường hàm hoạt động dựa trên một số thông tin đầu vào, thường gọi là tham số, và trả lại một kết quả.

    Việc sử dụng hàm đem lại rất nhiều lợi ích. Hàm có khả năng được tái sử dụng ở những vị trí khác nhau trong script, qua đó tránh được việc viết lặp code. Khi các thao tác cơ bản được chuyển thành hàm, code sẽ có cấu trúc rõ ràng, dễ đọc, dễ theo dõi hơn.

    Trên thực tế, ngay từ những bài học đầu tiên về PHP bạn đã tiếp xúc với hàm. Đến giờ bạn đã làm quen và sử dụng rất nhiều hàm khác nhau được xây dựng sẵn trong PHP.

    Tuy nhiên, bạn mới chỉ dừng lại ở việc sử dụng các hàm đã xây dựng sẵn.

    PHP, cũng như các ngôn ngữ lập trình khác, cung cấp cho người lập trình khả năng tự xây dựng hàm của riêng mình. Không chỉ vậy, bạn còn có thể tập hợp các hàm tự xây dựng lại thành các bộ thư viện hàm của riêng mình để tái sử dụng qua nhiều dự án, giống như khi sử dụng các thư viện hàm xây dựng sẵn.

    Định nghĩa hàm (function) trong PHP

    Việc tạo ra các hàm của riêng bạn được gọi là định nghĩa hoặc khai báo hàm. Để định nghĩa (khai báo) một hàm mới, bạn sử dụng từ khóa function với cú pháp như sau:

    function tên_hàm($tham_số_1, $tham_số_2, ...) {
       Lệnh 1;
       Lệnh 2;
       ...
    }

    Trong đó, tên_hàm cần tuân thủ quy tắc đặt định danh chung của PHP.

    Tên hàm cần thể hiện hành động, do vậy nên dùng động từ làm tên hàm. Tên hàm nên đặt theo một trong các kiểu sau:

    • tất cả các từ viết thường và ghép bởi dấu gạch chân. Ví dụ, get_name, set_age.
    • camelCase – bắt đầu bằng chữ thường, viết hoa chữ cài đầu mỗi từ tiếp theo. Ví dụ, getName, setAge.

    Nếu chọn kiểu nào thì sử dụng thống nhất trong toàn bộ code.

    Cặp dấu {} là bắt buộc. Cặp dấu {} và những lệnh nằm giữa tạo ra khối code thân hàm.

    Trong đó, ($tham_số_1, $tham_số_2, …) được gọi là danh sách tham số. Danh sách tham số không quy định kiểu của các tham số. Do vậy khi sử dụng tham số nên kiểm tra kiểu cẩn thận.

    Trong định nghĩa hàm, $tham_số_1, $tham_số_2 như vậy được gọi là tham số hình thức do ở giai đoạn định nghĩa hàm bạn không biết giá trị cụ thể của các tham số này. Bạn có thể sử dụng các tham số này trong thân hàm như các biến định nghĩa sẵn (nhưng chưa xác định giá trị).

    Danh sách tham số không bắt buộc. Nếu hàm không có tham số nào, lệnh khai báo hàm chỉ đơn giản như sau (phần danh sách tham số để trống):

    function tên_hàm(){
      Lệnh 1;
      Lệnh 2;
      ...
    }

    Sau đây là một số ví dụ về khai báo hàm đơn giản:

    // đây là định nghĩa một hàm không tham số
    function hello() {
        echo "Hello world from PHP";
    }
    
    // hàm này có 1 tham số
    function greeting($name) { 
        echo "Hello world, $name!";  // sử dụng tham số $name như một biến bình thường
    }
    
    // hàm có 2 tham số
    function welcome($fname, $lname) {
      echo "Hello, $fname $lname. Welcome to heaven!";
    }

    Lệnh khai báo hàm trong PHP có thể nằm trong lệnh khác, hoặc thậm chí nằm trong hàm khác (nested function).

    Hàm trả về kết quả

    Hàm cũng có thể trả lại giá trị (qua lời gọi hàm). Nếu hàm cần trả lại giá trị, bạn dùng từ khóa return trước giá trị trả về.

    Sau đây là định nghĩa một hàm có trả về kết quả:

    // hàm tính tổng các số từ 0 đến $n
    function sum($n) {
        $s = 0;
        for($i = 0; $i <= $n; ++$i)
            $s += $i;
        return $s;
    }

    Khi sử dụng return có một số lưu ý sau:

    (1) Thông thường, return là lệnh cuối cùng trong thân hàm. Tuy nhiên, return có thể nằm ở bất kỳ đâu, tùy vào logic của bạn.

    (2) Khi gặp return, PHP sẽ kết thúc thực hiện code bên trong thân hàm và copy giá trị (nếu có) đứng sau return chuyển cho nơi sử dụng (gọi) hàm. Như vậy, lời gọi hàm cũng có thể xem là một biểu thức vì nó tính ra giá trị.

    (3) Bạn cũng có thể sử dụng lệnh return mà không cần cung cấp giá trị. Khi này return sẽ kết thúc thực hiện hàm và giá trị trả về là NULL.

    (4) PHP không quy định kiểu kết quả của hàm nên bạn có thể để hàm trả lại giá trị khác kiểu nhau tùy từng điều kiện. Đây là tình huống của hàm factorial dưới đây, khi có thể trả về kiểu số (khi $n nguyên dương) hoặc kiểu logic (khi $n không nguyên dương).

    // hàm tính n! (giai thừa)
    function factorial($n) {
        if(!is_int($n) || $n < 0) return false;
        $p = 1;
        for($i = 1; $i <= $n; ++$i)
            $p *= $i;
        return $p;
    }

    Từ khóa return trong PHP chỉ có thể trả về một giá trị. Điều này cũng có nghĩa là hàm chỉ có thể trả lại một giá trị.

    Trong trường hợp cần trả lại nhiều giá trị, bạn có thể sử dụng mảng, đặc biệt là mảng kết hợp. Ví dụ:

    function equation($a, $b, $c){
        $d = $b*$b - 4*$a*$c;
        if($d < 0) return false;
        return [
            'x1' => (-$b + sqrt($d))/(2*$a),
            'x2' => (-$b - sqrt($d))/(2*$a)
        ];
    }

    Đây là một hàm giải phương trình bậc 2. Do phương trình có thể có 2 nghiệm thực, chúng ta lưu hai nghiệm này vào một mảng kết hợp (coi như là 1 giá trị duy nhất) và trả lại mảng này làm kết quả.

    Sử dụng hàm trong PHP

    Một hàm sau khi định nghĩa sẽ có thể được sử dụng. Bạn chỉ có thể sử dụng hàm sau khi đã định nghĩa nó. Nếu sử dụng hàm trước định nghĩa sẽ báo lỗi.

    Việc sử dụng hàm (lời gọi hàm) thực hiện theo cú pháp chung sau:

    // nếu hàm có tính toán ra kết quả
    $kết_quả = tên_hàm($tham_số_1, $tham_số_2, ...);
    // nếu hàm không tính ra kết quả
    tên_hàm($tham_số_1, $tham_số_2, ...);
    // nếu hàm không yêu cầu tham số
    [$kết_quả =]tên_hàm();

    Thực ra việc sử dụng hàm đã rất quen thuộc với bạn.

    Với các hàm hello, greeting và hello khai báo ở trên bạn có thể gọi như sau:

    hello(); // lời gọi đơn giản nhất không tham số
    
    greeting('Donald'); // truyền giá trị cho tham số $name
    $n= 'Donald';
    greeting($n); // truyền một biến làm tham số cho greeting
    
    welcome('Donald', 'Trump'); // truyền hai giá trị cho hai tham số tương ứng
    

    Đối với hai hàm sum và factorial, lời gọi hàm như sau:

    $s = sum(100); // tính tổng các số từ 0 đến 100
    $n4 = factorial(4); // tính 4!

    Dĩ nhiên bạn cũng có thể viết:

    sum(100);
    factorial(4);

    Tuy nhiên kết quả do hàm tính ra sẽ không được lưu trữ hoặc sử dụng (tức là phí công sử dụng hàm!).

    Ở giai đoạn gọi (sử dụng) hàm bạn cung cấp giá trị cụ thể tương ứng với các tham số có trong khai báo (định nghĩa hàm). Khi gọi hàm, các giá trị truyền cho tham số được gọi là các tham số thực.

    Thông thường, khi định nghĩa hàm với bao nhiêu tham số hình thức thì khi gọi bạn truyền bấy nhiêu giá trị, theo đúng thứ tự. Tuy nhiên những điều này đều không bắt buộc. Chúng ta sẽ nói chi tiết hơn về cách khai báo và truyền tham số cho hàm trong một bài học riêng.

    Khi định nghĩa và sử dụng hàm cần lưu ý, đặc biệt là đối với các bạn xuất phát từ C:

    (1) trong PHP tên hàm không phân biệt hoa thường. Ví dụ, tên hàm Greeting và greeting là như nhau. Phần code sau sẽ báo lỗi vì bạn định nghĩa hàm greeting (hay Greeting) hai lần:

    function  greeting() { ... }
    function Greeting($name) { ... }

    (2) trong khai báo hàm PHP không giới hạn kiểu của tham số, và trong lời gọi hàm PHP cũng không thực hiện kiểm soát kiểu, do vậy cần cẩn trọng kiểm tra kiểu của tham số trước khi sử dụng.

    (3) trong khai báo hàm không giới hạn kiểu của kết quả, điều này dẫn đến một hàm có thể trả lại kết quả thuộc nhiều kiểu khác nhau. Điều này bạn đã gặp khá nhiều lần khi một hàm có thể trả kết quả vừa kiểu số vừa kiểu logic, hoặc vừa kiểu chuỗi vừa kiểu logic.

    Trong bài học sau bạn sẽ làm quen với chỉ báo kiểu (type hinting) trong khai báo hàm – một tính năng hỗ trợ chỉ báo kiểu tham số và kiểu kết quả của hàm trong PHP 7.4.

    Một số trường hợp khai báo hàm đặc biệt trong PHP

    Trong PHP việc khai báo và sử dụng hàm có nhiều điểm đặc biệt mà các bạn xuất phát từ C/C#/Java/C++ có thể cảm thấy tương đối bất thường.

    Khai báo hàm trong lệnh

    Đầu tiên, PHP cho phép khai báo hàm bên trong một lệnh khác. Ví dụ:

    $n = (int) readline('N = ');
    if(is_int($n) && $n > 0) {
        
        function factorial($n) {
            $p = 1;
            for($i = 1; $i <= $n; ++$i)
                $p *= $i;
            return $p;
        }
    
        echo "$n! = ", factorial($n);
    }
    

    Trong ví dụ này lệnh khai báo hàm factorial nằm bên trong lệnh if. Nó có nghĩa là chúng ta chỉ thực hiện khai báo (và sử dụng) hàm factorial khi biến $n đảm bảo nguyên dương. Trong những trường hợp khác, hàm factorial không được khai báo!

    Khai báo hàm trong hàm

    Tình huống thứ hai là khả năng khai báo hàm bên trong một hàm khác. Hàm khai báo bên trong một hàm khác được gọi là hàm lồng (nested function).

    Hãy xem ví dụ sau:

    function factorial($n) {
    
        function check($n) {
            if(!is_int($n) || $n < 0) return true;
            return false;
        }
    
        function prod($n){
            $p = 1;
            for($i = 1; $i <= $n; ++$i)
                $p *= $i;
            return $p;
        }
    
        return check($n) ? false : prod($n);
    }
    
    echo "$n! = ", factorial($n);

    Trong ví dụ này chúng ta khai báo và sử dụng hai hàm check và prod bên trong hàm factorial.

    Hàm lồng nhau trong PHP có một điểm đặc biệt.

    • Mặc dù gọi là hàm lồng nhưng bạn vẫn có thể gọi nó từ ngoài hàm cha, giống hệt như hàm cha!
    • Hàm lồng chỉ tồn tại (bên ngoài hàm cha) sau khi gọi hàm cha. Nếu bạn sử dụng hai hàm lồng này bên ngoài hàm factorial trước khi gọi chính hàm factorial() thì sẽ gặp lỗi ‘Call to undefined function’.
    • Hàm lồng sau khi gọi hàm cha sẽ có phạm vi là global giống hệt như hàm cha.

    Những đặc điểm này làm cho hàm lồng trong PHP khá rắc rối. Nhìn chung nếu đã có nhu cầu dùng làm lồng, bạn nên chuyển sang xây dựng class.

    Một số trường hợp sử dụng hàm đặc biệt trong PHP

    Hàm tự gọi chính nó – đệ quy trong PHP

    Đệ quy là hiện tượng một hàm tự gọi chính nó!

    Hãy xem ví dụ về định nghĩa giai thừa của một số nguyên n:

    Theo định nghĩa này, giá trị của n! bằng 1 nếu n = 0, và bằng n * (n-1)! nếu n > 0. Đây là cách định nghĩa giai thừa theo kiểu đệ quy – định nghĩa một khái niệm qua chính nó!

    Với định nghĩa giai thừa như trên chúng ta có thể viết lại hàm tính giai thừa trong PHP như sau:

    function factorial($n){
        if($n == 0) return 1;
        return $n * factorial($n - 1);
    }

    Để ý rằng bên trong thân hàm factorial có lời gọi đến chính nó!

    Có thể thấy, việc gọi đệ quy cho phép viết chương trình rất ngắn gọn và bám sát theo định nghĩa toán học của hàm.

    Tuy nhiên, việc sử dụng đệ quy không được khuyến khích vì nó làm phức tạp quá trình gọi hàm. Trừ khi bắt buộc hay không còn lựa chọn nào khác, bạn nên sử dụng vòng lặp thay cho gọi hàm kiểu đệ quy.

    Gọi hàm động

    Hàm trong PHP rất linh động cả trong khai báo và sử dụng. Một trong những đặc thù của hàm trong PHP là khả năng gọi hàm từ chuỗi ký tự hoặc tên biến. Để dễ hiểu, hãy cùng xem ví dụ sau đây:

    function hello() { echo 'Hello world!' . "\r\n"; }
    function bye() { echo 'Goodbye world!' . "\r\n"; }
    function welcome($name) { echo "Hello, $name. Welcome to heaven!" . "\r\n"; }
    while (true) {
        $command = readline('Enter a command: ');
        if ($command == 'welcome') {
            $name = readline('Your name: ');
            $command($name);
        } else
            $command();
    }

    Đoạn script này cho phép thực hiện hàm theo chuỗi người dùng nhập vào:

    Enter a command: hello
    Hello world!
    Enter a command: bye
    Goodbye world!
    Enter a command: welcome
    Your name: Donald
    Hello, Donald. Welcome to heaven!
    Enter a command:

    Trong ví dụ trên chúng ta định nghĩa ba hàm thông thường hello, bye, và welcome.

    Trong vòng lặp chúng ta yêu cầu người dùng nhập một chuỗi lệnh. Giá trị chuỗi này lưu vào biến $command.

    Hãy để ý điểm đặc biệt của biến $command: bạn có thể biến $command thành lời gọi hàm nếu thêm cặp dấu ngoặc tròn () vào sau tên biến $command(). Tính năng này trong PHP được gọi là dynamic function name / dynamic function call / variable function.

    PHP sẽ tìm và thực thi hàm có tên trùng với giá trị của biến.

    Tính năng này khiến việc gọi hàm theo yêu cầu của người dùng trở nên vô cùng đơn giản. Một chuỗi người dùng nhập vào có thể chuyển thẳng thành lời gọi hàm. Nếu không có tính năng này bạn phải xây dựng các cơ chế ánh xạ yêu cầu với lời gọi hàm, ví dụ, sử dụng lệnh switch-case.

    Kết luận

    Trong bài học này chúng ta học cách tự xây dựng hàm trong PHP.

    • Hàm trong PHP được xây dựng với từ khóa function với một số quy tắc cú pháp riêng.
    • Hàm có thể nhận tham số và có thể trả về kết quả.
    • Tham số và kết quả không bị giới hạn và kiểm tra về kiểu.
    • Hàm trong PHP có thể được khai báo trong lệnh khác hoặc trong hàm khác.
    • PHP cho phép hàm tự gọi chính nó và gọi hàm dựa trên chuỗi ký tự.

    + Nếu bạn thấy site hữu ích, trước khi rời đi hãy giúp đỡ site bằng một hành động nhỏ để site có thể phát triển và phục vụ bạn tốt hơn.
    + Nếu bạn thấy bài viết hữu ích, hãy giúp chia sẻ tới mọi người.
    + Nếu có thắc mắc hoặc cần trao đổi thêm, mời bạn viết trong phần thảo luận cuối trang.
    Cảm ơn bạn!

    Subscribe
    Notify of
    guest
    2 Thảo luận
    Oldest
    Newest
    Inline Feedbacks
    View all comments
    Khoa

    Bạn nói không chính xác về hàm lồng trong lệnh và hàm lồng trong hàm rồi
    Cụ thể về ví dụ hàm lồng trong hàm
    Nếu ta gọi
    factorial(0);
    echo check(5);
    echo prod(6);
    Chương trình sẽ chạy bình thường và cho ra kết quả đúng

    Nhật Linh

    Tác giả viết chưa đủ hết ý chứ không phải sai đâu ạ. Nếu bạn không gọi hàm factorial() trước mà đã gọi check() và prod() thì sẽ dính lỗi Call to undefined function.
    Khi bạn gọi hàm cha, PHP sẽ khai báo các hàm con và khi này các hàm con sẽ có phạm vi tác dụng là global giống như hàm cha.
    Cái loại hàm lồng nhau này của PHP dở hơi như vậy đấy.