PHP: Phạm vi của biến
Phạm vi của biến là bối cảnh trong đó nó được định nghĩa. Đa số các biến PHP chỉ có một phạm vi duy nhất. Phạm vi này bao gồm cả các file được include và require tương ứng. Ví dụ:
<?php
$a = 1;
include 'b.inc';
?>
Ở ví dụ trên thì biến $a sẽ sử dụng được trong file b.inc. Tuy nhiên, trong các hàm do người dùng định nghĩa thì ta không sử dụng được biến khai báo ngoài hàm một cách thông thường. Ví dụ:
<?php
$a = 1; /* phạm vi toàn cục */
function test()
{
echo $a; /* không sử dụng được biến toàn cục $a như thế này */
}
test();
?>
Ở đoạn mã trên thì ta không thể sử dụng được biến $a trong hàm test() như vậy, lúc này biến $a trong hàm test() lại được hiểu là biến cục bộ của chính hàm test() đó. Để khắc phục điều này thì ta cần phải sử dụng đến từ khóa global.
Từ khóa global
Ta xét một ví dụ sử dụng từ khóa global như sau:
Ví dụ 1: Sử dụng global
<?php
$a = 1;
$b = 2;
function Sum()
{
global $a, $b;
$b = $a + $b;
}
Sum();
echo $b;
?>
Đoạn mã trên sẽ in ra 3. $a và $b là các biến global vì chúng được khai báo ngoài hàm, và chúng được sử dụng trong hàm Sum() bằng cách khai báo lại và đặt từ khóa global để trình dịch hiểu chúng là các biến toàn cục. Một hàm có quyền sử dụng bất kỳ biến global nào và với số lượng tùy ý.
Ở ví dụ dưới đây thể hiện một cách khác để sử dụng biến toàn cục, đó là sử dụng mảng $GLOBALS được định nghĩa sẵn trong PHP:
Ví dụ 2: Sử dụng $GLOBALS thay cho global
<?php
$a = 1;
$b = 2;
function Sum()
{
$GLOBALS['b'] = $GLOBALS['a'] + $GLOBALS['b'];
}
Sum();
echo $b;
?>
Mảng $GLOBALS là mảng kết hợp với tên của biến toàn cục với giá trị mà biến toàn cục chứa, theo đó, mỗi biến toàn cục sẽ được hiểu là một phần tử của mảng này, với chỉ số của phần tử mảng chính là tên của biến toàn cục, còn giá trị của phần tử đó chính là giá trị của biến toàn cục. Lưu ý ta có thể sử dụng $GLOBALS ở bất kỳ đâu, đó là bởi vì nó là siêu biến toàn cục (superglobal). Dưới đây là một ví dụ demo về khả năng của siêu biến toàn cục:
Ví dụ 3: Demo khả năng của siêu biến toàn cục
<?php
function test_global()
{
// Phần lớn các biến được định nghĩa không phải là "super" và yêu cầu
// sử dụng 'global' để dùng được trong hàm.
global $HTTP_POST_VARS;
echo $HTTP_POST_VARS['name'];
// Siêu biến toàn cục sử dụng được ở mọi nơi và không yêu cầu từ khóa
// 'global'. Tuy nhiên, hiện nay thì siêu biến toàn cục dạng như
// HTTP_POST_VARS không còn được khuyến khích sử dụng.
echo $_POST['name'];
}
?>
Lưu ý:
Không được sử dụng từ khóa global bên ngoài hàm, khi đó sẽ phát sinh lỗi. Tuy nhiên, ta có thể sử dụng điều này nếu nó nằm trong tập tin được gọi từ hàm.
Sử dụng biến static
Một đặc tính khác của phạm vi biến là biến static. Biến tĩnh chỉ dùng được trong hàm, tuy nhiên thì giá trị của nó không bị mất đi sau khi hàm được thực thi xong. Xét ví dụ sau:
Ví dụ 4: demo sự cần thiết của biến static
<?php
function test()
{
$a = 0;
echo $a;
$a++;
}
?>
Ở hàm trên thì câu lệnh $a++; tỏ ra không có tác dụng mỗi khi hàm được gọi, bởi vì mỗi khi được gọi thì giá trị của biến $a lại được đặt thành 0. Để khắc phục điều này thì ta chỉ cần khai báo biến $a là biến tĩnh:
Ví dụ 5: sử dụng biến tĩnh
<?php
function test()
{
static $a = 0;
echo $a;
$a++;
}
?>
Ở ví dụ trên, giá trị của biến $a vẫn được giữ lại, vì thế mỗi khi gọi hàm test() thì giá trị của $a lại tăng một đơn vị.
Biến tĩnh cũng cung cấp một cách để đối phó với hàm đệ quy. Hàm đệ quy là hàm có đặc điểm nó gọi đến chính nó. Cần hết sức cẩn thận khi sử dụng hàm đệ quy bởi vì nó có khả năng đệ quy vĩnh viễn. Bạn phải đảm bảo rằng bạn đã cung cấp cách để dừng đệ quy cho hàm. Ví dụ sau đây sẽ đếm từ 1 đến 10 sử dụng biến tĩnh $count:
Ví dụ 6: Biến tĩnh với hàm đệ quy
<?php
function test()
{
static $count = 0;
$count++;
echo $count;
if ($count < 10) {
test();
}
$count--;
}
?>
Lưu ý:
Từ bản PHP 5.6 ta có thể gán giá trị cho biến tĩnh là kết quả của một biểu thức, nhưng không được gán cho biến tĩnh là lời gọi tới hàm nào đó.
Ví dụ 7: Khai báo biến tĩnh
<?php
function foo(){
static $int = 0; // đúng
static $int = 1+2; // đúng (PHP 5.6)
static $int = sqrt(121); // sai (vì gán lời gọi hàm cho biến tĩnh)
$int++;
echo $int;
}
?>
Lưu ý:
Những biến tĩnh được được giải quyết trong thời gian biên dịch.
Tham chiếu với các biến global và static
Zend Engine 1 và PHP 4 thực thi các bổ từ static và global theo hình thức là các tham chiếu. Ví dụ như một biến toàn cục được đưa vào trong một hàm với từ khóa global thì bản chất là tạo ra một tham chiếu tới biến toàn cục. Điều này có thể dẫn đến hành vi bất ngờ đối với các địa chỉ như ví dụ sau:
<?php
function test_global_ref() {
global $obj;
$obj = &new stdclass;
}
function test_global_noref() {
global $obj;
$obj = new stdclass;
}
test_global_ref();
var_dump($obj);
test_global_noref();
var_dump($obj);
?>
Ví dụ trên sẽ in ra:
object(stdClass)(0) {
}
Hành vi tương tự cũng được áp dụng cho câu lệnh static. Khi đó các tham chiếu sẽ không được lưu trữ dưới dạng tĩnh:
<?php
function &get_instance_ref() {
static $obj;
echo 'Static object: ';
var_dump($obj);
if (!isset($obj)) {
// Gán một tham chiếu tới biến tĩnh
$obj = &new stdclass;
}
$obj->property++;
return $obj;
}
function &get_instance_noref() {
static $obj;
echo 'Static object: ';
var_dump($obj);
if (!isset($obj)) {
// Gán một đối tượng cho biến tĩnh
$obj = new stdclass;
}
$obj->property++;
return $obj;
}
$obj1 = get_instance_ref();
$still_obj1 = get_instance_ref();
echo "\n";
$obj2 = get_instance_noref();
$still_obj2 = get_instance_noref();
?>
Ví dụ trên sẽ in ra:
Static object: NULL
Static object: NULL
Static object: object(stdClass)(1) {
["property"]=>
int(1)
}
Ví dụ trên cho thấy khi ta gán một tham chiếu tới biến tĩnh thì nó không hề được lưu lại khi ta gọi hàm &get_instance_ref() lần thứ hai.