PHP "php: // input" vs $ _POST

243

Ich wurde angewiesen, die Methode php://inputanstelle der $_POSTInteraktion mit Ajax-Anforderungen von JQuery zu verwenden. Was ich nicht verstehe, sind die Vorteile dieser Verwendung gegenüber der globalen Methode von $_POSToder $_GET.

Lee
quelle
2
Ich habe "Hacks" verwendet, um Ajax-Anrufe auf PHP-Seite zu empfangen, bevor ich auf diesen Beitrag gestoßen bin und seine fantastischen Antworten gelesen habe! Für andere Leute, die in Zukunft das gleiche Problem haben, hoffe ich, dass Suchmaschinen auch meinen Kommentar lesen werden! :)
Aderchox

Antworten:

484

Der Grund ist, dass php://inputalle Rohdaten nach den HTTP-Headern der Anfrage zurückgegeben werden, unabhängig vom Inhaltstyp.

Die PHP - Superglobal $_POST, nur auf vermeintliche Wrap Daten , die entweder

  • application/x-www-form-urlencoded (Standardinhaltstyp für einfache Formularbeiträge) oder
  • multipart/form-data (meistens für Datei-Uploads verwendet)

Dies liegt daran, dass dies die einzigen Inhaltstypen sind , die von Benutzeragenten unterstützt werden müssen . Daher erwarten der Server und PHP traditionell keinen anderen Inhaltstyp (was nicht bedeutet, dass sie dies nicht können).

Wenn Sie also einfach einen guten alten HTML-Code veröffentlichen form, sieht die Anfrage ungefähr so ​​aus:

POST /page.php HTTP/1.1

key1=value1&key2=value2&key3=value3

Wenn Sie jedoch häufig mit Ajax arbeiten, umfasst dieses Probaby auch den Austausch komplexerer Daten mit Typen (Zeichenfolge, Int, Bool) und Strukturen (Arrays, Objekte). In den meisten Fällen ist JSON daher die beste Wahl. Eine Anfrage mit einer JSON-Nutzlast würde jedoch ungefähr so ​​aussehen:

POST /page.php HTTP/1.1

{"key1":"value1","key2":"value2","key3":"value3"}

Der Inhalt wäre jetzt application/json(oder zumindest keiner der oben genannten), so dass der PHP- $_POSTWrapper (noch) nicht weiß, wie er damit umgehen soll.

Die Daten sind noch vorhanden, Sie können einfach nicht über den Wrapper darauf zugreifen. Sie müssen es also selbst im Rohformat mit abrufenfile_get_contents('php://input') ( solange es nicht multipart/form-data-codiert ist ).

Auf diese Weise können Sie auch auf XML-Daten oder andere nicht standardmäßige Inhaltstypen zugreifen.

Quasdunk
quelle
40
+1 für "So würden Sie auch auf XML-Daten oder andere nicht standardmäßige Inhaltstypen
zugreifen
@Quasdank Ich sende JSON von der Android-App an den PHP-XAMPP-Server in der Cloud ( stackoverflow.com/questions/36558261/… ), aber ich konnte es nicht zum Laufen bringen, als ich file_get_contents ('php: // input') ausprobierte. was einfach string (0) zurückgibt. Dies funktionierte früher auf meinem lokalen Computer, funktioniert jedoch nicht, wenn ich ihn in der Cloud bereitgestellt habe. Kannst du mir bitte helfen?
The_Martian
1
Es ist erwähnenswert, dass die Verwendung des XMLHttpRequest-Objekts in einer AJAX-Anforderung an PHP nicht bedeutet, dass JSON veröffentlicht werden muss. Es ist ein zusätzlicher Aufwand, aber Ihr clientseitiges JavaScript kann in das Format application / x-www-form-urlencoded konvertiert werden. Die Übersetzung ist jedoch möglicherweise nicht vom Datentyp rein .
Anthony Rutledge
Es muss gesagt werden, dass die Grenze von zwei anerkannten Inhaltstypen weitgehend historisch ist. Nichts hindert PHP daran, dh application/jsonals gültige Datenquelle für das $_POSTArray zu erkennen. Und es gibt sogar veröffentlichte Anfragen für speziell diese Unterstützung.
AnrDaemon
Hallo @quasdunk können Sie mir bitte auf diesem magento.stackexchange.com/questions/296960/…
Nagaraju K
53

