Tìm hiểu phạm vi sử dụng biến Scope trong PHP

PHP Tutorial | by Học PHP

Giống như việc mỗi người trong một tòa nhà có phạm vi hoạt động riêng của mình (ai đó chỉ ở văn phòng của họ, trong khi người quản lý có thể đi khắp các tầng), các biến trong PHP cũng có những "ranh giới" tồn tại và khả năng truy cập riêng. Hiểu rõ những ranh giới này không chỉ giúp bạn tránh được những lỗi "biến không xác định" khó chịu, mà còn là chìa khóa để viết code PHP có cấu trúc, dễ quản lý và mạnh mẽ hơn rất nhiều.

Trong bài viết này, mình sẽ cùng nhau tìm hiểu sâu hơn về các loại phạm vi biến trong PHP – từ những biến cục bộ chỉ tồn tại trong một khoảnh khắc ngắn ngủi, đến những biến toàn cục có thể được "nhìn thấy" từ khắp mọi nơi, và cả những biến có hành vi đặc biệt khác. Hãy cùng tìm hiểu để làm chủ cách các biến "sống" và "chết" trong ứng dụng PHP của bạn nhé!

Phạm vi biến (Variable Scope) trong PHP

Phạm vi biến là gì?

Để dễ hình dung, bạn có thể nghĩ phạm vi của một biến như là "khu vực ảnh hưởng" hoặc "lãnh thổ" mà một biến có thể được nhận diện và sử dụng trong chương trình của bạn. Khi bạn khai báo một biến, nó không tự động có sẵn ở mọi nơi trong toàn bộ mã của bạn. Thay vào đó, nó chỉ tồn tại và có thể được truy cập trong một giới hạn nhất định.

  • Định nghĩa: Phạm vi của một biến xác định nơi mà biến đó có thể được truy cập (hoặc "nhìn thấy") trong suốt quá trình thực thi chương trình PHP. Nó quy định "tuổi thọ" và "tầm nhìn" của biến.

  • Ví dụ minh họa đơn giản:

<?php
$bienBenNgoai = "Tôi là biến bên ngoài."; // Khai báo ở đây

function motHam() {
    // echo $bienBenNgoai; // Nếu không có khai báo gì thêm, biến này sẽ không được "nhìn thấy" ở đây.
    $bienBenTrong = "Tôi là biến bên trong hàm."; // Khai báo ở đây
    echo $bienBenTrong . "<br>"; // Có thể truy cập $bienBenTrong
}

motHam();
echo $bienBenNgoai . "<br>"; // Có thể truy cập $bienBenNgoai
// echo $bienBenTrong; // Lỗi! Biến này không được "nhìn thấy" ở đây.
?>

Tầm quan trọng của việc hiểu phạm vi biến

Việc nắm vững phạm vi biến là một trong những kiến thức nền tảng quan trọng nhất trong lập trình PHP nói riêng và lập trình nói chung. Nó không chỉ giúp bạn viết code chạy đúng mà còn ảnh hưởng trực tiếp đến chất lượng và khả năng bảo trì của ứng dụng:

  • Giúp tránh lỗi "Undefined variable": Đây là một trong những lỗi phổ biến nhất mà lập trình viên PHP thường gặp phải. Khi bạn cố gắng sử dụng một biến ở nơi nó không nằm trong phạm vi (không "nhìn thấy" được), PHP sẽ báo lỗi hoặc cảnh báo, khiến chương trình bị gián đoạn hoặc hoạt động không mong muốn. Hiểu phạm vi giúp bạn đặt biến đúng chỗ.
  • Quản lý bộ nhớ hiệu quả: Biến chỉ tồn tại trong phạm vi của nó. Khi phạm vi kết thúc (ví dụ: một hàm hoàn thành công việc), các biến cục bộ sẽ bị hủy và giải phóng bộ nhớ. Điều này giúp tối ưu hóa việc sử dụng tài nguyên của hệ thống, đặc biệt quan trọng trong các ứng dụng lớn.
  • Thiết kế ứng dụng có cấu trúc: Khi bạn biết rõ biến nào có thể truy cập ở đâu, bạn có thể thiết kế các hàm và module độc lập hơn. Mỗi phần của code chỉ cần quan tâm đến những biến trong phạm vi của nó, giảm sự phụ thuộc phức tạp giữa các thành phần khác nhau của chương trình.

Tại sao cần hiểu về phạm vi biến?

