Werden globale Variablen in PHP als schlechte Praxis angesehen? Wenn ja warum?

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

In meinen kleinen PHP-Projekten gehe ich normalerweise den prozeduralen Weg. Ich habe im Allgemeinen eine Variable, die die Systemkonfiguration enthält, und wenn ich in einer Funktion auf diese Variable zugreifen möchte, tue ich dies global $var;.

Ist das eine schlechte Praxis?

KRTac
quelle
19
globale Variable ist ein Synonym für schlechte Praxis
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
2
Versuchen Sie es mit Unit- / Akzeptanztests, und Sie werden schnell feststellen, warum Globals ein Problem sind: Sie machen Ihren Code unzuverlässig, wenn Sie mehr als einmal Dinge tun.
Kzqai

Antworten:

101

Wenn Menschen über globale Variablen in anderen Sprachen sprechen, bedeutet dies etwas anderes als in PHP. Das liegt daran, dass Variablen in PHP nicht wirklich global sind. Der Umfang eines typischen PHP-Programms ist eine HTTP-Anforderung. Sitzungsvariablen haben tatsächlich einen größeren Umfang als "globale" PHP-Variablen, da sie normalerweise viele HTTP-Anforderungen umfassen.

Oft (immer?) Können Sie Mitgliedsfunktionen mit folgenden Methoden aufrufen preg_replace_callback():

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

Weitere Informationen finden Sie unter Rückrufe .

Der Punkt ist, dass Objekte mit PHP verschraubt wurden und in gewisser Weise zu Unbeholfenheit führen.

Kümmern Sie sich nicht zu sehr darum, Standards oder Konstrukte aus verschiedenen Sprachen auf PHP anzuwenden. Eine weitere häufige Gefahr besteht darin, PHP in eine reine OOP-Sprache umzuwandeln, indem Objektmodelle auf alles geklebt werden.

Verwenden Sie wie alles andere "globale" Variablen, prozeduralen Code, ein bestimmtes Framework und OOP, da dies sinnvoll ist, ein Problem löst, die Menge an Code reduziert, die Sie schreiben müssen, oder es wartbarer und verständlicher macht, nicht weil Sie denken Du solltest.

