Các biến toàn cục trong PHP có được coi là hành vi xấu không? Nếu vậy, tại sao?


86
function foo () {
    global $var;
    // rest of code
}

Trong các dự án PHP nhỏ của tôi, tôi thường làm theo cách thủ tục. Tôi thường có một biến chứa cấu hình hệ thống và khi tôi muốn truy cập biến này trong một hàm, tôi sẽ làm global $var;.

Đây có phải là thực hành xấu?


19
biến toàn cục là một từ đồng nghĩa với hành vi xấu
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳


2
Hãy thử kiểm tra đơn vị / chấp nhận nó và bạn sẽ nhanh chóng tìm ra lý do tại sao các khối cầu lại là một vấn đề: chúng làm cho mã của bạn không đáng tin cậy khi bạn thực hiện nhiều lần.
Kzqai

Câu trả lời:


102

Khi mọi người nói về các biến toàn cục bằng các ngôn ngữ khác, nó có nghĩa khác với những gì nó làm trong PHP. Đó là bởi vì các biến không thực sự toàn cầu trong PHP. Phạm vi của một chương trình PHP điển hình là một yêu cầu HTTP. Các biến phiên thực sự có phạm vi rộng hơn các biến "toàn cầu" PHP vì chúng thường bao gồm nhiều yêu cầu HTTP.

Thông thường (luôn luôn?) Bạn có thể gọi các hàm thành viên trong các phương thức preg_replace_callback()như sau:

preg_replace_callback('!pattern!', array($obj, 'method'), $str);

Xem các cuộc gọi lại để biết thêm.

Vấn đề là các đối tượng đã được gắn chặt vào PHP và theo một số cách dẫn đến một số khó xử.

Đừng quan tâm quá mức đến việc áp dụng các tiêu chuẩn hoặc cấu trúc từ các ngôn ngữ khác nhau vào PHP. Một cạm bẫy phổ biến khác là cố gắng biến PHP thành một ngôn ngữ OOP thuần túy bằng cách gắn các mô hình đối tượng lên trên mọi thứ.

Giống như bất kỳ thứ gì khác, hãy sử dụng các biến "toàn cục", mã thủ tục, một khuôn khổ cụ thể và OOP vì nó có ý nghĩa, giải quyết vấn đề, giảm số lượng mã bạn cần viết hoặc làm cho nó dễ bảo trì hơn và dễ hiểu hơn, không phải vì bạn nghĩ bạn nên.


8
Cần lưu ý rằng PHP 5.3 giải quyết một số vấn đề này với các hàm lambda cho phép bạn tránh sử dụng hàm được khai báo trong phạm vi toàn cục cho các lệnh gọi lại. 1 cho bảo trì, tư vấn mã dễ đọc
Jonathan Fingland

Bạn có thể không sử dụng lệnh gọi lại của biểu mẫu array ($obj, 'callbackMethod')trong các cuộc gọi tới preg_replace_callback()không? (Tôi biết, tôi đã trở thành con mồi để bẫy OOP này ...)
grossvogel

25
Câu hỏi không phải là "có nên sử dụng biến toàn cục không?". Câu trả lời cho điều đó sẽ là, 'chắc chắn vào đôi khi nếu cần thiết'. Câu hỏi là họ có thực hành xấu. Câu trả lời là 'có, đôi khi'. Đối với dự án nhỏ áp phích, không có gì xấu có thể xảy ra - tuy nhiên đối với các dự án lớn hơn với nhiều thành viên trong nhóm và nhiều bộ phận chuyển động, việc sử dụng nhiều biến toàn cục sẽ khiến mã khó gỡ lỗi, gần như không thể cấu trúc lại và thậm chí là một vấn đề đọc. Đôi khi bạn có thể sử dụng chúng không, .. chắc chắn - chúng có hút không, .. vâng!
eddiemoya

