Tham khảo: Phạm vi biến là gì, các biến nào có thể truy cập được từ đâu và các lỗi biến không xác định được là gì?


167

Lưu ý: Đây là một câu hỏi tham khảo để xử lý phạm vi biến trong PHP. Vui lòng đóng bất kỳ câu hỏi nào phù hợp với mẫu này như một bản sao của câu hỏi này.

"Phạm vi biến" trong PHP là gì? Các biến từ một tệp .php có thể truy cập được trong tệp khác không? Tại sao đôi khi tôi nhận được lỗi "biến không xác định" ?


1
Nếu bạn đặt tiêu đề này là "Biến không xác định", bạn sẽ nhận được nhiều lượt truy cập hơn :) mặc dù vậy, công việc tốt
Dale

@Dale, Thật ra là không. Lượt xem 2k trong 2 năm là ....
Pacerier

7
@Pacerier ... về thời điểm thích hợp để bình luận ngẫu nhiên?
Dale

@Pacerier Tôi thực sự không chắc chắn những gì bạn đang cố gắng nói với nhận xét đó. "Là ...." ... cái gì?! : P
lừa dối

@Dale, Bây giờ là thời điểm thích hợp: wow mặc dù câu hỏi bị đình trệ trong 2 năm, sau khi từ " không quốc tịch " được thêm vào GoogleDex, tỷ lệ trúng của nó theo nghĩa đen gấp 3 lần chỉ trong 6 tháng.
Pacerier

Câu trả lời:


188

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

Các biến có một "phạm vi" giới hạn hoặc "những nơi mà chúng có thể truy cập được". Chỉ vì bạn đã viết $foo = 'bar';một lần ở đâu đó trong ứng dụng của mình không có nghĩa là bạn có thể tham khảo $footừ mọi nơi khác trong ứng dụng. Biến $foocó một phạm vi nhất định trong đó nó là hợp lệ và chỉ có mã trong cùng phạm vi có quyền truy cập vào biến.

Làm thế nào là một phạm vi được định nghĩa trong PHP?

Rất đơn giản: PHP có phạm vi chức năng . Đó là loại phân cách phạm vi duy nhất tồn tại trong PHP. Các biến bên trong một hàm chỉ có sẵn bên trong hàm đó. Các biến bên ngoài các hàm có sẵn ở bất kỳ đâu ngoài các hàm, nhưng không nằm trong bất kỳ hàm nào. Điều này có nghĩa là có một phạm vi đặc biệt trong PHP: toàn cầu phạm vi . Bất kỳ biến nào được khai báo bên ngoài bất kỳ chức năng nào đều nằm trong phạm vi toàn cầu này.

Thí dụ:

<?php

$foo = 'bar';

function myFunc() {
    $baz = 42;
}

$foolà trong phạm vi toàn cầu , $bazlà trong một phạm vi địa phương bên trong myFunc. Chỉ có mã bên trong myFunccó quyền truy cập $baz. Chỉ có mã bên ngoài myFunc có quyền truy cập $foo. Không có quyền truy cập khác:

<?php

$foo = 'bar';

function myFunc() {
    $baz = 42;

    echo $foo;  // doesn't work
    echo $baz;  // works
}

echo $foo;  // works
echo $baz;  // doesn't work

Phạm vi và tập tin bao gồm

Ranh giới tệp không tách phạm vi:

a.php

<?php

$foo = 'bar';

b.php

<?php

include 'a.php';

echo $foo;  // works!

Các quy tắc tương tự áp dụng cho includemã d như áp dụng cho bất kỳ mã nào khác: chỉfunction phạm vi riêng biệt. Với mục đích phạm vi, bạn có thể nghĩ đến việc bao gồm các tệp như sao chép và dán mã:

c.php

<?php

function myFunc() {
    include 'a.php';

    echo $foo;  // works
}

myFunc();

echo $foo;  // doesn't work!

Trong ví dụ trên, a.phpđược bao gồm bên trong myFunc, bất kỳ biến nào bên trong a.phpchỉ có phạm vi hàm cục bộ. Chỉ vì chúng xuất hiện như nằm trong phạm vi toàn cầu a.phpkhông có nghĩa là chúng thực sự có nghĩa, nó thực sự phụ thuộc vào bối cảnh mà mã được bao gồm / thực thi trong bối cảnh nào.

Điều gì về các chức năng bên trong các chức năng và các lớp?

