Sichere Alternativen zu PHP Globals (Good Coding Practices)

8

Seit Jahren verwende ich global $var,$var2,...,$varn für Methoden in meiner Anwendung. Ich habe sie für zwei Hauptimplementierungen verwendet:

Abrufen einer bereits festgelegten Klasse (z. B. DB-Verbindung) und Übergeben von Informationen an Funktionen, die auf der Seite angezeigt werden.

Beispiel:

%Vor%

Es gibt jedoch Auswirkungen auf die Leistung und die Sicherheit, dies zu tun.

Welche alternative Vorgehensweise kann ich verwenden, um meine Funktionalität beizubehalten, aber Design, Leistung und / oder Sicherheit zu verbessern?

Das ist die erste Frage, die ich je zu SO gestellt habe. Wenn Sie also Erläuterungen benötigen, kommentieren Sie sie bitte!

    
ShaneC 03.09.2011, 03:33
quelle

6 Antworten

7

Die Alternative wird Abhängigkeitsinjektion genannt. Kurz gesagt, es bedeutet, dass Sie die Daten übergeben, die eine Funktion / Klasse / Objekt als Parameter benötigt.

%Vor%

Dies ist aus mehreren Gründen besser:

  • Lokalisierung / Kapselung / Namespacing-Funktionalität (der Funktionskörper hat keine impliziten Abhängigkeiten zur Außenwelt mehr und umgekehrt, Sie können nun beide Teile neu schreiben, ohne den anderen neu schreiben zu müssen, solange sich der Funktionsaufruf nicht ändert) / li>
  • ermöglicht Unit-Tests, da Sie Funktionen isoliert testen können, ohne dass Sie eine bestimmte Außenwelt einrichten müssen
  • Es ist klar, was eine Funktion mit Ihrem Code tun wird, wenn Sie nur auf die Signatur
  • schauen
deceze 03.09.2011, 03:47
quelle
12
  

1. Globals. Klappt wunderbar. Globals hassen so meine Gedanken, es nicht zu benutzen.

Nun, Globals werden nicht nur gehasst. Sie sind aus einem Grund gehasst. Wenn Sie nicht so weit in die Probleme laufen, die Globals verursachen, in Ordnung. Sie müssen Ihren Code nicht umgestalten.

  

2. Definieren Sie eine Konstante in meiner config.php Datei.

Das ist eigentlich wie ein globaler, aber mit einem anderen Namen. Sie würden auch die $ sparen und die global am Anfang der Funktionen verwenden. Wordpress tat dies für ihre Konfiguration, ich würde sagen, das ist schlimmer als die Verwendung globaler Variablen. Es macht es viel komplizierter, Nähte einzuführen. Sie können auch kein Objekt einer Konstanten zuweisen.

  

3. Fügen Sie die Konfigurationsdatei in die Funktion ein.

Ich würde das als Overhead betrachten. Sie segmentieren die Codebasis für nicht viel Gewinn. Das "global" wird hier der Name der Datei sein, die Sie btw .. enthalten.

Wenn Sie diese drei Gedanken von Ihnen und meine Kommentare zu ihnen in Betracht ziehen, würde ich sagen: Wenn Sie nicht in tatsächliche Probleme mit einigen globalen Variablen geraten, können Sie sich daran halten. Global arbeitet dann als Ihr Service Locator (Konfiguration, Datenbank). Andere tun viel mehr, um das Gleiche zu schaffen.

Wenn Sie auf Probleme stoßen (z. B. möchten Sie wahrscheinlich testgesteuert entwickeln), schlage ich vor, dass Sie einen Teil nach dem anderen testen und dann lernen, wie man die Globalen vermeidet.

Abhängigkeitsinjektion

In Kommentaren wurde deutlich, dass Sie nach Dependency-Injection suchen, und wenn Sie die Funktionsparameterdefinition nicht bearbeiten können, können Sie - wenn Sie Objekte verwenden - Abhängigkeiten über den Konstruktor oder mit einem so genannten -Setter injizieren Methoden . Im folgenden Beispielcode werde ich beides tun, was nur zu Demonstrationszwecken dient, wie Sie vielleicht erraten haben, es ist nicht sinnvoll, beides gleichzeitig zu verwenden:

