Czy zmienne globalne w PHP są uważane za złą praktykę? Jeśli tak, dlaczego?

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

W moich małych projektach PHP zwykle podążam drogą proceduralną. Generalnie mam zmienną, która zawiera konfigurację systemu, a kiedy chcę uzyskać dostęp do tej zmiennej w funkcji, robię to global $var;.

Czy to zła praktyka?

KRTac
źródło
19
zmienna globalna jest synonimem złych praktyk
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
2
Wypróbuj testowanie jednostkowe / akceptacyjne, a szybko przekonasz się, dlaczego globalne są problemem: powodują, że twój kod jest niewiarygodny, gdy robisz coś więcej niż raz.
Kzqai

Odpowiedzi:

102

Kiedy ludzie mówią o zmiennych globalnych w innych językach, oznacza to coś innego niż to, co robi się w PHP. Dzieje się tak, ponieważ zmienne nie są tak naprawdę globalne w PHP. Zakres typowego programu PHP to jedno żądanie HTTP. Zmienne sesji mają w rzeczywistości szerszy zakres niż zmienne „globalne” PHP, ponieważ zazwyczaj obejmują wiele żądań HTTP.

Często (zawsze?) Możesz wywołać funkcje składowe w metodach takich preg_replace_callback()jak ta:

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

Zobacz oddzwonienia, aby uzyskać więcej informacji.

Chodzi o to, że obiekty zostały przykręcone do PHP i pod pewnymi względami prowadzą do pewnej niezręczności.

Nie przejmuj się zbytnio stosowaniem standardów lub konstrukcji z różnych języków do PHP. Inną częstą pułapką jest próba przekształcenia PHP w czysty język OOP poprzez umieszczanie modeli obiektowych na wszystkim.

Jak wszystko inne, używaj zmiennych „globalnych”, kodu proceduralnego, określonego frameworka i OOP, ponieważ ma to sens, rozwiązuje problem, zmniejsza ilość kodu, który musisz napisać lub sprawia, że ​​jest łatwiejszy do utrzymania i łatwiejszy do zrozumienia, nie dlatego, że myślisz powinieneś.