Mỗi cái mới function tuyên bố giới thiệu một phạm vi mới, nó đơn giản.

(ẩn danh) các chức năng bên trong các chức năng

function foo() {
    $foo = 'bar';

    $bar = function () {
        // no access to $foo
        $baz = 'baz';
    };

    // no access to $baz
}

các lớp học

$foo = 'foo';

class Bar {

    public function baz() {
        // no access to $foo
        $baz = 'baz';
    }

}

// no access to $baz

Phạm vi tốt cho là gì?

Xử lý các vấn đề phạm vi có vẻ khó chịu, nhưng phạm vi biến hạn chế là điều cần thiết để viết các ứng dụng phức tạp! Nếu mọi biến bạn khai báo sẽ có sẵn từ mọi nơi khác trong ứng dụng của bạn, bạn sẽ bước qua tất cả các biến của mình mà không có cách nào thực sự để theo dõi những gì thay đổi những gì. Chỉ có rất nhiều tên hợp lý bạn có thể đặt cho các biến của mình, có lẽ bạn muốn sử dụng biến "$name " ở nhiều nơi. Nếu bạn chỉ có thể có tên biến duy nhất này một lần trong ứng dụng của mình, bạn sẽ phải sử dụng các lược đồ đặt tên thực sự phức tạp để đảm bảo các biến của bạn là duy nhất và bạn không thay đổi biến sai từ đoạn mã sai.

Quan sát:

function foo() {
    echo $bar;
}

Nếu không có phạm vi, chức năng trên sẽ làm gì? Nơi nào $barđến từ đâu? Nó có trạng thái gì? Nó thậm chí còn được khởi tạo? Bạn có phải kiểm tra mỗi lần? Điều này là không thể duy trì. Điều này đưa chúng ta đến ...

Vượt qua ranh giới phạm vi

Đúng cách: truyền biến vào và ra

function foo($bar) {
    echo $bar;
    return 42;
}

Biến $barrõ ràng là đi vào phạm vi này như là đối số chức năng. Chỉ cần nhìn vào chức năng này, rõ ràng các giá trị mà nó hoạt động bắt nguồn từ đâu. Sau đó, nó trả về một giá trị rõ ràng . Người gọi có thể tự tin để biết các biến mà hàm sẽ làm việc với và các giá trị trả về của nó đến từ đâu:

$baz   = 'baz';
$blarg = foo($baz);

Mở rộng phạm vi của các biến thành các hàm ẩn danh

$foo = 'bar';

$baz = function () use ($foo) {
    echo $foo;
};

$baz();

Hàm ẩn danh rõ ràng bao gồm $footừ phạm vi xung quanh của nó. Lưu ý rằng điều này không giống như toàn cầu phạm vi .

Cách sai: global

Như đã nói trước đây, phạm vi toàn cầu có phần đặc biệt và các hàm có thể nhập rõ ràng các biến từ nó:

$foo = 'bar';

function baz() {
    global $foo;
    echo $foo;
    $foo = 'baz';
}

Hàm này sử dụng và sửa đổi biến toàn cục $foo. Đừng làm điều này! (Trừ khi bạn thực sự thực sự thực sự thực sự biết những gì bạn đang làm, và thậm chí sau đó: không!)

Tất cả người gọi của chức năng này thấy là:

baz(); // outputs "bar"
unset($foo);
baz(); // no output, WTF?!
baz(); // outputs "baz", WTF?!?!!

Không có dấu hiệu cho thấy chức năng này có bất kỳ tác dụng phụ , nhưng nó có. Điều này rất dễ trở thành một mớ hỗn độn khi một số chức năng tiếp tục sửa đổi và yêu cầu một số trạng thái toàn cầu. Bạn muốn các chức năng không trạng thái , chỉ hoạt động trên đầu vào của chúng và trả về đầu ra được xác định, tuy nhiên nhiều lần bạn gọi chúng.

Bạn nên tránh sử dụng phạm vi toàn cầu theo bất kỳ cách nào càng nhiều càng tốt; chắc chắn bạn không nên "kéo" các biến ra khỏi phạm vi toàn cầu vào phạm vi cục bộ.


Bạn vừa nói sai cáchglobal , vậy xin vui lòng cho chúng tôi biết khi nào chúng ta nên sử dụng global? Và hãy giải thích (một chút)static .. là gì?