Việc đầu tư thời gian để hiểu sâu về phạm vi biến mang lại nhiều lợi ích thiết thực cho quá trình phát triển phần mềm của bạn:

  • Ngăn ngừa lỗi:

    • Hãy tưởng tượng bạn có một biến $total để tính tổng trong một hàm. Nếu bạn vô tình cố gắng truy cập $total này ở bên ngoài hàm mà không biết rằng nó là biến cục bộ, bạn sẽ gặp lỗi. Hiểu phạm vi giúp bạn tránh những tình huống này, giúp code của bạn chạy ổn định và ít bug hơn.

    • Ví dụ:

<?php
function tinhTong() {
    $tongLocal = 0; // Đây là biến cục bộ
    $tongLocal = 5 + 3;
    echo "Tổng trong hàm: " . $tongLocal . "<br>";
}

tinhTong();
// Cố gắng truy cập $tongLocal ở đây sẽ gây lỗi Undefined variable
// echo "Tổng ngoài hàm: " . $tongLocal . "<br>";
?>

Quản lý dữ liệu:

  • Phạm vi biến giúp bạn kiểm soát chặt chẽ luồng dữ liệu. Bạn có thể đảm bảo rằng một biến chỉ được sửa đổi bởi những phần code được phép truy cập nó. Điều này rất quan trọng để tránh các "tác dụng phụ" không mong muốn (side effects) khi một phần code vô tình thay đổi giá trị của một biến mà một phần khác đang dựa vào.

  • Giúp phân biệt rõ ràng giữa các biến có cùng tên nhưng có mục đích khác nhau trong các phần khác nhau của chương trình.

  • Ví dụ: Bạn có thể có biến $id trong hàm xử lý sản phẩm và một $id khác trong hàm xử lý người dùng. Nhờ phạm vi cục bộ, chúng không bị lẫn lộn.

<?php
$id = "GLOBAL_ID_123"; // ID toàn cục (chỉ mang tính ví dụ)

function xuLySanPham() {
    $id = "PRODUCT_ID_XYZ"; // Đây là biến cục bộ, khác với $id toàn cục
    echo "ID sản phẩm trong hàm: " . $id . "<br>";
}

function xuLyNguoiDung() {
    $id = "USER_ID_A001"; // Đây cũng là biến cục bộ, khác với cả hai biến $id kia
    echo "ID người dùng trong hàm: " . $id . "<br>";
}

xuLySanPham();    // Output: ID sản phẩm trong hàm: PRODUCT_ID_XYZ
xuLyNguoiDung(); // Output: ID người dùng trong hàm: USER_ID_A001
echo "ID bên ngoài các hàm: " . $id . "<br>"; // Output: ID bên ngoài các hàm: GLOBAL_ID_123
?>

Thiết kế code sạch (Clean Code):

  • Khi hiểu phạm vi, bạn có thể thiết kế các hàm hoạt động độc lập hơn, nhận dữ liệu thông qua tham số và trả về kết quả, thay vì phụ thuộc quá nhiều vào các biến toàn cục. Điều này làm cho code của bạn dễ đọc, dễ kiểm thử (unit testing) và dễ bảo trì hơn.
  • Giảm sự phụ thuộc giữa các module hoặc các phần của chương trình, tạo ra một kiến trúc phần mềm rõ ràng và module hóa.

Các loại phạm vi biến chính trong PHP

PHP định nghĩa ba loại phạm vi biến cơ bản mà bạn cần nắm vững để quản lý dữ liệu hiệu quả trong ứng dụng của mình: Cục bộ (Local), Toàn cục (Global)Tĩnh (Static). Mỗi loại có quy tắc riêng về nơi biến có thể được truy cập và thời gian tồn tại của nó.

Phạm vi Cục bộ (Local Scope)

Định nghĩa: Một biến cục bộ là biến được khai báo và tồn tại chỉ bên trong một hàm cụ thể. Hãy tưởng tượng nó như một vật dụng cá nhân mà bạn chỉ mang vào phòng họp, và khi rời khỏi phòng, bạn sẽ cất nó đi.

Đặc điểm:

  • Tồn tại ngắn hạn: Biến cục bộ được tạo ra khi hàm được gọi và bị hủy ngay lập tức khi hàm kết thúc thực thi. Điều này giúp giải phóng bộ nhớ.
  • Không thể truy cập từ bên ngoài hàm: Đây là đặc điểm quan trọng nhất. Bạn không thể "nhìn thấy" hoặc sử dụng biến cục bộ từ bất kỳ đâu bên ngoài hàm mà nó được khai báo.
  • Khởi tạo lại mỗi lần gọi hàm: Mỗi khi hàm được gọi, các biến cục bộ bên trong nó sẽ được tạo mới và khởi tạo lại giá trị ban đầu của chúng (trừ khi là biến static, điều này chúng ta sẽ nói đến sau).

