Hướng dẫn dùng refernece trong PHP

Tham chiếu trong PHP, bạn đã hiểu rõ về nó ?

Tổng hợp PHP 2,959 Lượt xem

1. Tham chiếu trong PHP là gì ?

References in PHP are a means to access the same variable content by different names.

Trích từ định nghĩa từ //php.net/, ta có thể hiểu nôm na rằng tham chiếu trong PHP nghĩa là truy cập vào cũng một nội dung nhưng khác tên. Có thể định nghĩa trên bạn đọc vẫn còn hơi khó hiểu? Mình sẽ đưa ra một cách nói khác, hi vọng bạn sẽ dễ hiểu hơn.

Tham chiếu trong PHP có nghĩa là có nghĩa là 1 biến chiếu đến vùng nhớ của 1 biến khác. 2 biến này dùng chung 1 vùng nhớ chứa giá trị nên khi dùng phép gán cho biến này thì giá trị của biến kia cũng thay đổi . Tham chiếu trong PHP có ký hiệu là dấu &

Bạn vẫn còn mơ hồ? Không sao, mình sẽ đưa ra ví dụ sau đây:

Giả sử mình tạo 1 file tên là kungfuphp.txt, trong file này ta điền nội dung đại loại là “kungfuphp xin chào các bạn”, sau đó ta tạo ra 2 shortcut [lối tắt] của file kungfuphp.txt này và đặt ở 2 vị trí khác nhau, chẳng hạn file shortcut 1 mình đặt ở desktop, file còn lại đặt ở thư mục document. Bạn thấy đấy, khi double click lên 2 file này, dù là ở 2 vị trí khác nhau nhưng nó đều cùng trỏ về 1 file là kungfuphp.txt với cùng 1 nội dung là “kungfuphp xin chào các bạn”, nhưng khi mình thay đổi nội dung file kungfuphp.txt thành “hello các bạn”, thì 2 shortcut đã tạo kia cũng trỏ đến nội dung đã thay đổi là “hello các bạn”.

2. Các ví dụ về tham chiếu trong PHP

Ví dụ 1 : Đoạn code sau :

nghiễm nhiên sẽ cho kết quả là :

5
10

Nhưng khi mình thay đổi lại đoạn code trên 1 chút

kết quả sẽ cho ra như sau.

10
10

Đến đây, bạn đã phần nào mường tượng được trong đầu mình về khái niệm tham chiếu rồi chứ ? Ở đây ta thấy rằng, khi dùng phép gán $b = &$a, ta đã cho phép biến $b trỏ tới vùng nhớ của biến $a. Nên khi biến $b thay đổi giá trị thì biến $a  cũng thay đổi giá trị [dành 1 phút liên tưởng lại ví dụ file kungfuphp.txt ở trên bạn nhé ]

Ví dụ 2 : Cho đoạn code sau :

Bạn thấy đó, kết quả của phép toán trên là 6, thông thường nếu không truyền tham chiếu thì kết quả phép toán trên, khi echo biến $a, nó vẫn giữ nguyên giá trị là 5. Nhưng khi truyền tham chiếu, bạn đã cho phép một bản sao khác của biến $a chính sử dụng vùng nhớ của biến $a và ghi đè lên giá trị của biến $a là 6. Để dễ hiểu hơn, bạn hãy viết lại function trên như bên dưới đây và tự nghiệm ra cho bản thân mình nhé

Ví dụ 3 :

Theo mặc định, đối số của function được truyền theo giá trị nào đó [do đó nếu giá trị của các đối số trong các chức năng bị thay đổi, thì giá trị của nó không bị thay đổi bên ngoài của function]. Vì vậy, để cho phép một function sửa đổi giá trị mặc đinh của đối số truyền vào, thì phải gán tham chiếu cho nó.

Bạn vẫn còn chưa hiểu và có thắc mắc, hãy để lại comment bên dưới nhé. Thân !

Nguồn : kungfuphp.com

 

Gợi ý : Bạn là người mới và muốn tìm hiểu về Bitcoin nhưng không biết bắt đầu từ đâu? Click xem ngay Hướng Dẫn Đầu Tư Bitcoin Cho Người Mới nhé!

