PHP „php: // input” vs $ _POST

243

Polecono mi użyć tej metody php://inputzamiast $_POSTpodczas interakcji z żądaniami Ajax z JQuery. To, czego nie rozumiem, to korzyści wynikające z zastosowania tego w porównaniu z globalną metodą $_POSTlub $_GET.

Zawietrzny
źródło
2
Używałem „hacków” do odbierania wywołań ajax po stronie PHP, zanim natknąłem się na ten post i przeczytałem niesamowite odpowiedzi! Jeśli chodzi o inne osoby mające ten sam problem w przyszłości, mam nadzieję, że wyszukiwarki również przeczytają mój komentarz! :)
aderchox

Odpowiedzi:

484

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

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

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

Jest tak, ponieważ są to jedyne typy treści, które muszą być obsługiwane przez programy klienckie . Tak więc serwer i PHP tradycyjnie nie oczekują żadnego innego rodzaju treści (co nie znaczy, że nie mogą).

Jeśli więc POST umieś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 typami (ciąg, int, bool) i strukturami (tablice, obiekty), więc w większości przypadków JSON jest najlepszym wyborem. Ale żądanie zawierające ładunek JSON wyglądałoby mniej więcej tak:

POST /page.php HTTP/1.1

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

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

Dane nadal tam są, po prostu nie można uzyskać do nich dostępu za pomocą opakowania. Więc musisz pobrać go sam w surowym formacie file_get_contents('php://input')( o ile nie jest multipart/form-datazakodowany ).

W ten sposób uzyskasz dostęp do danych XML lub dowolnego innego niestandardowego typu treści.

Quasdunk
źródło
40
+1 dla „W ten sposób uzyskasz dostęp do danych XML lub dowolnego innego niestandardowego typu zawartości”
mandza 10.10.2014
@Quasdank Wysyłam JSON z aplikacji na Androida do serwera php xampp w chmurze ( stackoverflow.com/questions/36558261/... ), ale nie mogłem go uruchomić, gdy próbowałem file_get_contents ('php: // input'), który po prostu zwraca ciąg (0). To kiedyś działało na moim komputerze lokalnym, ale nie działa, gdy wdrożyłem go do chmury. Proszę, mógłbyś mi pomóc?
The_Martian
1
Warto zauważyć, że użycie obiektu XMLHttpRequest w żądaniu AJAX do PHP nie oznacza, że ​​należy opublikować JSON. Jest to dodatkowy narzut, ale JavaScript po stronie klienta można przekonwertować na format application / x-www-form-urlencoded. Tłumaczenie może jednak nie być czystym typem danych .
Anthony Rutledge
Trzeba powiedzieć, że granica dwóch uznanych typów treści jest w dużej mierze historyczna. Nic nie powstrzymuje PHP przed rozpoznaniem np. application/jsonJako prawidłowego źródła danych dla $_POSTtablicy. Są nawet opublikowane prośby o wsparcie.
AnrDaemon,
Cześć @quasdunk, czy możesz mi pomóc w tej magento.stackexchange.com/questions/296960/…
Nagaraju K
53

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

Oto funkcja, aby to zrobić:

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

$_POSTTablica jest bardziej użyteczny, gdy jesteś obsługi danych klucz-wartość z formularza, przedłożony przez tradycyjnego testu POST. Działa to tylko wtedy, gdy dane POSTed są zwykle w rozpoznawalnym formacie application/x-www-form-urlencoded( szczegółowe informacje można znaleźć na stronie http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 ).

Rob Agar
źródło
7
Warto zauważyć, że jeśli podasz truejako drugi parametr json_decode, zwróci tablicę asocjacyjną.
Vahid Amiri,
28

Jeśli dane postu są zniekształcone, $ _POST nic nie będzie zawierało. Jednak wejście php: // będzie zawierało źle sformułowany ciąg.

Na przykład jest kilka aplikacji ajax, które nie tworzą prawidłowej sekwencji klucz-wartość dla przesłania pliku, i po prostu zrzucają cały plik jako dane postu, bez nazw zmiennych lub czegokolwiek. $ _POST będzie pusty, $ _FILES również pusty, a wejście php: // będzie zawierało dokładny plik zapisany jako ciąg znaków.

Bezimienny
źródło
22

Po pierwsze, podstawowa prawda o PHP.

PHP nie zostało zaprojektowane tak, aby jawnie dać ci czysty interfejs REST (GET, POST, PUT, PATCH, DELETE) do obsługi żądań HTTP .

Jednakże $_POST, $_GETi $_FILES Superglobale i funkcja filter_input_array()są bardzo przydatne dla potrzeb przeciętnego człowieka / laika.