Ví dụ Code minh họa:

<?php
function tinhTongHaiSo() {
    // $soHang1 và $soHang2 là các biến cục bộ của hàm tinhTongHaiSo()
    $soHang1 = 10;
    $soHang2 = 5;
    $tong = $soHang1 + $soHang2;
    echo "Tổng bên trong hàm: " . $tong . "<br>";
}

tinhTongHaiSo(); // Gọi hàm, các biến cục bộ được tạo và sử dụng

// Thử truy cập biến cục bộ từ bên ngoài hàm sẽ gây lỗi!
// echo "Tổng bên ngoài hàm: " . $tong . "<br>";
// Lỗi sẽ là: PHP Notice: Undefined variable: tong in ...
?>
  • Trong ví dụ trên, biến $soHang1, $soHang2, và $tong chỉ tồn tại trong hàm tinhTongHaiSo(). Ngay khi hàm kết thúc, chúng sẽ biến mất khỏi bộ nhớ.

Phạm vi toàn cục (Global Scope)

Định nghĩa: Biến toàn cục là biến được khai báo bên ngoài bất kỳ hàm nào. Chúng có phạm vi rộng nhất trong một script PHP.

Đặc điểm:

  • Tồn tại suốt script: Biến toàn cục được tạo ra khi script bắt đầu và tồn tại cho đến khi script kết thúc.
  • Truy cập trực tiếp bên ngoài hàm: Bạn có thể truy cập và sửa đổi biến toàn cục từ bất kỳ đâu trong script, miễn là bạn không ở bên trong một hàm.
  • KHÔNG thể truy cập trực tiếp từ bên trong một hàm: Đây là điểm thường gây nhầm lẫn. Mặc dù biến toàn cục tồn tại, các hàm trong PHP không tự động "nhìn thấy" chúng. Bạn cần một cơ chế đặc biệt để đưa chúng vào phạm vi của hàm.

Cách truy cập biến toàn cục từ bên trong hàm:

PHP cung cấp hai cách chính để bạn có thể sử dụng biến toàn cục bên trong một hàm:

Sử dụng từ khóa global:

  • Mô tả: Từ khóa global được đặt trước tên biến bên trong hàm. Nó báo hiệu cho PHP rằng bạn muốn tham chiếu đến một biến toàn cục đã tồn tại, chứ không phải tạo một biến cục bộ mới có cùng tên. Bất kỳ thay đổi nào bạn thực hiện với biến này trong hàm sẽ ảnh hưởng đến biến toàn cục bên ngoài.

  • Ví dụ Code:

<?php
$thongTinCuaHang = "Shop Quần Áo XYZ"; // Biến toàn cục
$luongBanDau = 1000; // Biến toàn cục

function hienThiTenCuaHang() {
    global $thongTinCuaHang; // Sử dụng từ khóa global để truy cập biến toàn cục
    echo "Tên cửa hàng: " . $thongTinCuaHang . "<br>";
}

function tangLuongBan() {
    global $luongBanDau; // Sử dụng từ khóa global để sửa đổi biến toàn cục
    $luongBanDau += 500; // Tăng giá trị của biến toàn cục
    echo "Lương bán trong hàm: " . $luongBanDau . "<br>";
}

hienThiTenCuaHang();     // Output: Tên cửa hàng: Shop Quần Áo XYZ
echo "Lương ban đầu bên ngoài: " . $luongBanDau . "<br>"; // Output: Lương ban đầu bên ngoài: 1000

tangLuongBan();         // Output: Lương bán trong hàm: 1500
echo "Lương sau khi tăng bên ngoài: " . $luongBanDau . "<br>"; // Output: Lương sau khi tăng bên ngoài: 1500 (Giá trị đã thay đổi)
?>

Sử dụng mảng siêu toàn cục $GLOBALS:

  • Mô tả: $GLOBALS là một mảng kết hợp (associative array) đặc biệt của PHP. Nó tự động chứa tất cả các biến toàn cục trong script. Khóa của mảng $GLOBALS chính là tên của biến toàn cục (không có dấu $). Bạn có thể đọc hoặc sửa đổi các biến toàn cục thông qua mảng này.

  • Ví dụ Code:

<?php
$soLuongDatHang = 50; // Biến toàn cục
$giaTriThue = 0.1; // Biến toàn cục