cletus
źródło
8
Należy zauważyć, że PHP 5.3 rozwiązuje niektóre z tych problemów za pomocą funkcji lambda, co pozwala uniknąć używania funkcji zadeklarowanej w zasięgu globalnym do wywołań zwrotnych. +1 za
łatwe
Czy nie możesz użyć wywołania zwrotnego formularza array ($obj, 'callbackMethod')w wywołaniach preg_replace_callback()? (Wiem,
padłem
25
Pytanie nie brzmiało „czy kiedykolwiek należy używać zmiennych globalnych?”. Odpowiedź na to brzmiałaby: „w razie potrzeby z pewnością”. Pytanie brzmi, czy to zła praktyka. Odpowiedź brzmi „czasami tak”. W przypadku małego projektu plakatów nic złego może z tego nie wyniknąć - jednak w przypadku większych projektów z wieloma członkami zespołu i wieloma ruchomymi częściami, intensywne użycie zmiennych globalnych sprawi, że kod będzie trudny do debugowania, prawie niemożliwy do refaktoryzacji, a nawet uciążliwy czytać. Czy możesz ich czasem używać,… jasne - czy one są do bani… tak!
eddiemoya,
@eddiemoya Dobrze powiedział Eddie. Jest tak wielu ludzi usprawiedliwiających złe praktyki, takie jak używanie zmiennych globalnych. Unikaj ich jak zarazy. Każdy przyzwoity stopień inżyniera oprogramowania wbije to w ciebie ... wykładowcy nie tylko mówią ci o tym, ale wiedzą z dziesięcioleci doświadczenia. W miarę możliwości powinieneś używać funkcji członkowskich, aby uzyskać dostęp do potrzebnych wartości, na przykład get_query_var () w Wordpress itp.
27

Zmienne globalne, jeśli nie są używane ostrożnie, mogą utrudnić znalezienie problemów. Powiedzmy, że żądasz skryptu php i otrzymujesz ostrzeżenie, że próbujesz uzyskać dostęp do indeksu tablicy, która nie istnieje w jakiejś funkcji.

Jeśli tablica, do której próbujesz uzyskać dostęp, jest lokalna dla funkcji, sprawdzasz funkcję, aby zobaczyć, czy nie popełniłeś tam błędu. Może to być problem z danymi wejściowymi do funkcji, więc sprawdzasz miejsca, w których funkcja jest wywoływana.

Ale jeśli ta tablica jest globalna, musisz sprawdzić wszystkie miejsca, w których używasz tej zmiennej globalnej, i nie tylko, musisz dowiedzieć się, w jakiej kolejności uzyskuje się dostęp do tych odwołań do zmiennej globalnej.

Jeśli masz zmienną globalną w fragmencie kodu, trudno jest wyodrębnić funkcjonalność tego kodu. Dlaczego chcesz wyodrębnić funkcjonalność? Możesz więc go przetestować i użyć ponownie w innym miejscu. Jeśli masz kod, którego nie musisz testować i nie musisz go ponownie używać, użycie zmiennych globalnych jest w porządku.

rojoca
źródło
Ale błąd głównie pokazuje, w którym pliku / linii skrypt się
psuje,
8
Miejsce, w którym zepsuł się skrypt! = Miejsce, w którym popełniono błąd.
HonoredMule
16

zgadzam się z cletusem. dodałbym dwie rzeczy:

  1. użyj przedrostka, aby od razu zidentyfikować go jako globalny (np. $ g_)
  2. zadeklaruj je w jednym miejscu, nie rozsypuj ich po całym kodzie.

pozdrawiam, don

Don Dickinson
źródło
1
Jezu, zawsze poprzedzam zmienne, których zamierzam używać globalnie, za pomocą podkreślenia.
KRTac
9
@KRTac, ale $ _testVariable jest zwykle rozumiana jako zmienna prywatna - jest to nieformalny standard definiowania zmiennych prywatnych, a nie globalnych.
Aditya MP
6
Powszechną praktyką jest definiowanie zmiennych globalnych przy użyciu WIELKICH LITER. przykład:$DB = 'foo';
pixeline
7

Kto może spierać się z doświadczeniem, stopniami wyższymi i inżynierią oprogramowania? Nie ja. Powiedziałbym tylko, że przy tworzeniu aplikacji PHP zorientowanych obiektowo na pojedynczą stronę, bawię się lepiej, gdy wiem, że mogę zbudować całość od zera, nie martwiąc się o kolizje przestrzeni nazw. Budowanie od podstaw to coś, czego wiele osób już nie robi. Mają pracę, termin, premię lub reputację, o którą muszą się troszczyć. Te typy mają tendencję do używania tak dużo gotowego kodu z wysokimi stawkami, że nie mogą w ogóle ryzykować używania zmiennych globalnych.

Używanie zmiennych globalnych może być złe, nawet jeśli są one używane tylko w globalnym obszarze programu, ale nie zapominajmy o tych, którzy po prostu chcą się bawić i sprawić, by coś działało .

Jeśli to oznacza użycie kilku zmiennych (<10) w globalnej przestrzeni nazw, które są używane tylko w globalnym obszarze programu, niech tak będzie. Tak, tak, MVC, wstrzykiwanie zależności, kod zewnętrzny, bla, bla, bla, bla. Ale jeśli zawarłeś 99,99% swojego kodu w przestrzeni nazw i klas, a kod zewnętrzny jest w piaskownicy, świat się nie skończy (powtarzam, świat się nie skończy), jeśli użyjesz zmiennej globalnej.

Generalnie nie powiedziałbym, że używanie zmiennych globalnych jest złą praktyką . Powiedziałbym, że używanie zmiennych globalnych (flag itp.) Poza globalnym obszarem programu jest problemem i (na dłuższą metę) nierozsądne, ponieważ można dość łatwo stracić orientację w ich stanach. Powiedziałbym również, że im więcej się nauczysz, tym mniej będziesz zależny od zmiennych globalnych, ponieważ doświadczysz „radości” ze śledzenia błędów związanych z ich użyciem. Już samo to zachęci Cię do znalezienia innego sposobu rozwiązania tego samego problemu. Przypadkowo popycha to ludzi PHP w kierunku uczenia się, jak używać przestrzeni nazw i klas (statyczne elementy członkowskie itp.).

Dziedzina informatyki jest rozległa. Jeśli odstraszamy wszystkich od zrobienia czegoś, ponieważ określamy to jako złe , wtedy tracą radość z prawdziwego zrozumienia argumentów stojących za etykietą.

Jeśli musisz, użyj zmiennych globalnych, ale zobacz, czy możesz rozwiązać problem bez nich. Kolizje, testowanie i debugowanie mają większe znaczenie, gdy dokładnie zrozumiesz prawdziwą naturę problemu, a nie tylko opis problemu.

Anthony Rutledge
źródło
3

Opublikowane ponownie z zakończonej wersji beta dokumentacji SO

Możemy zilustrować ten problem następującym pseudokodem

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

Twoje pierwsze pytanie jest oczywiste

Skąd się wziął $bob?

Jesteś zmieszany? Dobry. Właśnie dowiedziałeś się, dlaczego globale są mylące i uważane za złą praktykę. Gdyby to był prawdziwy program, następną zabawą będzie wytropienie wszystkich jego wystąpień $bobi miejmy nadzieję, że znajdziesz właściwy (to się pogarsza, jeśli $bobjest używany wszędzie). Gorzej, jeśli ktoś inny podejdzie i zdefiniuje $bob(lub zapomnisz i ponownie użyjesz tej zmiennej), twój kod może się zepsuć (w powyższym przykładzie kodu, posiadanie niewłaściwego obiektu lub jego brak spowodowałby błąd krytyczny). Ponieważ praktycznie wszystkie programy PHP wykorzystują kod, tak jak include('file.php');twoja praca, utrzymanie takiego kodu staje się wykładniczo trudniejsze, im więcej plików dodajesz.

Jak unikamy Globals?

Najlepszym sposobem na uniknięcie zjawisk globalnych jest filozofia zwana Dependency Injection . W tym miejscu przekazujemy potrzebne nam narzędzia do funkcji lub klasy.

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

Jest to znacznie łatwiejsze do zrozumienia i utrzymania. Nie ma zgadywania, gdzie $bobzostało ustawione, ponieważ dzwoniący jest odpowiedzialny za to, aby to wiedzieć (przekazuje nam to, co musimy wiedzieć). Co więcej, możemy użyć deklaracji typu, aby ograniczyć to, co jest przekazywane. Więc wiemy, że $bobjest to instancja Barklasy lub instancja dziecka Bar, co oznacza, że ​​wiemy, że możemy użyć metod tej klasy. W połączeniu ze standardowym autoloaderem (dostępnym od PHP 5.3), możemy teraz sprawdzić, gdzie Barzostało zdefiniowane. PHP 7.0 lub nowszy zawiera rozszerzone deklaracje typów, w których można również używać typów skalarnych (takich jak intlub string).

Machavity
źródło
Alternatywą dla konieczności przekazywania $ bob wszędzie jest uczynienie klasy Bar singletonem, przechowywanie instancji Bar statycznie wewnątrz samego Bar i użycie statycznej metody do utworzenia instancji / pobrania obiektu. Wtedy możesz $bob = Bar::instance();zawsze, kiedy tego potrzebujesz.
Scoots
1
Pamiętaj tylko, że Singletony są uważane za anty-wzór . Dependency Injection pozwala uniknąć tych pułapek
Machavity
2
W poście, do którego utworzyłeś link, jest pewien stopień niezgody (na przykład najwyżej oceniany komentarz do zaakceptowanej odpowiedzi i druga najbardziej pozytywna odpowiedź zdecydowanie nie zgadzają się z zaakceptowaną odpowiedzią), co skłoniło mnie do argumentowania, że ​​użycie singletonów powinno być rozpatrywane indywidualnie dla każdego przypadku, a nie natychmiastowo odrzucane.
Scoots
0

Tak jak:

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

to zła praktyka (jak Wordpress $pagenow) ... hmmm

Rozważ to:

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

to błąd PHP Ale:

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

NIE jest błędem, myślniki nie będą kolidować z "typowymi" zmiennymi zadeklarowanymi przez użytkownika, takimi jak $pagenow. A użycie WIELKICH LITER oznacza superglobalny w użyciu, łatwy do wykrycia w kodzie lub śledzenia za pomocą funkcji znajdowania w plikach

Używam łączników, jeśli jestem leniwy, aby zbudować klasy wszystkiego dla jednego rozwiązania, na przykład:

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

Ale w przypadku szerszego zastosowania używam JEDNEGO globalnego jako tablicy:

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

Ta ostatnia jest dla mnie dobrą praktyką na obiektywach typu „cola light” lub używaniu, zamiast zaśmiecania za każdym razem pojedynczymi klasami, aby „buforować” jakieś dane. Proszę o komentarz, jeśli się mylę lub brakuje mi czegoś głupiego ...

Jonas Lundman
źródło