Cletus
quelle
8
Es sollte beachtet werden, dass PHP 5.3 einige dieser Probleme mit Lambda-Funktionen behebt, sodass Sie die Verwendung von Funktionen vermeiden können, die im globalen Bereich für Rückrufe deklariert sind. +1 für wartbare, lesbare Code-Ratschläge
Jonathan Fingland
Können Sie einen Rückruf des Formulars nicht array ($obj, 'callbackMethod')bei Anrufen an verwenden preg_replace_callback()? (Ich weiß, ich bin dieser OOP-
Falle
24
Die Frage war nicht "sollten jemals globale Variablen verwendet werden?". Die Antwort darauf wäre "gelegentlich sicher, wenn nötig". Die Frage ist, ob sie schlecht sind. Die Antwort lautet "Ja, manchmal". Für das winzige Projekt des Posters kann nichts Schlimmes daraus werden. Bei größeren Projekten mit vielen Teammitgliedern und vielen beweglichen Teilen wird der Code jedoch durch die starke Verwendung globaler Variablen schwer zu debuggen, fast unmöglich umzugestalten und sogar schmerzhaft lesen. Kannst du sie manchmal benutzen, .. sicher - saugen sie kina, .. ja!
Eddiemoya
@eddiemoya Gut gesagt, Eddie. Es gibt so viele Leute, die schlechte Praktiken wie die Verwendung globaler Variablen rechtfertigen. Sie sollten sie wie die Pest vermeiden. Jeder anständige Abschluss in Software-Engineering wird Ihnen dies näher bringen ... die Dozenten erzählen es Ihnen nicht nur zum Teufel ... sie wissen es aus jahrzehntelanger Erfahrung. Sie sollten nach Möglichkeit Elementfunktionen verwenden, um auf die benötigten Werte zuzugreifen, z. B. get_query_var () in Wordpress usw.
27

Wenn globale Variablen nicht sorgfältig verwendet werden, kann es schwieriger werden, Probleme zu finden. Angenommen, Sie fordern ein PHP-Skript an und erhalten eine Warnung, dass Sie versuchen, auf einen Index eines Arrays zuzugreifen, der in einer Funktion nicht vorhanden ist.

Wenn das Array, auf das Sie zugreifen möchten, lokal für die Funktion ist, überprüfen Sie die Funktion, um festzustellen, ob Sie dort einen Fehler gemacht haben. Möglicherweise liegt ein Problem mit einer Eingabe in die Funktion vor, sodass Sie die Stellen überprüfen, an denen die Funktion aufgerufen wird.

Wenn dieses Array jedoch global ist, müssen Sie alle Stellen überprüfen, an denen Sie diese globale Variable verwenden, und nicht nur das, sondern auch herausfinden, in welcher Reihenfolge auf diese Verweise auf die globale Variable zugegriffen wird.

Wenn Sie eine globale Variable in einem Code haben, ist es schwierig, die Funktionalität dieses Codes zu isolieren. Warum sollten Sie die Funktionalität isolieren wollen? Sie können es also testen und an anderer Stelle wiederverwenden. Wenn Sie Code haben, den Sie nicht testen und nicht wiederverwenden müssen, ist die Verwendung globaler Variablen in Ordnung.

Rojoca
quelle
Aber der Fehler zeigt meistens, in welcher Datei / Zeile das Skript so bricht. Ich sehe das Problem hier nicht
Samayo
8
Ort, an dem das Skript gebrochen ist! = Ort, an dem ein Fehler gemacht wurde.
HonoredMule
16

Ich stimme Cletus zu. Ich würde zwei Dinge hinzufügen:

  1. Verwenden Sie ein Präfix, damit Sie es sofort als global identifizieren können (z. B. $ g_).
  2. deklarieren Sie sie an einer Stelle, streuen Sie sie nicht rund um den Code.

Mit freundlichen Grüßen, don

Don Dickinson
quelle
1
Ja, ich stelle Variablen, die ich global verwenden möchte, immer einen Unterstrich voran.
KRTac
9
@KRTac, aber $ _testVariable wird normalerweise als private Variable verstanden - dies ist ein informeller Standard zum Definieren privater Variablen, nicht globaler Variablen.
Aditya MP
6
Es ist üblich, globale Variablen mit ALL CAPS zu definieren. Beispiel:$DB = 'foo';
Pixeline
7

Wer kann gegen Erfahrung, Hochschulabschlüsse und Software-Engineering argumentieren? Nicht ich. Ich würde nur sagen, dass ich bei der Entwicklung objektorientierter PHP-Anwendungen für einzelne Seiten mehr Spaß habe, wenn ich weiß, dass ich das Ganze von Grund auf neu erstellen kann, ohne mir Gedanken über Namespace-Kollisionen machen zu müssen. Von Grund auf neu zu bauen ist etwas, was viele Menschen nicht mehr tun. Sie haben einen Job, eine Frist, einen Bonus oder einen Ruf, um den sie sich kümmern müssen. Diese Typen verwenden in der Regel so viel vorgefertigten Code mit hohen Einsätzen, dass sie nicht riskieren können, überhaupt globale Variablen zu verwenden.

Es mag schlecht sein, globale Variablen zu verwenden, auch wenn sie nur im globalen Bereich eines Programms verwendet werden. Vergessen wir jedoch nicht diejenigen, die einfach nur Spaß haben und etwas zum Laufen bringen möchten .

Wenn dies bedeutet, dass einige Variablen (<10) im globalen Namespace verwendet werden, werden diese nur im globalen Bereich eines Programms verwendet. Ja, ja, MVC, Abhängigkeitsinjektion, externer Code, bla, bla, bla, bla. Wenn Sie jedoch 99,99% Ihres Codes in Namespaces und Klassen enthalten haben und externer Code in einer Sandbox gespeichert ist, endet die Welt nicht (ich wiederhole, die Welt endet nicht), wenn Sie eine globale Variable verwenden.

Im Allgemeinen würde ich nicht sagen, dass die Verwendung globaler Variablen eine schlechte Praxis ist . Ich würde sagen, dass die Verwendung globaler Variablen (Flags und dergleichen) außerhalb des globalen Bereichs eines Programms Probleme bereitet und (auf lange Sicht) schlecht beraten ist, da Sie den Überblick über ihre Zustände ziemlich leicht verlieren können. Außerdem würde ich sagen, je mehr Sie lernen, desto weniger sind Sie auf globale Variablen angewiesen , da Sie die "Freude" erlebt haben, Fehler aufzuspüren, die mit ihrer Verwendung verbunden sind. Dies allein wird Sie dazu anregen, einen anderen Weg zu finden, um dasselbe Problem zu lösen. Zufälligerweise führt dies dazu, dass PHP-Benutzer lernen, wie man Namespaces und Klassen (statische Mitglieder usw.) verwendet.

Das Gebiet der Informatik ist riesig. Wenn wir alle davon abhalten, etwas zu tun, weil wir es als schlecht bezeichnen , verlieren sie den Spaß daran, die Gründe für das Etikett wirklich zu verstehen.

Verwenden Sie gegebenenfalls globale Variablen, aber prüfen Sie dann, ob Sie das Problem ohne sie lösen können. Kollisionen, Tests und Debugging bedeuten mehr, wenn Sie die wahre Natur des Problems genau verstehen, nicht nur eine Beschreibung des Problems.

Anthony Rutledge
quelle
3

Reposted von der beendeten SO Documentation Beta

Wir können dieses Problem mit dem folgenden Pseudocode veranschaulichen

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

Ihre erste Frage hier ist offensichtlich

Woher kam $bobes?

Bist du verwirrt? Gut. Sie haben gerade erfahren, warum Globals verwirrend sind und als schlechte Praxis angesehen werden. Wenn dies ein echtes Programm wäre, besteht Ihr nächster Spaß darin, alle Instanzen aufzuspüren $bobund zu hoffen, dass Sie das richtige finden (dies wird schlimmer, wenn $bobes überall verwendet wird). Schlimmer noch, wenn jemand anderes $bobdiese Variable definiert (oder Sie diese Variable vergessen und wiederverwendet haben), kann Ihr Code beschädigt werden (im obigen Codebeispiel würde ein falsches Objekt oder gar kein Objekt einen schwerwiegenden Fehler verursachen). Da praktisch alle PHP-Programme Code wie include('file.php');Ihren Job verwenden, wird die Verwaltung von Code wie diesem exponentiell schwieriger, je mehr Dateien Sie hinzufügen.

Wie vermeiden wir Globals?

Der beste Weg, um Globals zu vermeiden, ist eine Philosophie namens Dependency Injection . Hier übergeben wir die benötigten Werkzeuge an die Funktion oder Klasse.

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

Dies ist viel einfacher zu verstehen und zu pflegen. Es gibt keine Vermutung, wo $bobeingerichtet wurde, da der Anrufer dafür verantwortlich ist, dies zu wissen (er übergibt uns das, was wir wissen müssen). Besser noch, wir können Typdeklarationen verwenden, um die Übergabe einzuschränken. Wir wissen also, dass dies $bobentweder eine Instanz der BarKlasse oder eine Instanz eines Kindes von ist Bar, was bedeutet, dass wir wissen, dass wir die Methoden dieser Klasse verwenden können. In Kombination mit einem Standard-Autoloader (verfügbar seit PHP 5.3) können wir jetzt nachverfolgen, wo Bardefiniert ist. PHP 7.0 oder höher enthält erweiterte Typdeklarationen, in denen Sie auch skalare Typen (wie intoder string) verwenden können.

Machavity
quelle
Eine Alternative dazu, $ bob überall übergeben zu müssen, besteht darin, die Klasse Bar zu einem Singleton zu machen, eine Instanz von Bar statisch in Bar selbst zu speichern und das Objekt mithilfe einer statischen Methode zu instanziieren / abzurufen. Dann können Sie einfach $bob = Bar::instance();wann immer Sie es brauchen.
Scoots
1
Seien Sie sich nur bewusst, dass Singletons als Anti-Pattern angesehen werden . Abhängigkeitsinjektion vermeidet diese Fallstricke
Machavity
2
In diesem Beitrag, auf den Sie verlinkt haben, gibt es einen gewissen Grad an Streit (zum Beispiel stimmen der am besten bewertete Kommentar zur akzeptierten Antwort und die am zweithäufigsten bewertete Antwort nicht mit der akzeptierten Antwort überein), was mich dazu veranlasst, zu argumentieren, dass die Verwendung von Singletons sollte von Fall zu Fall geprüft werden, anstatt sofort außer Kraft gesetzt zu werden.
Scoots
0

Wie:

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

ist eine schlechte Praxis (wie Wordpress $pagenow) ... hmmm

Betrachten Sie dies:

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

ist PHP-Fehler Aber:

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

ist KEIN Fehler, Hypens kollidieren nicht mit "allgemeinen" vom Benutzer deklarierten Variablen wie $pagenow. Die Verwendung von UPPERCASE weist auf eine Superglobalität hin, die verwendet wird, leicht im Code zu erkennen ist oder mit der Suche in Dateien verfolgt wird

Ich benutze Bindestriche, wenn ich faul bin, Klassen von allem für eine einzige Lösung zu erstellen, wie:

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

Aber in Fällen einer breiteren Verwendung verwende ich EIN Globale als Array:

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

Letzteres ist für mich eine gute Praxis für "Cola Light" -Objektive oder -Verwendung, anstatt jedes Mal mit Singleton-Klassen zu überladen, um einige Daten "zwischenzuspeichern". Bitte machen Sie einen Kommentar, wenn ich falsch liege oder etwas Dummes hier vermisse ...

Jonas Lundman
quelle