Verstoßen $ _POST, $ _GET usw. gegen das Kapselungsprinzip?


9

Die Verwendung von Globals macht Ihren Code schwer zu testen und ist daher anfälliger für Fehler, nicht sicher und unvorhersehbar. Deshalb übergeben wir die gewünschten Variablen innerhalb einer Funktion / eines Objekts. Meine Frage ist also einfach:

Verstoßen $ _POST, $ _GET usw. gegen das Kapselungsprinzip ?

Ich denke, um die Kontrolle über diese Variablen auf OO-Weise zu behalten, wäre es eine ideale Lösung, dem Code einige Zeilen wie diese hinzuzufügen:

// Convert the $_GET array to an object
$get = json_decode(json_encode($_GET), FALSE);  // stackoverflow.com/a/1869147
// Stop it from being included from anywhere
unset($_GET);

// Small example of what could be done later on
$DB = new PDO(/* ... */);
$Person = new Person($DB, $get->id);

Ich habe das nirgendwo gesehen, nicht einmal ein Tutorial oder eine Empfehlung. Außerdem können wir deutlich sehen, dass der obige Code viel einfacher zu testen ist als einer, der ein $Person = new Person($DB, $_GET['id']);oder sogar (das Hässliche) enthält, $Person = new Person($DB);da Sie ein Scheinobjekt verwenden können $get.

Ist der Code oben in die richtige Richtung oder fehlt mir etwas?

EDIT: Nach einigen Untersuchungen ( Zend Framework und Cake PHP ), wie Alexander Kuzmin vorgeschlagen hat, scheint es das Richtige zu sein. Sie sind wahrscheinlich zu groß für mich, um in den Code-Geldautomaten zu graben, aber ich werde es mir merken.


1
Die Einkapselung erschien mir immer albern, als "Prinzip" zu gelten. Es ist nur eine Funktion; Entweder hat eine Sprache es oder nicht.
Ignacio Vazquez-Abrams

Ich bin kein Muttersprachler, daher war ich mir nicht sicher, wie ich es nennen sollte, und schrieb schließlich das Prinzip . Bitte zögern Sie nicht, den Titel / Text zu bearbeiten, wenn Sie sehen, dass er einen besseren Wortlaut haben könnte.
Francisco Presencia

6
Dies ist intelligent und auch die Art und Weise, wie die meisten PHP-Frameworks es lösen. Also ja, ich denke du bist auf etwas.
Alexander Kuzmin

Ich habe gerade dort nachgeforscht, @AlexanderKuzmin! Es scheint das Beste zu sein, aktualisiert.
Francisco Presencia

Kann ich fragen, warum es das meinungsbasierte Close-Tag hat? Während die zweite Unterfrage meiner Meinung nach meinungsbasiert ist (aber nur für das spezifische Beispiel), denke ich, dass die Hauptfrage gültig ist.
Francisco Presencia

Antworten:


8

Ich bin mir nicht ganz sicher, warum Sie sich json_decodefür $_GETdie Konvertierung in ein Array bewerben . $_GETbereits ist ein Array.

Verwendung der super-globals ( $_GET, $_POSTetc) ist eine Verletzung der Einkapselung Prinzip. Aber es sollte eine Linie gezogen werden, in der Sie aufhören, Dinge zu kapseln. Anforderungsdaten sind ein guter Kandidat für die Einkapselung, aber lassen Sie sich nicht in den Kaninchenbau hineinziehen, wenn Sie versuchen, alle Dinge einzukapseln .

Die meisten Frameworks verpacken normalerweise die Super-Globals von PHP in eine Art Anforderungsobjekt. Dies macht es dann einfacher, sich über Tests usw. lustig zu machen. Der einfachste Ansatz wäre:

<?php
class Request
{
    public $get;
    public $post;
    public $session;
    public $cookie;

    public function __construct($get, $post, $session, $cookie)
    {
        $this->get = $get;
        $this->post = $post;
        $this->session = $session;
        $this->cookie = $cookie;
    }
}