Nehmen wir an, das Konfigurations-Array ist die Abhängigkeit, die wir injizieren möchten. Nennen wir es config und benennen Sie die Variable $config . Da es ein Array ist, können wir es als array eintippen. Definieren Sie zuerst die Konfiguration in einer Include-Datei. Sie könnten auch parse_ini_file verwenden, wenn Sie das ini-Dateiformat bevorzugen. Ich denke, es ist noch schneller.

config.php :

%Vor%

Diese Datei kann dann nur in Ihrer Anwendung benötigt werden, wo immer Sie wollen:

%Vor%

So kann es leicht in eine Array-Variable irgendwo in Ihrem Code umgewandelt werden. Bis jetzt nichts Spektakuläres und völlig unabhängig von Abhängigkeitsinjektionen. Sehen wir uns eine beispielhafte Datenbankklasse an, die die Konfiguration hier haben muss, sie muss den Benutzernamen und das Passwort haben, sonst kann sie keine Verbindung herstellen, sagen wir:

%Vor%

Dieses Beispiel ist etwas grob, hat aber zwei Beispiele für die Abhängigkeitsinjektion. Zuerst über den Konstruktor:

%Vor%

Dies ist sehr häufig, alle Abhängigkeiten, die die Klasse benötigt, um ihre Arbeit zu erledigen, sind die Injektion zur Erstellungszeit. Dies stellt auch sicher, dass, wenn eine andere Methode dieses Objekts aufgerufen wird, das Objekt in einem vorherbestimmbaren Zustand sein wird - was für ein System etwas wichtig ist.

Das zweite Beispiel ist eine öffentliche Setter-Methode :

%Vor%

Dies ermöglicht es, die Abhängigkeit später hinzuzufügen, aber einige Methoden müssen möglicherweise prüfen, ob Dinge verfügbar sind, bevor sie ihre Arbeit erledigen. Z.B. Wenn Sie das Objekt DBLayer ohne Konfiguration erstellen könnten, könnte die oneICanNotChange -Methode aufgerufen werden, ohne dass dieses Objekt eine Konfiguration hat, und sollte sich damit befassen (was in diesem Beispiel nicht gezeigt wird).

Service Locator

Da Sie wahrscheinlich Code on the fly integrieren müssen und möchten, dass Ihr neuer Code mit Dependency-Injection getestet wird und all das, was unser Leben einfacher macht, müssen Sie dies möglicherweise mit Ihrem alten / alten Code zusammenstellen. Ich denke, dieser Teil ist hart. Die Abhängigkeitsinjektion selbst ist ziemlich einfach, aber das zusammen mit altem Code zu tun ist nicht so einfach.

Was ich hier vorschlagen kann ist, dass Sie eine globale Variable erstellen, die der sogenannte Service-Locator ist. Es enthält einen zentralen Punkt, um Objekte (oder sogar Arrays wie Ihre $config ) abzurufen. Es kann dann verwendet werden und der Vertrag ist dieser einzelne Variablenname. Um Globals zu entfernen, verwenden wir eine globale Variable. Klingt etwas kontraproduktiv und es ist sogar, wenn Ihr neuer Code es auch zu viel verwendet. Sie benötigen jedoch ein Werkzeug, um Alt und Neu zusammenzubringen. Also hier ist die einfachste PHP Service Locator-Implementierung, die ich mir bisher vorstellen konnte.

Es besteht aus einem Services -Objekt, das alle Ihre Dienste anbietet, wie die config von oben. Denn wenn ein PHP-Skript gestartet wird, wissen wir noch nicht, ob überhaupt ein Dienst benötigt wird (z. B. können wir keine Datenbankabfrage ausführen, so dass wir die Datenbank nicht instanziieren müssen).Dies geschieht mithilfe von Factory-Skripten, bei denen es sich lediglich um PHP-Dateien handelt, die den Dienst einrichten und zurückgeben.

Ein erstes Beispiel: Nehmen wir an, die Funktion oneICanNotChange wäre kein Teil eines Objekts gewesen, sondern nur eine einfache Funktion im globalen Namespace. Wir hätten config dependency nicht injizieren können. Hier kommt das Services Service Locator-Objekt:

%Vor%