Chào các bạn, chắc hẳn ai trong chúng ta đã biết về biến và tham chiếu khi học các môn cơ sở lập trình khi mới vào nghề. Thế nhưng khi làm việc với PHP một vài người tưởng như đã quên là nó có tồn tại, hoặc một vài người thì biết nó có nhưng lại không biết xài nó như nào; thậm chí một vài trong chúng ta đã từng xài nhưng lại phát sinh một số lỗi không ngờ tới.

Nội dung chính Show

Để giúp các bạn hiểu rõ hơn về biến và tham chiếu trong PHP, đồng thời cũng giúp mình có cơ hội được tìm hiểu kỹ hơn về tính năng, đặc điểm này, hôm nay mình sẽ viết về vấn đề tham chiếu với biến trong PHP.

Để đảm bảo sự chính xác nguyên vẹn của thuật ngữ cũng như các định nghĩa, trong bài viết này mình xin phép dùng một số từ nguyên mẫu so với định nghĩa của nó:

  • $a;
    $b = &$a;
    
    3: Lớp
  • $a;
    $b = &$a;
    
    4 ,
    $a;
    $b = &$a;
    
    5: Các thuộc tính trong class
  • $a;
    $b = &$a;
    
    6: kiểu dữ liệu là các đối tượng [có thể là object hoặc class]
  • $a;
    $b = &$a;
    
    7: kiểu dữ liệu là mảng
  • $a;
    $b = &$a;
    
    8: tham chiếu

Trong bài này, mình định nghĩa hai functions giúp mình in ra giá trị dễ nhìn hơn:

// Dùng để log trên màn hình console
function beatifyLog[...$arr]
{
    foreach [$arr as $item] {
        print_r[$item];
    }
}
// Dùng để log trên giao diện html
function beatifyHtml[...$arr]
{
    echo '
';
    foreach [$arr as $item] {
        print_r[$item];
    }
    echo '
'; }

Biến tham chiếu [reference variables]

Biến

$a;
$b = &$a;
9 lúc này được gọi là biến tham chiếu của
$arr = [
    'key'       => 'value',
    'other_key' => 'other_value',
];
$obj = [object] $arr;

$arr2 = $arr;
$obj2 = $obj;

$arr['key'] = 'another_value';
$obj2->key  = 'another_value';

beatifyLog['arr: ', $arr, 'arr2: ', $arr2];
echo "=====\n";
beatifyLog['obj: ', $obj, 'obj2: ', $obj2];
0, còn gọi là [alias], khi
$a;
$b = &$a;
9 và
$arr = [
    'key'       => 'value',
    'other_key' => 'other_value',
];
$obj = [object] $arr;

$arr2 = $arr;
$obj2 = $obj;

$arr['key'] = 'another_value';
$obj2->key  = 'another_value';

beatifyLog['arr: ', $arr, 'arr2: ', $arr2];
echo "=====\n";
beatifyLog['obj: ', $obj, 'obj2: ', $obj2];
0 trỏ về cùng một địa chỉ bộ nhớ trên máy. Lúc này,
$arr = [
    'key'       => 'value',
    'other_key' => 'other_value',
];
$obj = [object] $arr;

$arr2 = $arr;
$obj2 = $obj;

$arr['key'] = 'another_value';
$obj2->key  = 'another_value';

beatifyLog['arr: ', $arr, 'arr2: ', $arr2];
echo "=====\n";
beatifyLog['obj: ', $obj, 'obj2: ', $obj2];
0 hoặc
$a;
$b = &$a;
9 thay đổi giá trị thì biến còn lại cũng sẽ thay đổi theo.

$a;
$b = &$a;

Ví dụ:

Tham chiếu trong object và array

Trong PHP, các biến object là tham chiếu, trong khi mảng thì không. Ví dụ:

$arr = [
    'key'       => 'value',
    'other_key' => 'other_value',
];
$obj = [object] $arr;

$arr2 = $arr;
$obj2 = $obj;

$arr['key'] = 'another_value';
$obj2->key  = 'another_value';

beatifyLog['arr: ', $arr, 'arr2: ', $arr2];
echo "=====\n";
beatifyLog['obj: ', $obj, 'obj2: ', $obj2];

Kết quả là:

Như vậy muốn array tham chiếu được thì như thế nào? Minh xin chia sẻ một cách dùng trong foreach hữu ích giúp chúng ta có được điều đó:

$arr = [1, 2, 3, 4,];
beatifyLog['Initial Value: ', $arr];
echo "=====\n";
foreach [$arr as $item] {
    $item++;
}
beatifyLog['Foreach without reference: ', $arr];
echo "=====\n";
foreach [$arr as &$item] {
    $item++;
}
beatifyLog['Foreach with reference: ', $arr];

Và đây là kết quả sau khi chạy các dòng lệnh trên:

Tham chiếu trong function

PHP có hỗ trợ biến tham chiếu khi đưa vào function, cú pháp:

function fnName[&$a, $b]
{}

Ở đây

$arr = [
    'key'       => 'value',
    'other_key' => 'other_value',
];
$obj = [object] $arr;

$arr2 = $arr;
$obj2 = $obj;

$arr['key'] = 'another_value';
$obj2->key  = 'another_value';

beatifyLog['arr: ', $arr, 'arr2: ', $arr2];
echo "=====\n";
beatifyLog['obj: ', $obj, 'obj2: ', $obj2];
0 là biến tham chiếu, còn
$a;
$b = &$a;
9 là biến bình thường. Xét ví dụ cụ thể sau:

function test[&$a, $b]
{
    $a++;
    $b++;
}
$a = 3;
$b = 4;
beatifyLog['Before: ', 'a=', $a, '; b=', $b];
echo "\n";
test[$a, $b];
beatifyLog['After:  ', 'a=', $a, '; b=', $b];

Kết quả:

Một số vấn đề khi dùng reference variable

Đây là phần mình mong chờ nhất trong bài viết này. Dưới đây mình xin chia sẻ các lỗi mình đã gặp và cách xử lí khi làm việc với reference variable. Dĩ nhiên sẽ còn thiếu rất nhiều trường hợp, mong các bạn có thể chia sẻ thêm để giúp ích hơn cho bài viết của mình cũng như các bạn khác được hiểu rõ hơn.

Reference object

Để nói về vấn đề này, mình xin đưa ra một bài toán nho nhỏ để minh hoạ nhé.

  • Cho class

    $arr = [
        'key'       => 'value',
        'other_key' => 'other_value',
    ];
    $obj = [object] $arr;
    
    $arr2 = $arr;
    $obj2 = $obj;
    
    $arr['key'] = 'another_value';
    $obj2->key  = 'another_value';
    
    beatifyLog['arr: ', $arr, 'arr2: ', $arr2];
    echo "=====\n";
    beatifyLog['obj: ', $obj, 'obj2: ', $obj2];
    
    7 cùng các method như bên dưới

    class Date
    {
        private $d = 0;
        private $m = 0;
        private $Y = 0;
    
        public function __construct[int $year, int $month, int $day]
        {
            $this->Y = $year;
            $this->m = $month;
            $this->d = $day;
        }
    
        private function addSubZero[int $i]: string
        {
            return $i > 9 ? $i : "0$i";
        }
    
        public function toString[]: string
        {
            $Y = $this->Y;
            $m = $this->addSubZero[$this->m];
            $d = $this->addSubZero[$this->d];
    
            return "$Y-$m-$d";
        }
    
        public function addYears[int $num]: Date
        {
            $this->Y += $num;
    
            return $this;
        }
    }
    
  • Cho một array các ngày tháng ngẫu nhiên, ví dụ

    $db = [
        '2021-01-02',
        '2020-01-02',
        '2021-01-01',
        '2020-01-05',
        '2019-01-05'
    ];
    
  • Nhập vào một ngày tháng năm [đúng định dạng], xuất ra các giá trị trong vòng 1 năm kể từ ngày nhập.

    Ví dụ input

    $arr = [
        'key'       => 'value',
        'other_key' => 'other_value',
    ];
    $obj = [object] $arr;
    
    $arr2 = $arr;
    $obj2 = $obj;
    
    $arr['key'] = 'another_value';
    $obj2->key  = 'another_value';
    
    beatifyLog['arr: ', $arr, 'arr2: ', $arr2];
    echo "=====\n";
    beatifyLog['obj: ', $obj, 'obj2: ', $obj2];
    
    8. Kết quả mong muốn với array như trên là
    $arr = [
        'key'       => 'value',
        'other_key' => 'other_value',
    ];
    $obj = [object] $arr;
    
    $arr2 = $arr;
    $obj2 = $obj;
    
    $arr['key'] = 'another_value';
    $obj2->key  = 'another_value';
    
    beatifyLog['arr: ', $arr, 'arr2: ', $arr2];
    echo "=====\n";
    beatifyLog['obj: ', $obj, 'obj2: ', $obj2];
    
    9,
    $arr = [
        'key'       => 'value',
        'other_key' => 'other_value',
    ];
    $obj = [object] $arr;
    
    $arr2 = $arr;
    $obj2 = $obj;
    
    $arr['key'] = 'another_value';
    $obj2->key  = 'another_value';
    
    beatifyLog['arr: ', $arr, 'arr2: ', $arr2];
    echo "=====\n";
    beatifyLog['obj: ', $obj, 'obj2: ', $obj2];
    
    8 và
    $arr = [1, 2, 3, 4,];
    beatifyLog['Initial Value: ', $arr];
    echo "=====\n";
    foreach [$arr as $item] {
        $item++;
    }
    beatifyLog['Foreach without reference: ', $arr];
    echo "=====\n";
    foreach [$arr as &$item] {
        $item++;
    }
    beatifyLog['Foreach with reference: ', $arr];
    
    1.

** Dưới đây là đoạn code của mình đề xuất:

// Should be 2021-01-01
$curYear  = new Date[2021, 1, 1];

// Should be 2020-01-01
$prevYear = $curYear;
$prevYear->addYears[-1];

// Should return 2020-01-02, 2021-01-01 and 2020-01-05
beatifyLog['Result:', array_filter[$db, function [$a] use [$curYear, $prevYear] {
    return $a >= $prevYear->toString[] && $a toString[];
}]];

Chạy thử đoạn code trên:

Xem kết quả, không như mong muốn, dể biết chi tiết, mình debug đoạn code trên:
// Should be 2021-01-01
$curYear  = new Date[2021, 1, 1];
beatifyLog['Initial CurYear: ' . $curYear->toString[]];
echo "\n";

// Should be 2020-01-01
$prevYear = $curYear;
$prevYear->addYears[-1];

// Should return 2020-01-02, 2021-01-01 and 2020-01-05
beatifyLog['Result: ', array_filter[$db, function [$a] use [$curYear, $prevYear] {
    return $a >= $prevYear->toString[] && $a toString[];
}]];

echo "\n";
beatifyLog['CurYear:', $curYear->toString[]];
echo "\n";
beatifyLog['PrevYear:', $prevYear->toString[]];

Kết quả:

Vậy lí do là gì ? Là vì object reference đó các bạn [mình đã nhắc ở trên rồi].

Thế thì có cách nào để sửa không nhỉ? Dĩ nhiên là có rồi, mình thử dùng

$arr = [1, 2, 3, 4,];
beatifyLog['Initial Value: ', $arr];
echo "=====\n";
foreach [$arr as $item] {
    $item++;
}
beatifyLog['Foreach without reference: ', $arr];
echo "=====\n";
foreach [$arr as &$item] {
    $item++;
}
beatifyLog['Foreach with reference: ', $arr];
2 của PHP nhé. Dưới đây là code mình có chỉnh sửa tí xíu:

$a;
$b = &$a;
0

Kết quả đây:

Kết quả đúng như mong muốn rồi!! Các bạn chú ý khi thao tác với object trong PHP nhé.

Reference array

Mình xin phép dùng lại một tí ví dụ ở phần reference trong array, đồng thời chỉnh sửa lại một tí:

$a;
$b = &$a;
1

Và đây là kết quả:

Để giải thích vấn đề này thì có lẽ là nên đi sâu vào phần bộ nhớ của reference variable, nên mình xin phép hẹn lại lần sau nhé. Ở đây mình chỉ đề xuất phương pháp để "né" thôi ạ. Xin xem mình viết lại như sau:

$a;
$b = &$a;
2

Đơn giản là thay bằng tên biến khác thôi, và đây là kết quả:

Kết luận

Trên đây là những gì mình đã nghiên cứu, đồng thời cũng thu thập từ một số kinh nghiệm của mình về reference trong PHP, hi vọng phần nào hữu ích cho các bạn trong một số trường hợp.

Nguồn tham khảo

Bài viết này mình chủ yếu tham khảo trên trang document của PHP và phần còn lại là do những kinh nghiệm thực tế mình có được.

Chủ Đề