@stack Không có cách "đúng" cho global. Nó luôn luôn sai. Truyền tham số hàm là đúng. staticđược giải thích tốt trong hướng dẫn và không liên quan nhiều đến phạm vi. Tóm lại, nó có thể được coi là một "biến toàn cầu có phạm vi". Tôi đang mở rộng một chút về cách sử dụng của nó ở đây kunststube.net/static .
lừa dối

Suy nghĩ đơn giản của tôi là nếu một biến php đủ quan trọng để xứng đáng với trạng thái toàn cầu, thì nó xứng đáng là một cột trong cơ sở dữ liệu. Có thể đó là một việc quá mức, nhưng đó là một cách tiếp cận ngu ngốc phù hợp với trí thông minh lập trình tầm thường của tôi
Arthur Tarasov

@Arthur Có quá nhiều thứ để giải nén ở đó ಠ_ಠ Đây chắc chắn không phải là một cách tiếp cận mà tôi sẽ chứng thực.
lừa dối

@deceze một chút của một đối số xảy ra ở đây hôm nay stackoverflow.com/q/51409392 - trong đó OP đề cập rằng bản sao (ở đây) không đề cập đến include_oncevà có thể require_oncecũng nên được thêm vào đâu đó; chỉ nói OP đã bỏ phiếu để mở lại câu hỏi của họ. Bài đăng của họ sẽ là một trường hợp đặc biệt và những gì nên được thực hiện về nó?
Funk Bốn mươi Niner

10

Mặc dù các biến được xác định bên trong phạm vi của hàm không thể được truy cập từ bên ngoài, điều đó không có nghĩa là bạn không thể sử dụng các giá trị của chúng sau khi hàm đó hoàn thành. PHP có một statictừ khóa nổi tiếng được sử dụng rộng rãi trong PHP hướng đối tượng để xác định các phương thức và thuộc tính tĩnh nhưng người ta nên nhớ rằng staticcũng có thể được sử dụng bên trong các hàm để xác định các biến tĩnh.

"Biến tĩnh" là gì?

Biến tĩnh khác với biến thông thường được xác định trong phạm vi của hàm trong trường hợp nó không mất giá trị khi thực thi chương trình rời khỏi phạm vi này. Hãy xem xét ví dụ sau về việc sử dụng các biến tĩnh:

function countSheep($num) {
 static $counter = 0;
 $counter += $num;
 echo "$counter sheep jumped over fence";
}

countSheep(1);
countSheep(2);
countSheep(3);

Kết quả:

1 sheep jumped over fence
3 sheep jumped over fence
6 sheep jumped over fence

Nếu chúng ta đã xác định $countermà không có staticthì mỗi lần giá trị lặp lại sẽ giống như $numtham số được truyền cho hàm. Sử dụng staticcho phép xây dựng bộ đếm đơn giản này mà không cần thêm cách giải quyết.

Các trường hợp sử dụng biến tĩnh

  1. Để lưu trữ giá trị giữa các lệnh gọi đến chức năng.
  2. Để lưu trữ các giá trị giữa các cuộc gọi đệ quy khi không có cách nào (hoặc không có mục đích) để chuyển chúng dưới dạng params.
  3. Để lưu giá trị bộ đệm thường tốt hơn để lấy một lần. Ví dụ, kết quả của việc đọc tệp bất biến trên máy chủ.

Thủ thuật

Biến tĩnh chỉ tồn tại trong một phạm vi chức năng cục bộ. Nó không thể được truy cập bên ngoài chức năng mà nó đã được xác định. Vì vậy, bạn có thể chắc chắn rằng nó sẽ giữ giá trị của nó không thay đổi cho đến lần gọi tiếp theo đến chức năng đó.

Biến tĩnh chỉ có thể được định nghĩa là vô hướng hoặc là biểu thức vô hướng (kể từ PHP 5.6). Việc gán các giá trị khác cho nó chắc chắn sẽ dẫn đến một thất bại ít nhất tại thời điểm bài viết này được viết. Tuy nhiên, bạn có thể làm như vậy chỉ trên dòng mã tiếp theo của bạn:

function countSheep($num) {
  static $counter = 0;
  $counter += sqrt($num);//imagine we need to take root of our sheep each time
  echo "$counter sheep jumped over fence";
}

Kết quả:

2 sheep jumped over fence
5 sheep jumped over fence
9 sheep jumped over fence