Główną ukrytą zaletą $_POST(i $_GET) jest to, że dane wejściowe są automatycznie kodowane automatycznie przez PHP . Nigdy nawet nie zastanawiasz się nad koniecznością zrobienia tego, szczególnie w przypadku parametrów ciągu zapytania w ramach standardowego żądania GET.

Jednak wtedy uczysz się więcej ...

Biorąc to pod uwagę, w miarę pogłębiania wiedzy programistycznej i chęci korzystania z XmlHttpRequestobiektu JavaScript (w niektórych przypadkach jQuery), pojawia się ograniczenie tego schematu.

$_POSTogranicza użycie dwóch typów mediów w Content-Typenagłówku HTTP :

  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 $_POSTsuperglobalu , musisz urlencode je po stronie klienta i wysłać te dane jako pary klucz / wartość - niewygodny krok dla nowicjuszy (szczególnie gdy próbujesz dowiedzieć się, czy różne części adresu URL wymagają różnych form urlencoding: normalny, surowy itp.).

Dla wszystkich użytkowników jQuery $.ajax()metoda polega na konwersji JSON na pary klucz / wartość zakodowane w adresie URL przed przesłaniem ich na serwer. Możesz zmienić to zachowanie, ustawiając processData: false. Po prostu przeczytaj dokumentację $ .ajax () i nie zapomnij wysłać poprawnego typu nośnika w nagłówku Content-Type.

Kodowanie URL? Co za cholera!!!???

Zazwyczaj, jeśli wykonujesz normalne, synchroniczne (gdy cała strona przerysowuje) żądania HTTP z formularzem HTML, agent użytkownika (przeglądarka internetowa) urlencode dla ciebie dane formularza. Jeśli chcesz wykonać asynchroniczne żądania HTTP przy użyciu XmlHttpRequestobiektu, musisz zaprojektować ciąg znaków w postaci urlencod i wysłać go, jeśli chcesz, aby te dane $_POSTpojawiały się w superglobalu .

Jak się masz w JavaScript? :-)

Konwersja z tablicy lub obiektu JavaScript na ciąg znaków zakodowany w trybie ciągłym przeszkadza wielu programistom (nawet w przypadku nowych interfejsów API, takich jak Form Data ). Woleliby po prostu wysłać JSON, a kod klienta byłby bardziej wydajny .

Pamiętaj (mrugnij, mrugnij), przeciętny twórca stron internetowych nie uczy się korzystać z XmlHttpRequestobiektu bezpośrednio, funkcji globalnych, funkcji łańcuchowych, funkcji tablicowych i wyrażeń regularnych takich jak ty i ja ;-). Urlencoding to dla nich koszmar. ;-)

PHP, co daje?

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

Tak wiele rodzajów mediów (typy MIME w przeszłości)

XML, JSON i YAML mają typy multimediów, które można umieścić w Content-Typenagłówku HTTP .

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

Sprawdź, ile typów mediów (wcześniej typów MIME) jest zdefiniowanych przez IANA.

Zobacz, ile jest nagłówków HTTP .

php: // input lub biust

Korzystanie ze php://inputstrumienia pozwala ominąć poziom abstrakcji dziecka siedzącego / trzymającego dłoń narzucony światu przez PHP. :-) Z dużą mocą przychodzi duża odpowiedzialność!

Teraz, zanim poradzisz sobie z wartościami danych przesyłanymi strumieniowo php://input, powinieneś / musisz zrobić kilka rzeczy.

  1. Sprawdź, czy wskazano prawidłową metodę HTTP (GET, POST, PUT, PATCH, DELETE, ...)
  2. Sprawdź, czy nagłówek HTTP Content-Type został przesłany.
  3. Sprawdź, czy wartość typu zawartości jest pożądanym typem nośnika.
  4. Sprawdź, czy przesyłane dane są poprawnie sformatowane XML / JSON / YMAL / itp.
  5. W razie potrzeby przekonwertuj dane na typ danych PHP: tablica lub obiekt.
  6. Jeśli którakolwiek z tych podstawowych kontroli lub konwersji zakończy się niepowodzeniem, rzuć wyjątek !

Co z kodowaniem znaków?

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

Dwa krytyczne problemy.

  1. Nie wiesz, ile danych przechodzi php://input.
  2. Nie wiesz na pewno obecnego kodowania strumienia danych.

Czy spróbujesz obsłużyć dane strumieniowe, nie wiedząc, ile najpierw? To okropny pomysł . Nie można polegać wyłącznie na Content-Lengthnagłówku HTTP w celu uzyskania wskazówek na temat wielkości przesyłanych strumieniowo danych wejściowych, ponieważ mogą one zostać sfałszowane.