@eddiemoya Đúng là Eddie. Có rất nhiều người biện minh cho các hành vi xấu như sử dụng các biến toàn cục. Bạn nên tránh chúng như bệnh dịch. Bất kỳ bằng kỹ sư phần mềm tử tế nào cũng sẽ khắc sâu điều này với bạn ... các giảng viên không chỉ nói cho bạn nghe về nó ... họ biết từ hàng chục năm kinh nghiệm. Bạn nên sử dụng chức năng thành viên nếu có thể để giá trị truy cập mà bạn cần, ví dụ get_query_var () trong Wordpress, vv

27

Các biến toàn cục nếu không được sử dụng cẩn thận có thể làm cho vấn đề khó tìm hơn. Giả sử bạn yêu cầu một tập lệnh php và bạn nhận được cảnh báo rằng bạn đang cố gắng truy cập chỉ mục của một mảng không tồn tại trong một số hàm.

Nếu mảng bạn đang cố gắng truy cập là cục bộ của hàm, bạn hãy kiểm tra hàm để xem liệu bạn có mắc lỗi ở đó không. Có thể là sự cố với đầu vào cho hàm, vì vậy bạn hãy kiểm tra các vị trí mà hàm được gọi.

Nhưng nếu mảng đó là toàn cục, bạn cần phải kiểm tra tất cả những nơi bạn sử dụng biến toàn cục đó và không chỉ vậy, bạn phải tìm ra thứ tự mà các tham chiếu đến biến toàn cục đó được truy cập.

Nếu bạn có một biến toàn cục trong một đoạn mã, điều đó sẽ gây khó khăn cho việc tách chức năng của mã đó. Tại sao bạn muốn tách biệt chức năng? Vì vậy, bạn có thể kiểm tra nó và sử dụng lại ở nơi khác. Nếu bạn có một số mã bạn không cần kiểm tra và không cần sử dụng lại thì việc sử dụng các biến toàn cục là tốt.


Tuy nhiên, lỗi chủ yếu là chương trình trong đó tập tin / dòng kịch bản là vi phạm như vậy .. Tôi không thấy vấn đề ở đây
samayo

8
Nơi mà tập lệnh bị hỏng! = Nơi xảy ra sai sót.
HonoredMule

16

tôi đồng ý với cletus. tôi sẽ thêm hai điều:

  1. sử dụng tiền tố để bạn có thể ngay lập tức xác định nó là toàn cầu (ví dụ: $ g_)
  2. khai báo chúng ở một chỗ, không đi rắc chúng xung quanh mã.

Trân trọng, don


1
Đúng vậy, tôi luôn đặt trước các biến mà tôi định sử dụng trên toàn cầu bằng một dấu gạch dưới.
KRTac

9
@KRTac nhưng $ _testVariable thường được hiểu là một biến riêng - đó là một tiêu chuẩn không chính thức để xác định các biến riêng, không phải biến toàn cục.
Aditya MP

6
Một thực tiễn phổ biến là xác định các vars toàn cục bằng cách sử dụng TẤT CẢ CÁC CHỮ HOA. ví dụ:$DB = 'foo';
pixeline

7

Ai có thể tranh luận chống lại kinh nghiệm, bằng đại học và kỹ thuật phần mềm? Không phải tôi. Tôi chỉ nói rằng trong việc phát triển các ứng dụng PHP một trang hướng đối tượng, tôi thấy vui hơn khi biết mình có thể xây dựng toàn bộ từ đầu mà không cần lo lắng về xung đột không gian tên. Xây dựng từ đầu là điều mà nhiều người không làm nữa. Họ có công việc, thời hạn, tiền thưởng, hoặc danh tiếng cần quan tâm. Những loại này có xu hướng sử dụng quá nhiều mã được tạo sẵn với tiền cược cao, đến mức chúng không thể mạo hiểm khi sử dụng các biến toàn cục.