function tinhTongTienThue() {
    // Truy cập biến toàn cục thông qua mảng $GLOBALS
    $tongThue = $GLOBALS['soLuongDatHang'] * $GLOBALS['giaTriThue'];
    echo "Tổng tiền thuế: " . $tongThue . "<br>";
    // Bạn cũng có thể sửa đổi biến toàn cục qua $GLOBALS
    $GLOBALS['soLuongDatHang'] += 10;
}

echo "Số lượng đặt hàng ban đầu: " . $soLuongDatHang . "<br>"; // Output: 50
tinhTongTienThue(); // Output: Tổng tiền thuế: 5 (50 * 0.1)
echo "Số lượng đặt hàng sau khi hàm chạy: " . $soLuongDatHang . "<br>"; // Output: 60 (Giá trị đã thay đổi)
?>

Lưu ý quan trọng: Việc sử dụng global hoặc $GLOBALS quá nhiều có thể làm cho code khó theo dõi, khó kiểm thử và dễ gây lỗi không mong muốn (gọi là "side effects" - tác dụng phụ). Lý do là một hàm có thể thay đổi một biến toàn cục mà các phần khác của chương trình cũng đang dùng, dẫn đến hành vi khó lường. Trong lập trình hiện đại, việc truyền biến qua tham số hàm hoặc sử dụng các mô hình thiết kế như lập trình hướng đối tượng (OOP) thường được ưu tiên hơn so với việc lạm dụng biến toàn cục.

Phạm vi Tĩnh (Static Scope)

Định nghĩa: Biến tĩnh là một loại biến cục bộ đặc biệt được khai báo với từ khóa static bên trong một hàm. Mục đích của nó là để giữ lại giá trị giữa các lần gọi hàm, thay vì bị hủy và khởi tạo lại như biến cục bộ thông thường.

Đặc điểm:

  • Giữ lại giá trị: Biến static vẫn tồn tại và giữ lại giá trị của nó giữa các lần gọi hàm (không bị hủy sau khi hàm kết thúc).
  • Chỉ khởi tạo một lần: Biến static chỉ được khởi tạo một lần duy nhất ở lần gọi hàm đầu tiên. Các lần gọi hàm sau đó sẽ bỏ qua phần khởi tạo và sử dụng giá trị hiện có của biến.
  • Vẫn là biến cục bộ: Quan trọng là biến static vẫn chỉ có thể truy cập được bên trong hàm nơi nó được khai báo. Nó không thể được truy cập từ bên ngoài hàm.

Ví dụ Code minh họa:

<?php
function demSoLanGoiHam() {
    // $counter là biến static, chỉ được khởi tạo bằng 0 ở lần gọi đầu tiên
    static $counter = 0;
    $counter++; // Tăng giá trị của biến static

    $bienThuong = 0; // Biến cục bộ thông thường
    $bienThuong++; // Tăng giá trị của biến cục bộ thông thường

    echo "Số lần gọi hàm (static): " . $counter . "<br>";
    echo "Giá trị biến thường (local): " . $bienThuong . "<br>";
}

echo "Lần gọi thứ nhất:<br>";
demSoLanGoiHam();
// Output:
// Số lần gọi hàm (static): 1
// Giá trị biến thường (local): 1

echo "Lần gọi thứ hai:<br>";
demSoLanGoiHam();
// Output:
// Số lần gọi hàm (static): 2 (giá trị được giữ lại từ 1)
// Giá trị biến thường (local): 1 (được khởi tạo lại)

echo "Lần gọi thứ ba:<br>";
demSoLanGoiHam();
// Output:
// Số lần gọi hàm (static): 3 (giá trị được giữ lại từ 2)
// Giá trị biến thường (local): 1 (được khởi tạo lại)
?>

Các loại phạm vi biến khác trong PHP

Ngoài ba loại phạm vi chính (Cục bộ, Toàn cục, Tĩnh), PHP còn có một số loại biến đặc biệt khác với phạm vi riêng, được thiết kế cho các mục đích cụ thể.

Biến Siêu Toàn Cục (Superglobals)

Định nghĩa: Các biến siêu toàn cục là các mảng tích hợp sẵn trong PHP và có một đặc điểm độc đáo: chúng luôn có sẵn ở mọi phạm vi trong script của bạn, dù bạn đang ở bên trong một hàm, một lớp hay ở phạm vi toàn cục. Bạn không cần dùng từ khóa global hay $GLOBALS để truy cập chúng. Chúng là cách PHP thu thập và cung cấp thông tin từ môi trường thực thi, yêu cầu của người dùng, hoặc phiên làm việc.