php://inputkann Ihnen die Rohbytes der Daten geben. Dies ist nützlich, wenn die POST-Daten eine JSON-codierte Struktur sind, was häufig bei einer AJAX-POST-Anforderung der Fall ist.

Hier ist eine Funktion, um genau das zu tun:

  /**
   * Returns the JSON encoded POST data, if any, as an object.
   * 
   * @return Object|null
   */
  private function retrieveJsonPostData()
  {
    // get the raw POST data
    $rawData = file_get_contents("php://input");

    // this returns null if not valid json
    return json_decode($rawData);
  }

Das $_POSTArray ist nützlicher, wenn Sie Schlüsselwertdaten aus einem Formular verarbeiten, das von einem herkömmlichen POST gesendet wird. Dies funktioniert normalerweise nur, wenn die POST-Daten in einem erkannten Format vorliegen application/x-www-form-urlencoded( Einzelheiten finden Sie unter http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 ).

Rob Agar
quelle
7
Wenn Sie trueals zweiten Parameter an übergeben json_decode, wird ein assoziatives Array zurückgegeben.
Vahid Amiri
28

Wenn die Postdaten fehlerhaft sind, enthält $ _POST nichts. Die Eingabe von php: // enthält jedoch die fehlerhafte Zeichenfolge.

Zum Beispiel gibt es einige Ajax-Anwendungen, die keine korrekte Post-Key-Value-Sequenz zum Hochladen einer Datei bilden und einfach die gesamte Datei als Post-Daten ohne Variablennamen oder ähnliches ausgeben. $ _POST ist leer, $ _FILES ist ebenfalls leer und die Eingabe von php: // enthält eine genaue Datei, die als Zeichenfolge geschrieben ist.

Namenlos
quelle
22

Erstens eine grundlegende Wahrheit über PHP.

PHP wurde nicht entwickelt, um Ihnen explizit eine reine REST-ähnliche Schnittstelle (GET, POST, PUT, PATCH, DELETE) für die Verarbeitung von HTTP-Anforderungen zu geben .

Allerdings ist der $_POST, $_GETund $_FILES superglobals und die Funktion filter_input_array()ist sehr nützlich für die durchschnittliche Person / Laien Bedürfnisse.

Der versteckte Vorteil Nummer eins von $_POST(und $_GET) ist, dass Ihre Eingabedaten von PHP automatisch urldecodiert werden . Sie denken nicht einmal daran, dies tun zu müssen, insbesondere bei Abfragezeichenfolgenparametern innerhalb einer Standard-GET-Anforderung.

Dann lernst du jedoch mehr ...

Wenn Sie jedoch Ihre Programmierkenntnisse erweitern und das JavaScript- XmlHttpRequestObjekt (für einige jQuery) verwenden möchten , werden Sie feststellen, dass dieses Schema Einschränkungen aufweist.

$_POSTbeschränkt Sie auf die Verwendung von zwei Medientypen im HTTP- Content-TypeHeader:

  1. application/x-www-form-urlencoded, und
  2. multipart/form-data

Wenn Sie also Datenwerte an PHP auf dem Server senden und diese im $_POSTSuperglobal anzeigen lassen möchten , müssen Sie sie auf der Clientseite urlencodieren und diese Daten als Schlüssel / Wert-Paare senden - ein unpraktischer Schritt für Anfänger (insbesondere, wenn Sie herausfinden möchten, ob verschiedene Teile der URL unterschiedliche Formen der Urlencodierung erfordern: normal, roh usw.).

Für alle jQuery-Benutzer $.ajax()konvertiert die Methode Ihren JSON in URL-codierte Schlüssel / Wert-Paare, bevor sie an den Server übertragen werden. Sie können dieses Verhalten durch Festlegen überschreiben processData: false. Lesen Sie einfach die Dokumentation zu $ ​​.ajax () und vergessen Sie nicht, den richtigen Medientyp im Content-Type-Header zu senden.