Có thể không tốt khi sử dụng các biến toàn cục, ngay cả khi chúng chỉ được sử dụng trong phạm vi toàn cục của một chương trình, nhưng đừng quên về những người chỉ muốn giải trí và làm cho một thứ gì đó hoạt động .

Nếu điều đó có nghĩa là sử dụng một vài biến (<10) trong vùng tên chung, thì biến đó chỉ được sử dụng trong vùng toàn cục của một chương trình, hãy cứ như vậy. Có, có, MVC, tiêm phụ thuộc, mã bên ngoài, blah, blah, blah, blah. Nhưng, nếu bạn đã chứa 99,99% mã của mình vào không gian tên và lớp, và mã bên ngoài được hộp cát, thế giới sẽ không kết thúc (tôi nhắc lại, thế giới sẽ không kết thúc) nếu bạn sử dụng biến toàn cục.

Nói chung, tôi sẽ không nói việc sử dụng các biến toàn cục là một cách làm không tốt . Tôi muốn nói rằng việc sử dụng các biến toàn cục (cờ, v.v.) bên ngoài phạm vi toàn cầu của một chương trình sẽ gây ra rắc rối và (về lâu dài) không nên vì bạn có thể mất dấu trạng thái của chúng một cách dễ dàng. Ngoài ra, tôi muốn nói rằng bạn càng học nhiều, bạn sẽ càng ít phụ thuộc vào các biến toàn cục bởi vì bạn sẽ trải nghiệm "niềm vui" khi theo dõi các lỗi liên quan đến việc sử dụng chúng. Chỉ điều này thôi cũng sẽ khuyến khích bạn tìm ra cách khác để giải quyết vấn đề tương tự. Thật trùng hợp, điều này có xu hướng đẩy người PHP theo hướng học cách sử dụng không gian tên và lớp (thành viên tĩnh, v.v.).

Lĩnh vực khoa học máy tính rất rộng lớn. Nếu chúng ta xua đuổi mọi người làm điều gì đó vì chúng ta dán nhãn nó là xấu , thì họ sẽ mất niềm vui khi thực sự hiểu được lý do đằng sau cái nhãn đó.

Sử dụng các biến toàn cục nếu bạn phải, nhưng sau đó xem liệu bạn có thể giải quyết vấn đề mà không có chúng hay không. Va chạm, kiểm tra và gỡ lỗi có ý nghĩa hơn khi bạn hiểu sâu sắc bản chất thực sự của vấn đề, không chỉ là mô tả vấn đề.


3

Được đăng lại từ Bản thử nghiệm Tài liệu SO đã kết thúc

Chúng tôi có thể minh họa vấn đề này bằng mã giả sau

function foo() {
     global $bob;
     $bob->doSomething();
}

Câu hỏi đầu tiên của bạn ở đây là một câu hỏi hiển nhiên

Từ đâu $bobđến?

Bạn đang bối rối? Tốt. Bạn vừa tìm hiểu lý do tại sao các hình cầu lại gây nhầm lẫn và được coi là một thực tiễn xấu. Nếu đây là một chương trình thực, niềm vui tiếp theo của bạn là theo dõi tất cả các phiên bản $bobvà hy vọng bạn tìm thấy một chương trình phù hợp (điều này sẽ trở nên tồi tệ hơn nếu $bobđược sử dụng ở mọi nơi). Tệ hơn nữa, nếu ai đó đi và xác định $bob(hoặc bạn quên và sử dụng lại biến đó) thì mã của bạn có thể bị hỏng (trong ví dụ mã ở trên, có đối tượng sai hoặc không có đối tượng nào, sẽ gây ra lỗi nghiêm trọng). Vì hầu như tất cả các chương trình PHP đều sử dụng mã như include('file.php');công việc duy trì mã của bạn như thế này trở nên khó hơn theo cấp số nhân khi bạn thêm nhiều tệp hơn.

Làm thế nào để chúng ta tránh Globals?