Đặc điểm:

  • Truy cập được từ mọi nơi trong script.
  • Là các mảng kết hợp (associative arrays), giúp bạn truy cập dữ liệu bằng các khóa có ý nghĩa.

Các biến siêu toàn cục phổ biến:

  • $_GET: Chứa dữ liệu được gửi đến script thông qua phương thức HTTP GET (thường từ các tham số trong URL).
    • Ví dụ: https://your-site.com/page.php?id=123&name=Alice
    • $_GET['id'] sẽ là 123.
    • $_GET['name'] sẽ là Alice.
  • $_POST: Chứa dữ liệu được gửi đến script thông qua phương thức HTTP POST (thường từ các biểu mẫu HTML). Dữ liệu này không hiển thị trong URL.
  • $_REQUEST: Là sự kết hợp của dữ liệu từ $_GET, $_POST, và $_COOKIE. Thứ tự ưu tiên mặc định là $_COOKIE, sau đó đến $_POST, và cuối cùng là $_GET. Thường không khuyến khích sử dụng vì có thể gây nhầm lẫn về nguồn gốc dữ liệu.
  • $_SESSION: Chứa dữ liệu phiên làm việc của người dùng. Dữ liệu này được lưu trữ trên server và duy trì giữa các lần truy cập trang của cùng một người dùng. Để sử dụng $_SESSION, bạn phải gọi session_start() ở đầu script.
  • $_COOKIE: Chứa dữ liệu cookie được gửi từ trình duyệt của người dùng. Cookie là các tệp nhỏ được lưu trữ trên máy tính của người dùng để "ghi nhớ" thông tin.
  • $_SERVER: Chứa thông tin về máy chủ web, môi trường thực thi, tiêu đề HTTP và đường dẫn script hiện tại. Rất hữu ích để lấy thông tin như địa chỉ IP của người dùng, tên file hiện tại, phương thức yêu cầu, v.v.
  • $_FILES: Chứa thông tin về các file được tải lên thông qua biểu mẫu HTML.
  • $_ENV: Chứa các biến môi trường của máy chủ.

Ví dụ Code minh họa: Sử dụng $_GET$_POST

Để chạy ví dụ này, bạn cần có một file HTML (form.html) và một file PHP (process.php).

File: form.html

<!DOCTYPE html>
<html lang="vi">
<head>
    <meta charset="UTF-8">
    <title>Form PHP</title>
</head>
<body>
    <h2>Gửi dữ liệu bằng GET</h2>
    <form action="process.php" method="GET">
        <label for="name_get">Tên của bạn:</label>
        <input type="text" id="name_get" name="username_get"><br><br>
        <input type="submit" value="Gửi GET">
    </form>

    <hr>

    <h2>Gửi dữ liệu bằng POST</h2>
    <form action="process.php" method="POST">
        <label for="name_post">Tên của bạn:</label>
        <input type="text" id="name_post" name="username_post"><br>
        <label for="age_post">Tuổi của bạn:</label>
        <input type="number" id="age_post" name="userage_post"><br><br>
        <input type="submit" value="Gửi POST">
    </form>

    <hr>

    <h2>Ví dụ $_SERVER</h2>
    <p>Thử truy cập trang này qua URL: <a href="process.php?action=view&id=1">process.php?action=view&id=1</a></p>
</body>
</html>

File: process.php

<?php
echo "<h1>Xử lý dữ liệu từ Form</h1>";

// --- Xử lý dữ liệu từ $_GET ---
echo "<h2>Dữ liệu từ GET:</h2>";
if (isset($_GET['username_get'])) {
    $usernameGet = htmlspecialchars($_GET['username_get']); // Luôn lọc dữ liệu người dùng
    echo "Tên (GET): " . $usernameGet . "<br>";
} else {
    echo "Không có dữ liệu 'username_get' từ GET.<br>";
}

// Ví dụ sử dụng $_GET để kiểm tra tham số URL (ví dụ: process.php?action=view&id=1)
if (isset($_GET['action'])) {
    echo "Hành động yêu cầu: " . htmlspecialchars($_GET['action']) . "<br>";
}
if (isset($_GET['id'])) {
    echo "ID yêu cầu: " . htmlspecialchars($_GET['id']) . "<br>";
}

echo "<hr>";