URL-Codierung? Was zum Teufel!!!???

Wenn Sie normale, synchrone (wenn die gesamte Seite neu gezeichnet wird) HTTP-Anforderungen mit einem HTML-Formular ausführen, codiert der Benutzeragent (Webbrowser) normalerweise Ihre Formulardaten für Sie. Wenn Sie mit dem XmlHttpRequestObjekt asynchrone HTTP-Anforderungen ausführen möchten, müssen Sie eine urlencodierte Zeichenfolge erstellen und diese senden. möchten, wenn diese Daten im $_POSTSuperglobal angezeigt werden sollen .

Wie sind Sie mit JavaScript in Kontakt? :-)

Das Konvertieren von einem JavaScript-Array oder -Objekt in eine urlencodierte Zeichenfolge stört viele Entwickler (selbst bei neuen APIs wie Formulardaten ). Sie würden viel lieber nur JSON senden können, und das wäre es auch effizienter für den Client-Code, dies zu tun.

Denken Sie daran (zwinker, zwinker), dass der durchschnittliche Webentwickler nicht lernt, das zu verwenden XmlHttpRequest Objekt, globale Funktionen, Zeichenfolgenfunktionen, Arrayfunktionen und reguläre Ausdrücke wie Sie und ich direkt ;-). Urlencodierung ist für sie ein Albtraum. ;-);

PHP, was gibt es?

Das Fehlen einer intuitiven XML- und JSON-Verarbeitung in PHP macht viele Leute aus. Sie würden denken, dass es jetzt Teil von PHP sein würde (seufz).

So viele Medientypen (MIME-Typen in der Vergangenheit)

XML, JSON und YAML verfügen alle über Medientypen, die in einen HTTP- Content-TypeHeader eingefügt werden können.

  • application / xml
  • Anwendung / json
  • Anwendung / Yaml (obwohl IANA keine offizielle Bezeichnung aufgeführt hat)

Schauen Sie, wie viele Medientypen (früher MIME-Typen) von IANA definiert wurden.

Schauen Sie, wie viele HTTP-Header es gibt.

php: // Eingabe oder Pleite

Verwendung der php://input Stream können Sie die Abstraktionsstufe für Babysitting / Handhaltung umgehen, die PHP der Welt aufgezwungen hat. :-) Mit großer Macht kommt große Verantwortung!

Bevor Sie sich mit Datenwerten befassen, die durch sie gestreamt werden php://input, sollten / müssen Sie einige Dinge tun.

  1. Stellen Sie fest, ob die richtige HTTP-Methode angegeben wurde (GET, POST, PUT, PATCH, DELETE, ...).
  2. Stellen Sie fest, ob der HTTP- Content-Type- Header übertragen wurde.
  3. Stellen Sie fest, ob der Wert für den Inhaltstyp der gewünschte Medientyp ist.
  4. Stellen Sie fest, ob die gesendeten Daten wohlgeformt sind. XML / JSON / YMAL / etc.
  5. Konvertieren Sie die Daten bei Bedarf in einen PHP-Datentyp: Array oder Objekt.
  6. Wenn eine dieser grundlegenden Überprüfungen oder Konvertierungen fehlschlägt, lösen Sie eine Ausnahme aus !

Was ist mit der Zeichenkodierung?

AH, HA! Ja, Sie möchten möglicherweise, dass der an Ihre Anwendung gesendete Datenstrom UTF-8-codiert wird. Wie können Sie jedoch feststellen, ob dies der Fall ist oder nicht?

Zwei kritische Probleme.

  1. Sie wissen nicht, wie viele Daten eingehen php://input.
  2. Sie kennen die aktuelle Codierung des Datenstroms nicht mit Sicherheit.

Versuchen Sie, Stream-Daten zu verarbeiten, ohne zu wissen, wie viel zuerst vorhanden ist? Das ist eine schreckliche Idee . Sie können sich nicht ausschließlich auf den HTTP- Content-LengthHeader verlassen, um eine Anleitung zur Größe der gestreamten Eingabe zu erhalten, da diese gefälscht werden kann.