Wie das Beispiel bereits zeigt, bildet das Objekt Services den String 'config' auf den Pfad der PHP-Datei ab, die das Array $config definiert: /path/to/config.php . Es verwendet die ArrayAccess -Schnittstelle, um diesen Dienst in der oneICanNotChange -Funktion verfügbar zu machen.

Ich schlage hier die ArrayAccess -Schnittstelle vor, weil sie gut definiert ist und zeigt, dass wir hier einen dynamischen Charakter haben. Auf der anderen Seite erlaubt es uns die faule Initialisierung:

%Vor%

Dieser exemplarische Stub benötigt nur die Factory-Skripte, wenn er dies noch nicht getan hat, sonst gibt er den Rückgabewert des Skripts zurück, wie ein Array, ein Objekt oder sogar eine Zeichenkette (aber nicht NULL , was Sinn macht). p>

Ich hoffe, dass diese Beispiele hilfreich sind und zeigen, dass nicht viel Code benötigt wird, um hier mehr Flexibilität zu erlangen und Globals aus Ihrem Code herauszufiltern. Aber Sie sollten sich darüber im Klaren sein, dass der Service-Locator den globalen Status Ihres Codes einführt. Der Vorteil ist einfach, dass es einfacher ist, dies von konkreten Variablennamen zu entkoppeln und ein bisschen mehr Flexibilität zu bieten. Vielleicht können Sie die Objekte, die Sie in Ihrem Code verwenden, in bestimmte Gruppen unterteilen, von denen nur einige über den Service-Locator verfügbar sein müssen, und Sie können den Code klein halten, der vom Locator abhängt.

    
hakre 09.06.2012 17:25
quelle
7
  

Es gibt jedoch Auswirkungen auf die Leistung und die Sicherheit, dies zu tun.

Um Ihnen die Wahrheit zu sagen, gibt es weder Auswirkungen auf die Leistung noch auf die Sicherheit. Die Verwendung von Globals ist eine Frage von sauberem Code und nicht mehr. (Nun, okay, solange Sie nicht Variablen von mehreren zehn Megabyte Größe übergeben)

Also, Sie müssen zuerst nachdenken, werden Alternativen sauberer Code für Sie machen oder nicht.

In Sachen saubereren Code hätte ich Angst, wenn ich in der Funktion namens showPage eine db-Verbindung sehe.

    
Your Common Sense 03.09.2011 04:29
quelle
2

Eine Möglichkeit, über die manche Leute mürrisch sind, ist ein Singleton Objekt, das für das Halten zuständig ist der Anwendungsstatus Wenn Sie auf ein freigegebenes "globales" Objekt zugreifen möchten, können Sie einen Aufruf wie zum Beispiel: State::get()->db->query(); oder $db = State::get()->db; machen.

Ich sehe diese Methode als einen vernünftigen Ansatz an, da es dadurch erspart wird, eine Reihe von Objekten überall herumzubefördern.

BEARBEITEN:

Mit diesem Ansatz können Sie die Organisation und Lesbarkeit Ihrer Anwendung vereinfachen. Zum Beispiel könnte Ihre State-Klasse die richtigen Methoden aufrufen, um Ihr Datenbankobjekt zu initialisieren und seine Initialisierung von Ihrer showPage-Funktion zu entkoppeln.

%Vor%

und Ihre Show-Seitenfunktion könnte etwa so aussehen:

%Vor%

Eine Alternative zur Verwendung eines Singleton-Objekts besteht darin, das Status-Objekt an Ihre verschiedenen Funktionen zu übergeben. Dies ermöglicht Ihnen alternative "Zustände", wenn Ihre Anwendung kompliziert wird und Sie nur ein einzelnes Zustandsobjekt weitergeben müssen. p> %Vor%     

GWW 03.09.2011 03:42
quelle
0
%Vor%

und

%Vor%     
Alon Eitan 03.09.2011 03:39
quelle
0

Beginnen Sie, Ihren Code in OOP zu entwerfen, und übergeben Sie dann config an den Konstruktor. Sie könnten auch alle Ihre Funktionen in eine Klasse kapseln.

%Vor%

Oder wenn Sie das Skript nicht umstellen können, durchlaufen Sie das config-Array und definieren Sie Konstanten.

%Vor%     
Lawrence Cherone 09.06.2012 16:45
quelle

Tags und Links