Cách tốt nhất để tránh hình cầu là một triết lý có tên là Dependency Injection . Đây là nơi chúng ta chuyển các công cụ chúng ta cần vào hàm hoặc lớp.

function foo(\Bar $bob) {
    $bob->doSomething();
}

Đây là nhiều dễ hiểu và duy trì. Không có phỏng đoán nơi $bobđược thiết lập bởi vì người gọi có trách nhiệm biết điều đó (nó chuyển cho chúng tôi những gì chúng tôi cần biết). Vẫn tốt hơn, chúng ta có thể sử dụng khai báo kiểu để hạn chế những gì đang được chuyển. Vì vậy, chúng ta biết rằng đó $boblà một thể hiện của Barlớp, hoặc là một thể hiện con của Bar, nghĩa là chúng ta biết chúng ta có thể sử dụng các phương thức của lớp đó. Được kết hợp với một trình tải tự động tiêu chuẩn (có sẵn kể từ PHP 5.3), bây giờ chúng ta có thể truy tìm nơi Barđược xác định. PHP 7.0 trở lên bao gồm các khai báo kiểu mở rộng, nơi bạn cũng có thể sử dụng các kiểu vô hướng (như inthoặc string).


Một giải pháp thay thế cho việc phải truyền $ bob ở khắp mọi nơi, là biến lớp Bar thành một lớp đơn, lưu trữ một thể hiện của Bar tĩnh bên trong chính Bar và sử dụng phương thức tĩnh để khởi tạo / tìm nạp đối tượng. Sau đó, bạn có thể chỉ cần $bob = Bar::instance();bất cứ khi nào bạn cần.
Scoots

1
Chỉ cần lưu ý rằng Singletons được coi là một mô hình phản đối . Dependency Injection tránh những cạm bẫy đó
Machavity

2
Có một số mức độ tranh cãi trong bài đăng mà bạn liên kết đến (Ví dụ: nhận xét được xếp hạng cao nhất cho câu trả lời được chấp nhận và câu trả lời được ủng hộ nhiều thứ hai đều hoàn toàn không đồng ý với câu trả lời được chấp nhận), khiến tôi có xu hướng tranh luận rằng việc sử dụng Singletons nên được xem xét trên cơ sở từng trường hợp cụ thể, thay vì miễn nhiệm một cách tóm tắt.
Scoots

0

Như:

global $my_global; 
$my_global = 'Transport me between functions';
Equals $GLOBALS['my_global']

là thực hành xấu (Giống như Wordpress $pagenow) ... hmmm

Kết luận điều này:

$my-global = 'Transport me between functions';

là lỗi PHP Nhưng:

$GLOBALS['my-global'] = 'Transport me between functions';

KHÔNG phải là lỗi, hypens sẽ không xung đột với các biến do người dùng khai báo "thông thường", như $pagenow. Và Sử dụng UPPERCASE cho biết một superglobal đang được sử dụng, dễ dàng phát hiện trong mã hoặc theo dõi bằng cách tìm thấy trong tệp

Tôi sử dụng dấu gạch nối, nếu tôi lười xây dựng các lớp của mọi thứ cho một giải pháp duy nhất, như:

$GLOBALS['PREFIX-MY-GLOBAL'] = 'Transport me ... ';

Nhưng trong trường hợp sử dụng rộng rãi hơn, tôi sử dụng MỘT hình cầu làm mảng:

$GLOBALS['PREFIX-MY-GLOBAL']['context-something'] = 'Transport me ... ';
$GLOBALS['PREFIX-MY-GLOBAL']['context-something-else']['numbers'][] = 'Transport me ... ';

Đối với tôi, thực hành tốt về mục tiêu hoặc sử dụng "ánh sáng cola", thay vì lộn xộn với các lớp singleton mỗi lần để "lưu vào bộ nhớ cache" một số dữ liệu. Vui lòng cho ý kiến ​​nếu tôi sai hoặc thiếu một cái gì đó ngu ngốc ở đây ...

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.