// --- Xử lý dữ liệu từ $_POST ---
echo "<h2>Dữ liệu từ POST:</h2>";
if (isset($_POST['username_post'])) {
    $usernamePost = htmlspecialchars($_POST['username_post']); // Luôn lọc dữ liệu người dùng
    echo "Tên (POST): " . $usernamePost . "<br>";
} else {
    echo "Không có dữ liệu 'username_post' từ POST.<br>";
}

if (isset($_POST['userage_post'])) {
    $userAgePost = (int)$_POST['userage_post']; // Ép kiểu sang số nguyên
    echo "Tuổi (POST): " . $userAgePost . "<br>";
} else {
    echo "Không có dữ liệu 'userage_post' từ POST.<br>";
}

echo "<hr>";

// --- Ví dụ sử dụng $_SERVER ---
echo "<h2>Dữ liệu từ SERVER:</h2>";
echo "Tên file hiện tại: " . $_SERVER['PHP_SELF'] . "<br>";
echo "Phương thức yêu cầu HTTP: " . $_SERVER['REQUEST_METHOD'] . "<br>";
if (isset($_SERVER['REMOTE_ADDR'])) {
    echo "Địa chỉ IP của client: " . $_SERVER['REMOTE_ADDR'] . "<br>";
} else {
    echo "Không thể lấy địa chỉ IP của client.<br>";
}

// Kiểm tra và sử dụng $_SESSION (cần session_start() ở đầu mỗi script sử dụng session)
session_start();
$_SESSION['visit_count'] = ($_SESSION['visit_count'] ?? 0) + 1;
echo "Bạn đã truy cập trang này " . $_SESSION['visit_count'] . " lần trong phiên này.<br>";

?>

Phạm vi Biến của Đối Tượng (Object Properties)

Định nghĩa: Khi bạn làm việc với lập trình hướng đối tượng (OOP) trong PHP, các biến được khai báo bên trong một lớp (class) được gọi là thuộc tính (properties). Các thuộc tính này thuộc về một đối tượng cụ thể được tạo ra từ lớp đó.

Đặc điểm:

  • Truy cập thông qua đối tượng:
    • Bên trong lớp (trong các phương thức của lớp), bạn truy cập thuộc tính của đối tượng hiện tại bằng cách sử dụng $this->propertyName.
    • Bên ngoài lớp, bạn truy cập thuộc tính của một đối tượng cụ thể bằng cú pháp $objectName->propertyName.
  • Phạm vi truy cập (Visibility): Các thuộc tính có thể có các cấp độ phạm vi truy cập khác nhau (public, protected, private), điều này quy định nơi chúng có thể được truy cập:
    • public: Có thể truy cập từ bất cứ đâu (bên trong hoặc bên ngoài lớp).
    • protected: Chỉ có thể truy cập từ bên trong lớp đó hoặc các lớp con kế thừa từ nó.
    • private: Chỉ có thể truy cập từ bên trong chính lớp đó.
    • (Đây là một khái niệm sâu hơn của OOP, bạn sẽ tìm hiểu kỹ hơn khi học về Class và Object).
  • Ví dụ Code minh họa: Khai báo thuộc tính trong class

<?php
class SanPham {
    // Thuộc tính public: Có thể truy cập từ bên ngoài lớp
    public $tenSanPham;
    public $gia;

    // Thuộc tính private: Chỉ có thể truy cập từ bên trong lớp SanPham
    private $maNoiBo;

    // Thuộc tính protected: Có thể truy cập từ lớp SanPham và các lớp kế thừa
    protected $tonKho;

    // Phương thức khởi tạo (Constructor) - được gọi khi tạo đối tượng mới
    public function __construct($ten, $gia, $ma, $ton) {
        $this->tenSanPham = $ten;
        $this->gia = $gia;
        $this->maNoiBo = $ma;
        $this->tonKho = $ton;
    }

    // Phương thức public để truy cập thuộc tính private (ví dụ getter)
    public function layMaNoiBo() {
        return $this->maNoiBo; // $this->maNoiBo hợp lệ vì đang ở trong lớp
    }

    // Phương thức public để hiển thị thông tin sản phẩm
    public function hienThiThongTin() {
        echo "Tên sản phẩm: " . $this->tenSanPham . "<br>";
        echo "Giá: " . $this->gia . " VNĐ<br>";
        echo "Mã nội bộ: " . $this->layMaNoiBo() . "<br>"; // Truy cập private qua getter
        echo "Số lượng tồn kho: " . $this->tonKho . "<br>";
    }
}

// Tạo một đối tượng từ lớp SanPham
$dienThoai = new SanPham("iPhone 15", 25000000, "IP15-A01", 100);