$request = new Request($_GET, $_POST, $_SESSION, $_COOKIE);

Es ist einfach und rudimentär, macht aber den Job. Es ist auch ratsam, die Daten an dieser Stelle zu filtern, um sich gegen XSS-Injektionen zu verteidigen.

Aber es ist in ein RequestObjekt eingewickelt . Das RequestObjekt verfügt über vier Arrays, und diese Arrays können leicht verspottet werden:

$get = array(
    'foo' => 'bar'
);
$post = array();
$session = array(
    'user' => 1
);
$cookie = array();

$request = new Request($get, $post, $session, $cookie);

Ich versuche, sie in Objekte zu konvertieren, wie im Kommentar angegeben // Convert the $_GET array to an object. Darüber hinaus ist diese Antwort stackoverflow.com/a/1869147 der Grund, warum ich es tue. Abgesehen von diesem kleinen Detail, vielen Dank für eine so vollständige Antwort mit zusätzlichen Tipps, die meiner Absicht sehr ähnlich ist.
Francisco Presencia

3
Ich würde keine $_GETDaten in ein Objekt konvertieren . Arrays sind nicht schmutzig. Ich habe das Gefühl, dass die Leute vor Arrays zurückschrecken, weil sie sich "nicht OOP" fühlen, so wie es niemand wagen würde, eine <table>in HTML zu verwenden, aus Angst, selbst mit tabellarischen Daten unsemantisch zu sein. Nimm das $_GETArray. Was passiert, wenn ich Array-Daten aus meinem Formular übergebe, dh <input type="checkbox" name="foo[]" />oder <select name="bar[]" multiple="multiple">? Wirst du sie in Objekte konvertieren oder so lassen, wie sie sind? Lassen Sie das $_GETArray einfach wie vorgesehen als Array.
Martin Bean

Ich habe auch über diesen Fall mit Arrays mit mehreren Ebenen nachgedacht und angenommen, dass sie zu Unterobjekten werden, aber die Annahmen sind nicht gut. Ich habe es (bisher schmutzig) in ein Objekt konvertiert, um den Getter und den Setter ändern zu können, was mir mehr Flexibilität geben würde, um beispielsweise die Ausgabe anhand der XSS-Injektion zu überprüfen. Dies kann jedoch auch in Ihrem vorgeschlagenen Schema erfolgen.
Francisco Presencia

Ja, Sie können sie auch einfach als mehrdimensionale Arrays belassen. Arrays sind nicht schmutzig und machen Ihren Code nicht "non-OOP", wenn Sie sie als Datenstruktur verwenden. Hab keine Angst vor ihnen. Wenn Ihre Daten keine Methoden oder Eigenschaften (wie GETund POSTDaten) haben, müssen sie wahrscheinlich kein Objekt sein.
Martin Bean

Ein kleines Nebenproblem / Problem ... Ich würde die Entscheidung abfragen, $ _SESSION in das Anforderungsobjekt aufzunehmen, da PHP das Superglobal $ _SESSION speziell behandelt (Sie müssten Getter / Setter implementieren und sicherstellen, dass die Sitzung wird früh gestartet usw.) und ist nicht wirklich Teil der IMO "Anfrage".
MrWhite

2

Die Verwendung von Superglobalen $_{POST,GET,SERVER}oder was auch immer verletzt sicherlich die Kapselung.

Dieses Problem tritt auf, wenn Sie "lokale Anforderungen" auf der Serverseite Ihrer Anwendung erstellen möchten, wie dies heutzutage bei vielen Frameworks der Fall ist.

Ich bin es nicht gewohnt, mit Frameworks zu arbeiten, aber normalerweise erstelle ich zu Beginn meiner Verarbeitung ein Request / Response-Paar. Die Anfrage enthält die Werte dieser globalen Parameter.