Będziesz potrzebować:

  1. Algorytm wykrywania wielkości strumienia.
  2. Limity wielkości strumienia zdefiniowane przez aplikację (limity Apache / Nginx / PHP mogą być zbyt szerokie).

Czy spróbujesz przekonwertować dane strumienia na UTF-8, nie znając aktualnego kodowania strumienia? W jaki sposób? Filtr strumienia iconv ( przykład filtra strumienia iconv ) wydaje się potrzebować początkowego i końcowego kodowania, takiego jak ten.

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

Zatem jeśli jesteś sumienny, będziesz potrzebować:

  1. Algorytm wykrywania kodowania strumieniowego.
  2. Algorytm definicji filtra strumienia dynamicznego / środowiska wykonawczego (ponieważ nie można poznać początkowego kodowania a priori).

( Aktualizacja : 'convert.iconv.UTF-8/UTF-8'zmusi wszystko do UTF-8, ale nadal musisz wziąć pod uwagę znaki, których biblioteka iconv może nie wiedzieć, jak tłumaczyć. : 1) Wstaw fikcyjną postać, 2) Niepowodzenie / rzut i wyjątek).

Nie można polegać wyłącznie na Content-Encodingnagłówku HTTP , ponieważ może to wskazywać na kompresję, jak poniżej. To nie jest to, z czego chcesz podjąć decyzję w odniesieniu do iconv.

Content-Encoding: gzip

Dlatego ogólne kroki mogą być ...

Część I: Powiązane żądanie HTTP

  1. Sprawdź, czy wskazano prawidłową metodę HTTP (GET, POST, PUT, PATCH, DELETE, ...)
  2. Sprawdź, czy nagłówek HTTP Content-Type został przesłany.
  3. Sprawdź, czy wartość typu zawartości jest pożądanym typem nośnika.

Część II: Przesyłanie danych powiązanych

  1. Określ rozmiar strumienia wejściowego (opcjonalny, ale zalecany).
  2. Określ kodowanie strumienia wejściowego.
  3. W razie potrzeby przekonwertuj strumień wejściowy na żądane kodowanie znaków (UTF-8).
  4. W razie potrzeby cofnij kompresję lub szyfrowanie na poziomie aplikacji, a następnie powtórz kroki 4, 5 i 6.

Część III: Powiązane typy danych

  1. Sprawdź, czy przesyłane dane są poprawnie sformatowane XML / JSON / YMAL / itp.

(Pamiętaj, że dane mogą nadal być ciągiem zakodowanym w adresie URL, który musisz następnie przeanalizować i zdekodować adres URL).

  1. W razie potrzeby przekonwertuj dane na typ danych PHP: tablica lub obiekt.

Część IV: Związane z wartością danych

  1. Filtruj dane wejściowe.

  2. Sprawdź poprawność danych wejściowych.

Teraz widzisz

$_POSTSuperglobalną wraz z php.ini ustawienia limitów na wejściu, są prostsze dla laika. Jednak obsługa kodowania znaków jest znacznie bardziej intuicyjna i wydajna podczas korzystania ze strumieni, ponieważ nie ma potrzeby przechodzenia przez superglobale (lub tablice, ogólnie), aby sprawdzić wartości wejściowe dla prawidłowego kodowania.

Anthony Rutledge
źródło
1
Och wow! Ta odpowiedź powinna mieć znacznie wyższą ocenę. Dziękuję bardzo za doprowadzenie światła powodzi do ciemności.
Lox
W końcowej analizie PHP dobrze by zaktualizowało podstawowe ustawienia domyślne. Logicznym błędem jest jednak połączenie metody żądania HTTP ze strukturą danych o tej samej nazwie ($ _GET, $ _POST). Liczy się (1) żądana metoda żądania HTTP i (2) czy istnieją dane żądania z tym żądaniem (Content-Type). Dlatego, podobnie jak w przypadku Perla, musisz zobaczyć, że możesz być chętną ofiarą opinii twórców / opiekunów języka.
Anthony Rutledge
0

Napisałem więc funkcję, która pobierałaby dane POST z strumienia wejściowego php: // .

Tak więc wyzwaniem było przejście na metodę żądania PUT, DELETE LUB PATCH i nadal uzyskiwanie danych pocztowych wysłanych z tym żądaniem.

Dzielę się tym może dla kogoś z podobnym wyzwaniem. Oto funkcja, którą 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;
    }
Ifeanyi Amadi
źródło
-5

Prosty przykład tego, jak z niego korzystać

 <?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
źródło