echo "<h2>Phạm vi Biến của Đối Tượng:</h2>";

// Truy cập thuộc tính public từ bên ngoài đối tượng
echo "Tên sản phẩm (truy cập trực tiếp): " . $dienThoai->tenSanPham . "<br>";
echo "Giá sản phẩm (truy cập trực tiếp): " . $dienThoai->gia . " VNĐ<br>";

// Cố gắng truy cập thuộc tính private từ bên ngoài sẽ gây lỗi!
// echo $dienThoai->maNoiBo; // Fatal error: Uncaught Error: Cannot access private property...

// Truy cập thuộc tính private thông qua phương thức public của đối tượng
echo "Mã nội bộ (qua phương thức): " . $dienThoai->layMaNoiBo() . "<br>";

// Gọi phương thức để hiển thị tất cả thông tin
$dienThoai->hienThiThongTin();

// Ví dụ về lớp kế thừa để minh họa protected
class DienThoaiThongMinh extends SanPham {
    public function __construct($ten, $gia, $ma, $ton) {
        parent::__construct($ten, $gia, $ma, $ton);
    }

    public function kiemTraTonKho() {
        // Có thể truy cập $this->tonKho vì nó là protected
        if ($this->tonKho > 0) {
            echo "Sản phẩm " . $this->tenSanPham . " còn hàng (tồn: " . $this->tonKho . ").<br>";
        } else {
            echo "Sản phẩm " . $this->tenSanPham . " hết hàng.<br>";
        }
    }
}

$samsung = new DienThoaiThongMinh("Samsung Galaxy S24", 20000000, "SSG-B02", 5);
$samsung->kiemTraTonKho();
?>

Ví dụ minh họa tổng hợp trong PHP

Dưới đây là một đoạn code PHP hoàn chỉnh, kết hợp các loại phạm vi biến khác nhau trong một kịch bản đơn giản để bạn có cái nhìn tổng quát về cách chúng tương tác.

Kịch bản: Mô phỏng một trang web đơn giản hiển thị số lượt truy cập, xử lý thông tin từ URL và form, đồng thời sử dụng một lớp để quản lý sản phẩm.

<?php
// Bắt đầu session để sử dụng $_SESSION
session_start();

// --- 1. Biến toàn cục (Global Scope) ---
$appName = "Ứng dụng Quản lý Demo";
$version = "1.0.0";
$maxItemsPerPage = 10; // Biến cấu hình toàn cục

echo "<h1>" . $appName . " - v" . $version . "</h1>";
echo "<p>Số mặt hàng tối đa mỗi trang: " . $maxItemsPerPage . "</p>";

// --- 2. Hàm và Biến cục bộ (Local Scope) & Biến tĩnh (Static Scope) ---
function demLuotXemTrang() {
    static $pageViews = 0; // Biến tĩnh: Duy trì giá trị giữa các lần gọi
    $pageViews++;
    echo "<strong>Lượt xem trang hiện tại: " . $pageViews . "</strong><br>";

    $thongTinNoiBo = "Chỉ thấy trong hàm này."; // Biến cục bộ
    // echo $maxItemsPerPage; // Lỗi: Biến toàn cục không truy cập được trực tiếp
    echo "(Thông tin nội bộ hàm: " . $thongTinNoiBo . ")<br>";
}

demLuotXemTrang(); // Lần 1
demLuotXemTrang(); // Lần 2 (pageViews sẽ là 2)

echo "<hr>";

// --- 3. Truy cập biến toàn cục từ bên trong hàm ---
function hienThiCauHinh() {
    // Cách 1: Sử dụng global
    global $appName, $maxItemsPerPage;
    echo "Sử dụng global: Ứng dụng: " . $appName . ", Max Items: " . $maxItemsPerPage . "<br>";

    // Cách 2: Sử dụng $GLOBALS
    echo "Sử dụng \$GLOBALS: Phiên bản: " . $GLOBALS['version'] . "<br>";

    // Thay đổi biến toàn cục qua global
    $GLOBALS['appName'] = "Ứng dụng Quản lý Demo (Đã Cập Nhật)";
}

hienThiCauHinh();
echo "Tên ứng dụng sau khi cập nhật bởi hàm: " . $appName . "<br>"; // AppName đã thay đổi!

echo "<hr>";

// --- 4. Biến siêu toàn cục (Superglobals) ---
echo "<h2>Thông tin từ Superglobals:</h2>";

// $_SERVER: Thông tin về yêu cầu và server
echo "Phương thức yêu cầu: " . $_SERVER['REQUEST_METHOD'] . "<br>";
echo "Đường dẫn Script hiện tại: " . $_SERVER['PHP_SELF'] . "<br>";

