Tản mạn về function trong PHP

php May 28, 2021

Chào mọi người!
Như tiêu đề bài viết, hãy cùng mình tìm hiểu cách thức cũng như một vài lưu ý nhỏ khi sử dụng function trong PHP.


Type declaration

Là cách khai báo chỉ ra kiểu của tham số truyền vào hàm, giá trị trả về của hàm hoặc với cả thuộc tính của lớp.

sử dụng với tham số truyền vào hàm:

// php 7.4
function foo(bool $a) // $a được định kiểu là boolean
{ 
    var_dump($a); 
}

foo(true); // bool(true)
foo(new stdClass()); /*Trả về lỗi: Uncaught TypeError: Argument 1 passed to foo() must be of the type bool*/

giá trị trả về của hàm:

function example($x): int // hàm example cần trả về số nguyên
{
    // do something
    return $x;
}

var_dump(example(1)); // int(1)
var_dump(example([1, 2])); /* Error: Return value of example() must be of the type int*/

Hai ví dụ trên cho thấy được ưu điểm của cách thức này là giúp kiểm soát đầu vào ra của hàm một cách khá trặt trẽ, cũng như dễ dàng tìm được lỗi phát sinh từ đâu.

Tuy nhiên,
vô tình truyền hoặc trả về không đúng kiểu, trình biên dịch sẽ cố gắng ép kiểu đó theo như khai báo và nếu thành công là không hề có lỗi.
(lấy ví dụ function foo ở trên: thay vì truyền kiểu boolean vào, ta có thể truyền vào giá trị int)

Theo cá nhân mình thì type declaration thường được áp dụng trong thư viện hoặc framework để người sử dụng tuân theo những quy ước đã định trước.


Splat operator(...)

Splat operator đại diện cho các parameter mà ta truyền, bên trong hàm sẽ nhận được mảng các giá trị đó. Để dễ hình dung cách sử dụng ta có ví dụ về hàm tính tổng 2 số:

function sum($a, $b): int 
{
    return $a + $b; 
}

Nhưng giờ tạo hàm để tính tổng tuỳ ý n số thì sao? Sẽ có một vài cách nhưng splat operator đáp ứng được mong muốn này một cách đơn giản, ngắn gọn:

function sum(...$args): int 
{
    return array_sum($args);	
}
echo sum(1, 2, 3, 4);

lưu ý là toán tử splat bắt buộc phải đứng sau cùng các tham số khác nếu có.


Pass by value, pass by reference

Là hai khái niệm quen thuộc khi lập trình hàm nhưng cũng cần phải cẩn trọng, theo ý hiểu của mình thì:

Pass by value: truyền giá trị của biến vào hàm -> hàm thực thi -> tạo bản sao của biến -> biến không thay đổi
Pass by reference: truyền tham chiếu - địa chỉ ô nhớ của biến -> hàm thực thi -> biến thay đổi

Cách thực hiện truyền tham chiếu:

  • Ngoại trừ object, muốn điều này xảy ra với kiểu dữ liệu nguyên thuỷ như int, float, string sẽ cần thêm '&' đứng trước
$str = 'Xin ';
function example(&$str)
{
    $str .= 'chao!';
}
example($str);
echo $str; // Xin chao!
  • Hàm cũng có thể trả về một tham chiếu
$x = 1;
function &example(&$x)
{
    return $x;
}
$y  = &example($x);
$y = 3;
var_dump($x); // int(3)

Anonymous function

Hay còn được gọi là closure, là hàm không có tên cụ thể khi tạo. Đặc biệt hữu ích khi có thể đóng vai trò là một callback function hay một hàm chỉ sử dụng một lần

  • Với function thông thường
function func($value) 
{
    return $value * 2;
}
$result = array_map('func', range(1, 5));
print_r($result);
  • Và khi sử dụng closure
$result = array_map(function ($value) {
    return $value * 2;
}, range(1, 5));
print_r($result);

Các closure có thể nhận các giá trị bên ngoài phạm vi của chúng bằng cách sử dụng 'use ($var)'

$message = 'hello';

$example = function () use ($message) {
    var_dump($message);
};
$example(); // hello
$message = 'olleh';
$example(); // hello

'use' sẽ lấy giá trị biến lúc hàm được định nghĩa chứ không phải lúc gọi. Chính vì thế mà ở lần gọi hàm thứ 2 dù có thay đổi $message trước đó nhưng kết quả vẫn không thay đổi.

Closure cũng có nhiều ứng dụng khác trong thực tế, một vài ví dụ trong laravel:

// Trả về view không thông qua controller
Route::get('/', function() {
    return view('home');
});

// Thực hiện truy vấn 
$model->where(function($query) {
    $query->orWhereNull('cancel');
    $query->orWhere('cancel', '!=', Order::CANCEL_ORDER);
})->get();


Arrow Functions

Khác biệt hoàn toàn với khái niệm của Javascript, PHP  arrow function thực ra là bản nâng cấp của anonymous function:

  • Cách viết ngắn gọn hơn: fn (argument_list) => expr
    với expr là một biểu thức, cũng chính là giá trị trả về
  • Các biến bên ngoài phạm vi được truyền vào tự động không cần thông qua use ()

Viết lại ví dụ trước:

$x = 2;
$result = array_map(fn($value) => $value * $x, range(1, 5));
print_r($result);

Kết

Với một khía cạnh nhỏ của ngôn ngữ, hi vọng qua bài viết này sẽ giúp mọi người hiểu thêm về function trong PHP.


Tài liệu tham khảo:
https://www.php.net/manual/en/language.functions.php

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.