Wenn ich eine serverseitige Unteranforderung erstellen möchte, habe ich zwei Möglichkeiten: Verwenden Sie den aktuellen Kontext oder erstellen Sie einen völlig neuen. Ich denke, Sie sollten diese superglobalen Variablen nicht deaktivieren, da Sie sie möglicherweise erneut verwenden möchten. Aus diesem Grund bin ich auch nicht der Meinung, dass Anforderungsparameter Singletons sein sollten.

Wenn nur Werte und keine Verweise auf diese Superglobalen enthalten sind, wirkt sich eine Änderung in einem Anforderungsobjekt niemals auf ein anderes aus. Daher ist das Problem mit dem globalen Status gelöst.

Grundsätzlich habe ich zwei Möglichkeiten:

// Using global context
$request = new Request(array(
    'post' => $_POST,
    'get' => $_GET
));

// or creating a new context

$request = new Request(array(
    'post' => ['someKey' => 'someValue'],
    'get' => ['queryParam' => 'queryValue'],
));

0

POST- und GET-Variablen werden in einer Masse an den Server gesendet, und PHP muss einen Sinn daraus machen. In gewisser Weise ist es sinnvoll, sie global verfügbar zu haben, damit der Entwickler auswählen kann, wo sie verarbeitet werden sollen.

Viele Frameworks (wie z. B. CakePHP) lesen die Parameter und platzieren sie dann alle in einem Array, Objekt oder einer ähnlichen Struktur. Danach werden sie wie alle anderen Daten behandelt und an alle Methoden übergeben, die sie benötigen.


1
Meinen Sie damit, dass es so funktioniert, weil PHP keine lokale Variable in das aktuelle Hauptskript wie einfügen kann index.php?
Francisco Presencia

Nein, aber die Bereitstellung der Variablen über ein globales Array bietet mehr Flexibilität für die Implementierung einer Form der Analyse durch den Entwickler.

0

Die Kapselung ist ein gutes Prinzip, wenn die Möglichkeit besteht, dass Sie mehrere Instanzen von etwas benötigen. Eine Webseite enthält jedoch nur einen Parametersatz. Wenn sie in einer Klasse statt in globalen Variablen wären, wären sie wahrscheinlich Singletons. Es gibt keine signifikante Verbesserung von globalen Variablen zu Singleton-Klassen, es ist nur eine andere Syntax, um auf sie zuzugreifen. Es handelt sich immer noch um von Natur aus globale Objekte. Dies ist nur umständlicher, da Sie die Klasseninstanz in den Griff bekommen und weitergeben müssen.

Da auf diese Parameter so häufig zugegriffen wird, haben die PHP-Designer beschlossen, den Zugriff auf sie zu vereinfachen, anstatt strenge Designprinzipien einzuhalten. Es ist eine gute Idee, die gängigsten Vorgänge bequem zu gestalten. Andernfalls verfluchen Sie Programmierer, weil sie jedes Mal dieselbe lange Sache erneut eingeben.


Ich stimme zwar dem häufigen Zugriff auf sie zu, stimme dem jedoch nicht zu, If they were in a class instead of global variables, they would probably be singletonsda Singletons ebenfalls im globalen Bereich liegen . Ich sage nur, dass dieser globale Staat vollständig gelöscht und lokalisiert werden soll, wobei diese Idee aus den Clean Code Talks - "Global State and Singletons" - übernommen wurde . Es ist mein Hauptanliegen, diese Frage zu stellen: Testen . Leider scheinen Sie nicht die ganze Frage gelesen zu haben, sondern nur den Titel
Francisco Presencia

1
Mein Punkt ist, dass, da es nur eine Client-Verbindung und einen Satz von Parametern gibt, diese von Natur aus global sind.

Es ist ein Kompromiss: Bequemlichkeit versus strikte Einhaltung der Designprinzipien. Wenn Sie die Superglobalen loswerden, müssen Sie $getvon Funktion zu Funktion weitergeben.
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.