PHP "php: / / input" vs $ POST

Zostałem poproszony o użycie metody php://input zamiast $_POST podczas interakcji z żądaniami Ajax z JQuery. Nie rozumiem jednak korzyści płynących z zastosowania tej metody w stosunku do metody globalnej $_POST lub $_GET.

 268
Author: Eric Leschinski, 2012-01-17

6 answers

Powodem jest to, że php://input zwraca wszystkie surowe dane po nagłówkach HTTP żądania, niezależnie od typu zawartości.

Superglobal PHP $_POST, tylko mA zawijać dane, które są albo

  • application/x-www-form-urlencoded (standardowy typ treści dla prostych formularzy-postów) lub
  • multipart/form-data (najczęściej używany do przesyłania plików)

To dlatego, że są to jedyne typy treści, które musi być wspierane przez agentów Użytkownika . Więc serwer A PHP tradycyjnie nie spodziewa się otrzymywać żadnych innych typów treści (co nie znaczy, że nie mogą).

Więc, jeśli po prostu zamieścisz stary dobry HTML form, żądanie wygląda mniej więcej tak:

POST /page.php HTTP/1.1

key1=value1&key2=value2&key3=value3

Ale jeśli dużo pracujesz z Ajaxem, ta probaby obejmuje również wymianę bardziej złożonych danych z typów (string, int, bool) i struktur (tablice, obiekty), więc w większości przypadków JSON jest najlepszym wyborem. Ale Prośba o JSON-payload wyglądałaby jak to:

POST /page.php HTTP/1.1

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

Zawartość będzie teraz application/json (a przynajmniej żadna z wyżej wymienionych), więc PHP $_POST - wrapper nie wie, jak sobie z tym poradzić (jeszcze).

Dane wciąż tam są, po prostu nie możesz uzyskać do nich dostępu przez owijarkę. Więc musisz pobrać go samodzielnie w formacie raw z file_get_contents('php://input') (dopóki nie jest multipart/form-data-zakodowane).

W ten sposób można uzyskać dostęp do danych XML lub innych niestandardowych typów treści.

 526
Author: Quasdunk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-19 02:52:34

php://input może podać surowe bajty danych. Jest to przydatne, jeśli opublikowane dane są zakodowaną strukturą JSON, co często ma miejsce w przypadku żądania AJAX POST.