Sie benötigen eine:

  1. Algorithmus zur Erkennung der Stream-Größe.
  2. Anwendungsdefinierte Stream-Größenbeschränkungen (Apache / Nginx / PHP-Beschränkungen sind möglicherweise zu weit gefasst).

Versuchen Sie, Stream-Daten in UTF-8 zu konvertieren, ohne die aktuelle Codierung des Streams zu kennen? Wie? Der iconv-Stream-Filter ( Beispiel für einen iconv-Stream-Filter ) scheint eine Start- und Endcodierung wie diese zu wollen.

'convert.iconv.ISO-8859-1/UTF-8'

Wenn Sie gewissenhaft sind, benötigen Sie:

  1. Algorithmus zur Erkennung der Stream-Codierung.
  2. Algorithmusdefinitionsalgorithmus für Dynamic / Runtime-Streams (da Sie die Startcodierung nicht a priori kennen).

( Update : 'convert.iconv.UTF-8/UTF-8'Erzwingt alles auf UTF-8, aber Sie müssen immer noch Zeichen berücksichtigen, die die iconv-Bibliothek möglicherweise nicht übersetzen kann. Mit anderen Worten, Sie müssen festlegen, welche Aktion ausgeführt werden soll, wenn ein Zeichen nicht übersetzt werden kann : 1) Fügen Sie ein Dummy-Zeichen ein, 2) Fail / Throw und Exception).

Sie können sich nicht ausschließlich auf den HTTP- Content-EncodingHeader verlassen, da dies möglicherweise auf eine Komprimierung wie im Folgenden hinweist. Dies ist nicht das, woraus Sie eine Entscheidung in Bezug auf iconv treffen möchten.

Content-Encoding: gzip

Daher könnten die allgemeinen Schritte sein ...

Teil I: HTTP-Anfrage im Zusammenhang

  1. Stellen Sie fest, ob die richtige HTTP-Methode angegeben wurde (GET, POST, PUT, PATCH, DELETE, ...).
  2. Stellen Sie fest, ob der HTTP- Content-Type- Header übertragen wurde.
  3. Stellen Sie fest, ob der Wert für den Inhaltstyp der gewünschte Medientyp ist.

Teil II: Stream-Daten

  1. Bestimmen Sie die Größe des Eingabestreams (optional, aber empfohlen).
  2. Bestimmen Sie die Codierung des Eingabestreams.
  3. Konvertieren Sie gegebenenfalls den Eingabestream in die gewünschte Zeichenkodierung (UTF-8).
  4. Kehren Sie gegebenenfalls die Komprimierung oder Verschlüsselung auf Anwendungsebene um und wiederholen Sie die Schritte 4, 5 und 6.

Teil III: Datentypbezogen

  1. Stellen Sie fest, ob die gesendeten Daten wohlgeformt sind. XML / JSON / YMAL / etc.

(Denken Sie daran, dass die Daten immer noch eine URL-codierte Zeichenfolge sein können, die Sie dann analysieren und URL-dekodieren müssen.)

  1. Konvertieren Sie die Daten bei Bedarf in einen PHP-Datentyp: Array oder Objekt.

Teil IV: Datenwertbezogen

  1. Eingabedaten filtern.

  2. Eingabedaten validieren.

Verstehst du jetzt?

Das $_POSTSuperglobale ist zusammen mit den php.ini-Einstellungen für Eingabegrenzen für den Laien einfacher. Der Umgang mit der Zeichenkodierung ist jedoch bei der Verwendung von Streams viel intuitiver und effizienter, da keine Superglobalen (oder Arrays im Allgemeinen) durchlaufen werden müssen, um die Eingabewerte auf die richtige Kodierung zu überprüfen.