// $_GET: Xử lý tham số từ URL (ví dụ: ?action=view&id=123)
if (isset($_GET['action']) && isset($_GET['id'])) {
    $action = htmlspecialchars($_GET['action']);
    $itemId = (int)$_GET['id'];
    echo "Yêu cầu: Action='" . $action . "', ID='" . $itemId . "'<br>";
} else {
    echo "Không có tham số 'action' hoặc 'id' trong URL.<br>";
}

// $_SESSION: Đếm số lần truy cập trong phiên
$_SESSION['visit_count'] = ($_SESSION['visit_count'] ?? 0) + 1; // Sử dụng toán tử null coalescing từ PHP 7.0+
echo "Bạn đã truy cập trang này " . $_SESSION['visit_count'] . " lần trong phiên hiện tại.<br>";

echo "<hr>";

// --- 5. Phạm vi Biến của đối tượng (Object Properties) ---
echo "<h2>Phạm vi biến của đối tượng:</h2>";

class Product {
    public $name;        // public property: truy cập mọi nơi
    private $productId;  // private property: chỉ truy cập trong class
    protected $price;    // protected property: truy cập trong class và lớp con

    public function __construct($name, $id, $price) {
        $this->name = $name;
        $this->productId = $id;
        $this->price = $price;
    }

    public function getProductId() {
        return $this->productId; // Truy cập private property từ phương thức nội bộ
    }

    public function displayProductInfo() {
        echo "Sản phẩm: " . $this->name . "<br>";
        echo "Mã sản phẩm: " . $this->getProductId() . "<br>"; // Dùng getter
        echo "Giá: " . $this->price . " VNĐ<br>"; // Truy cập protected từ phương thức nội bộ
    }
}

// Tạo một đối tượng Product mới
$laptop = new Product("Laptop Gaming X1", "LGX1-PRO", 25000000);

// Truy cập public property từ bên ngoài đối tượng
echo "Tên sản phẩm từ object: " . $laptop->name . "<br>";

// Cố gắng truy cập private hoặc protected property từ bên ngoài sẽ gây lỗi
// echo $laptop->productId; // Lỗi: Cannot access private property
// echo $laptop->price;     // Lỗi: Cannot access protected property

// Truy cập thông tin thông qua phương thức public
$laptop->displayProductInfo();

echo "<hr>";

// --- Minh họa truyền tham số (thay vì global) ---
echo "<h2>Truyền tham số (Best Practice):</h2>";
function tinhTong($soA, $soB) { // Các tham số là biến cục bộ của hàm này
    $tong = $soA + $soB;
    return $tong;
}

$num1 = 10;
$num2 = 20;
$ketQua = tinhTong($num1, $num2); // Truyền giá trị của $num1 và $num2 vào hàm
echo "Tổng của " . $num1 . " và " . $num2 . " là: " . $ketQua . "<br>";

?>

Kết bài

Chúng ta đã cùng nhau khám phá sâu rộng về phạm vi sử dụng biến (Variable Scope) trong PHP. Từ những quy tắc cơ bản của biến cục bộtoàn cục, cách biến tĩnh duy trì giá trị, đến sự tiện lợi của biến siêu toàn cục và cấu trúc dữ liệu của thuộc tính đối tượng. Việc nắm vững từng loại phạm vi này không chỉ là một kiến thức lý thuyết mà còn là một kỹ năng thực hành thiết yếu.

Hiểu rõ phạm vi biến giúp bạn:

  • Ngăn ngừa lỗi: Tránh xa các lỗi "Undefined variable" khó chịu.
  • Quản lý dữ liệu hiệu quả: Đảm bảo biến được sử dụng đúng mục đích và không gây xung đột không mong muốn.
  • Viết code sạch và dễ bảo trì: Giúp các hàm và thành phần của chương trình hoạt động độc lập, dễ đọc, dễ kiểm thử và tái sử dụng hơn.

Hãy luôn ghi nhớ những thực tiễn tốt đã được đề cập: hạn chế lạm dụng biến toàn cục, ưu tiên sử dụng biến cục bộ, cẩn trọng với việc đặt tên biến trùng lặp, và sử dụng biến static một cách hợp lý. Áp dụng những nguyên tắc này sẽ nâng cao đáng kể chất lượng code của bạn, giúp bạn xây dựng những ứng dụng PHP mạnh mẽ và bền vững hơn.

Bài viết liên quan