Hàm tĩnh được chia sẻ 'giữa các phương thức của các đối tượng cùng lớp. Thật dễ hiểu khi xem ví dụ sau:

class SomeClass {
  public function foo() {
    static $x = 0;
    echo ++$x;
  }
}

$object1 = new SomeClass;
$object2 = new SomeClass;

$object1->foo(); // 1
$object2->foo(); // 2 oops, $object2 uses the same static $x as $object1
$object1->foo(); // 3 now $object1 increments $x
$object2->foo(); // 4 and now his twin brother

Điều này chỉ hoạt động với các đối tượng cùng lớp. Nếu các đối tượng đến từ các lớp khác nhau (thậm chí mở rộng lẫn nhau) hành vi của các vars tĩnh sẽ như mong đợi.

Là biến tĩnh là cách duy nhất để giữ các giá trị giữa các lệnh gọi đến một hàm?

Một cách khác để giữ các giá trị giữa các lệnh gọi hàm là sử dụng các bao đóng. Đóng cửa được giới thiệu trong PHP 5.3. Trong hai từ, chúng cho phép bạn giới hạn quyền truy cập vào một số bộ biến trong phạm vi hàm thành một hàm ẩn danh khác sẽ là cách duy nhất để truy cập chúng. Các biến đóng có thể bắt chước (ít nhiều thành công) các khái niệm OOP như 'hằng số lớp' (nếu chúng được truyền theo giá trị đóng) hoặc 'thuộc tính riêng' (nếu được truyền bằng tham chiếu) trong lập trình có cấu trúc.

Cái sau thực sự cho phép sử dụng các bao đóng thay vì các biến tĩnh. Việc sử dụng luôn luôn tùy thuộc vào nhà phát triển để quyết định nhưng cần đề cập rằng các biến tĩnh chắc chắn hữu ích khi làm việc với các lần truy cập và xứng đáng được các nhà phát triển chú ý.


2

Tôi sẽ không đăng một câu trả lời hoàn chỉnh cho câu hỏi, vì những câu hỏi hiện có và hướng dẫn sử dụng PHP làm rất tốt việc giải thích hầu hết điều này.

Nhưng có một chủ đề mà đã bỏ lỡ là của superglobals , trong đó có sử dụng phổ biến $_POST, $_GET, $_SESSION, vv Những biến là mảng mà luôn luôn có sẵn, trong bất kỳ phạm vi, mà không có một globallời tuyên bố.

Ví dụ, chức năng này sẽ in ra tên của người dùng đang chạy tập lệnh PHP. Biến có sẵn cho hàm mà không có bất kỳ vấn đề.

<?php
function test() {
    echo $_ENV["user"];
}

Quy tắc chung của "toàn cầu là xấu" thường được sửa đổi trong PHP thành "toàn cầu là xấu nhưng các siêu sao vẫn ổn", miễn là người ta không lạm dụng chúng. (Tất cả các biến này đều có thể ghi được, vì vậy chúng có thể được sử dụng để tránh tiêm phụ thuộc nếu bạn thực sự khủng khiếp.)

Các biến này không được đảm bảo có mặt; người quản trị có thể vô hiệu hóa một số hoặc tất cả trong số họ sử dụng các variables_orderchỉ thị trong php.ini, nhưng đây không phải là hành vi phổ biến.


Một danh sách các siêu sao hiện tại:

  • $GLOBALS - Tất cả các biến toàn cục trong tập lệnh hiện tại
  • $_SERVER - Thông tin về máy chủ và môi trường thực thi
  • $_GET - Các giá trị được truyền trong chuỗi truy vấn của URL, bất kể phương thức HTTP được sử dụng cho yêu cầu
  • $_POST- Các giá trị được chuyển trong yêu cầu POST HTTP với application/x-www-form-urlencodedhoặc multipart/form-datacác loại MIME
  • $_FILES- Các tệp được truyền trong yêu cầu POST HTTP với multipart/form-dataloại MIME
  • $_COOKIE - Cookies được thông qua với yêu cầu hiện tại
  • $_SESSION - Các biến phiên được lưu trữ nội bộ bởi PHP
  • $_REQUEST- Điển hình là sự kết hợp của $_GET$_POST, nhưng đôi khi $_COOKIES. Nội dung được xác định bởi request_orderchỉ thị trong php.ini.
  • $_ENV - Các biến môi trường của tập lệnh hiện tại
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.