Oto funkcja do tego:

  /**
   * 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);
  }

Tablica $_POST jest bardziej przydatna, gdy obsługujesz dane o wartości klucza z formularza przesłanego tradycyjnym postem. Działa to tylko wtedy, gdy opublikowane dane są w uznanym formacie, zwykle application/x-www-form-urlencoded (Zobacz http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 dla szczegóły).

 55
Author: Rob Agar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-17 11:36:56

Po pierwsze, podstawowa prawda o PHP.

PHP nie zostało zaprojektowane tak, aby bezpośrednio dawało czysty REST (GET, POST, PUT, PATCH, DELETE) jak interfejs do obsługi żądań HTTP.

Jednak $_SERVER, $_COOKIE, $_POST, $_GET, oraz $_FILES superglobali , a funkcja filter_input_array() są bardzo przydatne dla potrzeb przeciętnego człowieka / laika.

Pierwszą ukrytą zaletą $_POST (i $_GET) jest to, że Twoje dane wejściowe są dekodowane url automatycznie przez PHP . Nigdy nawet nie myślisz o konieczności tego robić, szczególnie dla parametrów ciągu zapytania w ramach standardowego żądania GET lub danych ciała HTTP przesyłanych za pomocą żądania POST.

Inne metody żądania HTTP

Ci, którzy badają Podstawowy protokół HTTP i jego różne metody żądań, rozumieją, że istnieje wiele metod żądań HTTP, w tym często przywoływane PUT, PATCH (nie jest używany w Apigee Google), oraz DELETE.

W PHP, tam nie są żadnymi superglobalami ani funkcjami filtra wejściowego do uzyskiwania danych ciała żądania HTTP, gdy POST nie jest używana. Co robią uczniowie Roya Fieldinga? ;-)

Jednak wtedy dowiesz się więcej ...

Biorąc to pod uwagę, gdy rozwijasz swoją wiedzę programistyczną PHP i chcesz używać obiektu XmlHttpRequest JavaScript (dla niektórych jQuery), widzisz ograniczenie tego schematu.

$_POST Ograniczenie do korzystania z dwóch typów mediów w nagłówku HTTP Content-Type:

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

Tak więc, jeśli chcesz wysłać wartości danych do PHP na serwerze i pokazać je w $_POST superglobal , to musisz urlencode to po stronie klienta i wysłać te dane jako pary klucz/wartość--niewygodny krok dla początkujących (zwłaszcza gdy próbujesz dowiedzieć się, czy różne części URL wymagają różnych form urlencodingu: normal, raw, itp..).

dla wszystkich użytkowników jQuery metoda $.ajax() konwertuje Twoje JSON na URL zakodowane pary klucz / wartość przed przesłaniem ich do serwera. Można nadpisać to zachowanie, ustawiając processData: false. Po prostu przeczytaj $.dokumentacja ajax () , i nie zapomnij wysłać poprawnego typu nośnika w nagłówku Content-Type.

Php: / / input, ale ...

Nawet jeśli użyjesz php://input zamiast $_POST do żądania HTTP POST, to nie będzie działać z HTTP Content-Type z multipart/form-data jest to typ zawartości, którego używasz w HTML formularz, gdy chcesz zezwolić na przesyłanie plików!

<form enctype="multipart/form-data" accept-charset="utf-8" action="post">
    <input type="file" name="resume">
</form>

Dlatego w tradycyjnym PHP, aby poradzić sobie z różnorodnością typów treści z żądania HTTP POST, nauczysz się używać $_POST lub filter_input_array(POST), $_FILES, i php://input. Nie ma sposobu na użycie jednego, uniwersalnego źródła wejściowego dla żądań HTTP POST W PHP.

Nie można pobrać plików przez $_POST, filter_input_array(POST) lub php://input i nie można pobrać JSON/XML/YAML ani w filter_input_array(POST) ani $_POST.

Instrukcja PHP: php: / / input

Php: / / input jest strumieniem tylko do odczytu, który pozwala na odczyt surowych danych z ciała żądania ...php: / / input is not available with enctype= "multipart / form-data".

Frameworki PHP na ratunek?

Frameworki PHP, takie jak Codeigniter 4 i Laravel, wykorzystują fasadę do zapewnienia czystszego interfejsu (IncomingRequest lub Request) do powyższego. Dlatego profesjonalni Programiści PHP używają frameworków zamiast surowych PHP.

Oczywiście, jeśli lubisz programować, możesz opracować własny obiekt fasady, aby zapewnić to, co robią frameworki. To dlatego, że poświęciłem czas na zbadanie tej kwestii, jestem w stanie napisać tę odpowiedź.

Kodowanie URL? Co do cholery!!!???

Zazwyczaj, jeśli wykonujesz normalne, synchroniczne (gdy cała strona przerysowuje) żądania HTTP za pomocą formularza HTML, user-agent (przeglądarka internetowa)koduje Twoje dane formularza. Jeśli chcesz wykonać asynchroniczny HTTP żądania za pomocą obiektu XmlHttpRequest, wtedy należy zmodyfikować łańcuch urlencoded i wysłać go, jeśli chcesz, aby te dane pojawiły się w $_POST superglobalu .

Jak masz kontakt z JavaScript? :-)

Konwersja z tablicy lub obiektu JavaScript na urlencoded string przeszkadza wielu programistom (nawet z nowymi API, takimi jak Form Data). Znacznie woleliby po prostu być w stanie wysłać JSON, i byłoby to bardziej efektywne dla kodu klienta, aby zrobić więc.

Pamiętaj (wink, wink), Przeciętny web developer nie uczy się używać obiektu XmlHttpRequest bezpośrednio, funkcji globalnych, funkcji łańcuchowych, funkcji tablicowych i wyrażeń regularnych, takich jak ty i ja ;-). Urlencoding dla nich to koszmar. ;-)

PHP, co jest?

Brak intuicyjnej obsługi XML i JSON w PHP wyłącza Wiele osób. Można by pomyśleć, że będzie to już część PHP (westchnienie).

Tak wiele typów mediów (typy MIME w przeszłości)]}

XML, JSON, wszystkie YAML mają typy nośników, które można umieścić w nagłówku HTTP Content-Type.

  • application / xml
  • applicaiton / json
  • application / yaml (chociaż IANA nie ma oficjalnego oznaczenia na liście)

Zobacz ile typów mediów (dawniej typy MIME) są zdefiniowane przez IANA.

Zobacz ile nagłówków HTTP jest.

Php: / / input or bust

Za pomocą php://input stream pozwala obejść baby-sitting / hand holding poziom abstrakcji, który PHP wymusił na świecie. :- ) Z wielką mocą przychodzi wielka odpowiedzialność!

Teraz, zanim uporasz się z wartościami danych przesyłanymi strumieniowo przez php://input, powinieneś / musisz zrobić kilka rzeczy.

  1. określić, czy została wskazana prawidłowa metoda HTTP (GET, POST, PUT, PATCH, DELETE, ...)
  2. określa, czy nagłówek HTTP Content-Type został przesłany.
  3. określić, czy wartość Dla Content-Type jest pożądanym nośnikiem Typ.
  4. Określ, czy przesyłane dane są dobrze uformowane XML / JSON / YAML / itd.
  5. jeśli to konieczne, przekonwertować dane do PHP datatype: array lub obiekt.
  6. jeśli którekolwiek z tych podstawowych sprawdzeń lub konwersji nie powiedzie się, wyrzuć wyjątek!

A co z kodowaniem znaków?

AH, HA! Tak, możesz chcieć, aby strumień danych przesyłany do Twojej aplikacji był UTF-8 zakodowany, ale skąd możesz wiedzieć, czy jest, czy nie?

Dwa krytyczne problemy.

  1. nie wiesz, ile Danych przechodzi php://input.
  2. nie znasz na pewno bieżącego kodowania strumienia danych.

Czy spróbujesz obsłużyć dane strumieniowe, nie wiedząc najpierw, ile ich jest? To okropny pomysł. Nie można polegać wyłącznie na nagłówku HTTP Content-Length, aby uzyskać wskazówki dotyczące rozmiaru strumieniowanego wejścia, ponieważ może być sfałszowany.

Będziesz potrzebował:

  1. algorytm wykrywania rozmiaru strumienia.
  2. limity rozmiaru strumienia zdefiniowane przez aplikację(limity Apache / Nginx / PHP mogą być zbyt szerokie).

Czy będziesz próbował przekonwertować dane strumienia do UTF-8 bez znajomości bieżącego kodowania strumienia? Jak? Iconv Stream filter (iconv stream Filter example) wydaje się chcieć kodowania początkowego i końcowego, jak to.

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

Tak więc, jeśli jesteś sumienny, będziesz potrzebował:

  1. algorytm wykrywania kodowania strumienia.
  2. Dynamic / Runtime stream Filter definition algorithm(ponieważ nie możesz znać początkowego kodowania a priori).

(aktualizacja: 'convert.iconv.UTF-8/UTF-8' wymusi wszystko na UTF-8, ale nadal musisz uwzględnić znaki, których biblioteka iconv może nie wiedzieć, jak przetłumaczyć. Innymi słowy, musisz trochę określić, jakie działania podjąć, gdy postać nie może być przetłumaczona: 1) Insert a dummy character, 2) Fail / throw and exception).

Nie można polegać wyłącznie na nagłówku HTTP Content-Encoding, ponieważ może to wskazywać na coś takiego jak kompresja, jak poniżej. To nie jest to, co chcesz podjąć decyzję off w odniesieniu do iconv.

Content-Encoding: gzip

W związku z tym, ogólne kroki mogą być ...

Część i: HTTP Request Related

  1. określić, czy została wskazana prawidłowa metoda HTTP (GET, POST, PUT, PATCH, DELETE, ...)
  2. określa, czy nagłówek HTTP Content-Type został przesłany.
  3. Określ, czy wartość Dla Content-Type jest pożądanym nośnikiem Typ.

Część II: Stream Data Related

  1. określa rozmiar strumienia wejściowego (opcjonalne, ale zalecane).
  2. określa kodowanie strumienia wejściowego.
  3. w razie potrzeby przekonwertuj strumień wejściowy na żądany znak kodowanie (UTF-8).
  4. w razie potrzeby Odwróć kompresję lub szyfrowanie na poziomie aplikacji, a następnie powtórz kroki 4, 5 i 6.

Część III: typ danych

  1. Określ, czy przesyłane dane są dobrze uformowane XML / JSON / YMAL / itd.

(pamiętaj, że dane mogą być zakodowanym ciągiem URL, który musisz następnie przeanalizować i dekodować URL).

  1. jeśli to konieczne, przekonwertuj dane do PHP datatype: array lub obiekt.

Część IV: data Value Related

  1. Filtruj dane wejściowe.

  2. Zweryfikuj dane wejściowe.

Teraz widzisz?

Superglobal, wraz z php.ustawienia ini dla limitów na wejściu są prostsze dla laika. Jednak radzenie sobie z kodowaniem znaków jest znacznie bardziej intuicyjne i wydajne podczas korzystania ze strumieni, ponieważ nie ma potrzeby pętli przez superglobale (lub tablice, ogólnie), aby sprawdź wartości wejściowe dla prawidłowego kodowania.

 46
Author: Anthony Rutledge,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-02-12 04:33:13

Jeśli dane post są zniekształcone, $_POST nie będzie niczego zawierać. Jednak, PHP: / / input będzie miał zniekształcony ciąg znaków.

Na przykład istnieją aplikacje ajax, które nie tworzą poprawnej sekwencji wartości klucza post do przesłania pliku i po prostu zrzucają cały plik jako dane post, bez nazw zmiennych lub czegokolwiek. $_POST będzie pusty, $_FILES również pusty, a PHP: / / input będzie zawierał dokładny plik zapisany jako łańcuch znaków.

 29
Author: Nameless,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-17 11:22:24

Napisałem więc funkcję, która pobierałaby dane posta ze strumienia php: / / input stream .

Więc wyzwaniem tutaj było przejście na metodę PUT, DELETE lub PATCH request I nadal uzyskać dane post, które zostały wysłane z tym żądaniem.

Dzielę się tym może dla kogoś z podobnym wyzwaniem. Funkcja poniżej jest tym, co wymyśliłem i działa. Mam nadzieję, że to pomoże!
    /**
     * @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;
    }
 0
Author: Ifeanyi Amadi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-15 18:02:41

Prosty przykład użycia

 <?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;  }  
   ?>
 -5
Author: Dostonbek Oripjonov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-20 05:13:36