Anthony Rutledge
quelle
1
Oh wow! Diese Antwort sollte eine viel höhere Bewertung haben. Vielen Dank, dass Sie das Flutlicht in die Dunkelheit bringen.
Lox
Letztendlich würde PHP gut daran tun, die Basisstandards zu aktualisieren. Es ist jedoch ein logischer Irrtum, eine HTTP-Anforderungsmethode mit einer gleichnamigen Datenstruktur ($ _GET, $ _POST) zu koppeln. Was zählt, ist (1) die gewünschte HTTP-Anforderungsmethode und (2) Anforderungsdaten mit dieser Anforderung (Inhaltstyp). Daher müssen Sie wie bei Perl sehen, dass Sie das willige Opfer der Meinungen von Sprachschöpfern / -pflegern sein können.
Anthony Rutledge
0

Also habe ich eine Funktion geschrieben, die die POST-Daten aus dem php: // Eingabestream abruft .

Die Herausforderung bestand darin, auf die Anforderungsmethode PUT, DELETE OR PATCH umzuschalten und trotzdem die Post-Daten zu erhalten, die mit dieser Anforderung gesendet wurden.

Ich teile dies vielleicht für jemanden mit einer ähnlichen Herausforderung. Die folgende Funktion ist das, was ich mir ausgedacht habe und sie funktioniert. Ich hoffe, es hilft!

    /**
     * @method Post getPostData
     * @return array
     * 
     * Convert Content-Disposition to a post data
     */
    function getPostData() : array
    {
        // @var string $input
        $input = file_get_contents('php://input');

        // continue if $_POST is empty
        if (strlen($input) > 0 && count($_POST) == 0 || count($_POST) > 0) :

            $postsize = "---".sha1(strlen($input))."---";

            preg_match_all('/([-]{2,})([^\s]+)[\n|\s]{0,}/', $input, $match);

            // update input
            if (count($match) > 0) $input = preg_replace('/([-]{2,})([^\s]+)[\n|\s]{0,}/', '', $input);

            // extract the content-disposition
            preg_match_all("/(Content-Disposition: form-data; name=)+(.*)/m", $input, $matches);

            // let's get the keys
            if (count($matches) > 0 && count($matches[0]) > 0)
            {
                $keys = $matches[2];

                foreach ($keys as $index => $key) :
                    $key = trim($key);
                    $key = preg_replace('/^["]/','',$key);
                    $key = preg_replace('/["]$/','',$key);
                    $key = preg_replace('/[\s]/','',$key);
                    $keys[$index] = $key;
                endforeach;

                $input = preg_replace("/(Content-Disposition: form-data; name=)+(.*)/m", $postsize, $input);

                $input = preg_replace("/(Content-Length: )+([^\n]+)/im", '', $input);

                // now let's get key value
                $inputArr = explode($postsize, $input);

                // @var array $values
                $values = [];

                foreach ($inputArr as $index => $val) :
                    $val = preg_replace('/[\n]/','',$val);

                    if (preg_match('/[\S]/', $val)) $values[$index] = trim($val);

                endforeach;

                // now combine the key to the values
                $post = [];

                // @var array $value
                $value = [];

                // update value
                foreach ($values as $i => $val) $value[] = $val;

                // push to post
                foreach ($keys as $x => $key) $post[$key] = isset($value[$x]) ? $value[$x] : '';

                if (is_array($post)) :

                    $newPost = [];

                    foreach ($post as $key => $val) :

                        if (preg_match('/[\[]/', $key)) :

                            $k = substr($key, 0, strpos($key, '['));
                            $child = substr($key, strpos($key, '['));
                            $child = preg_replace('/[\[|\]]/','', $child);
                            $newPost[$k][$child] = $val;

                        else:

                            $newPost[$key] = $val;

                        endif;

                    endforeach;

                    $_POST = count($newPost) > 0 ? $newPost : $post;

                endif;
            }

        endif;

        // return post array
        return $_POST;
    }
Ifeanyi Amadi
quelle
-5

Einfaches Beispiel für die Verwendung

 <?php  
     if(!isset($_POST) || empty($_POST)) { 
     ?> 
        <form name="form1" method="post" action=""> 
          <input type="text" name="textfield"><br /> 
          <input type="submit" name="Submit" value="submit"> 
        </form> 
   <?php  
        } else { 
        $example = file_get_contents("php://input");
        echo $example;  }  
   ?>
Dostonbek Oripjonov
quelle