PHP "php: // input" vs $ _POST


243

Mi è stato chiesto di utilizzare il metodo php://inputinvece di $_POSTinteragire con le richieste Ajax da JQuery. Quello che non capisco sono i vantaggi dell'utilizzo di questo rispetto al metodo globale di $_POSTo $_GET.


2
Usavo "hack" per ricevere chiamate ajax sul lato PHP prima di imbattermi in questo post e leggere le sue fantastiche risposte! Per altre persone che avranno lo stesso problema in futuro, spero che anche i motori di ricerca leggano il mio commento! :)
aderchox,

Risposte:


484

Il motivo è che php://inputrestituisce tutti i dati non elaborati dopo le intestazioni HTTP della richiesta, indipendentemente dal tipo di contenuto.

Il superglobal PHP dovrebbe$_POST solo avvolgere i dati

  • application/x-www-form-urlencoded (tipo di contenuto standard per semplici moduli) o
  • multipart/form-data (utilizzato principalmente per i caricamenti di file)

Questo perché questi sono gli unici tipi di contenuto che devono essere supportati dagli agenti utente . Quindi il server e PHP tradizionalmente non si aspettano di ricevere nessun altro tipo di contenuto (il che non significa che non potrebbero).

Quindi, se semplicemente POST un buon vecchio HTML form, la richiesta è simile alla seguente:

POST /page.php HTTP/1.1

key1=value1&key2=value2&key3=value3

Ma se lavori molto con Ajax, questo probaby include anche lo scambio di dati più complessi con tipi (string, int, bool) e strutture (array, oggetti), quindi nella maggior parte dei casi JSON è la scelta migliore. Ma una richiesta con un payload JSON sarebbe simile a questa:

POST /page.php HTTP/1.1

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

Il contenuto ora sarebbe application/json(o almeno nessuno dei precedenti), quindi il $_POSTwrapper di PHP non sa come gestirlo (ancora).

I dati sono ancora lì, non è possibile accedervi tramite il wrapper. Quindi è necessario recuperarlo da soli in formato raw con file_get_contents('php://input')( purché non sia multipart/form-datacodificato ).

Questo è anche il modo in cui si accede ai dati XML o a qualsiasi altro tipo di contenuto non standard.


40
+1 per "Questo è anche il modo in cui accedere ai dati XML o a qualsiasi altro tipo di contenuto non standard"
mandza,

@Quasdank Sto inviando JSON dall'app Android al server php xampp nel cloud ( stackoverflow.com/questions/36558261/… ) ma non sono riuscito a farlo funzionare quando ho provato file_get_contents ('php: // input'), che restituisce semplicemente stringa (0). Questo funzionava nel mio computer locale ma non funziona quando lo ho distribuito sul cloud. Potresti aiutarmi per favore?
The_Martian

1
Vale la pena notare che l'uso dell'oggetto XMLHttpRequest in una richiesta AJAX a PHP non significa che si debba pubblicare JSON. È un sovraccarico aggiuntivo, ma JavaScript JavaScript sul lato client può essere convertito in formato application / x-www-form-urlencoded. Tuttavia, la traduzione potrebbe non essere del tipo di dati puro .
Anthony Rutledge,

È necessario dire che il limite di due tipi di contenuto riconosciuti è ampiamente storico. Nulla impedisce a PHP di riconoscere, ad esempio, application/jsoncome fonte di dati valida per l' $_POSTarray. E ci sono persino richieste pubblicate per quel supporto specifico.
AnrDaemon

Ciao @quasdunk, per favore, puoi aiutarmi in questo magento.stackexchange.com/questions/296960/…
Nagaraju K,

53

php://inputpuò darti i byte grezzi dei dati. Ciò è utile se i dati POST sono una struttura codificata JSON, che è spesso il caso di una richiesta POST AJAX.

Ecco una funzione per fare proprio questo:

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

L' $_POSTarray è più utile quando si gestiscono i dati valore-chiave da un modulo, inviati da un POST tradizionale. Funziona solo se i dati POST sono in un formato riconosciuto, di solito application/x-www-form-urlencoded(vedi http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 per i dettagli).


7
Vale la pena notare che se si passa truecome secondo parametro a json_decode, verrà restituito un array associativo.
Vahid Amiri,

28

Se i dati post non sono corretti, $ _POST non conterrà nulla. Tuttavia, php: // input avrà la stringa non valida.

Ad esempio, ci sono alcune applicazioni ajax, che non formano una corretta sequenza di valori-chiave post per il caricamento di un file e scaricano semplicemente tutto il file come dati post, senza nomi di variabili o altro. $ _POST sarà vuoto, anche $ _FILES vuoto e php: // input conterrà un file esatto, scritto come una stringa.


22

Innanzitutto, una verità di base su PHP.

PHP non è stato progettato per darti esplicitamente un'interfaccia pura REST (GET, POST, PUT, PATCH, DELETE) per la gestione delle richieste HTTP .

Tuttavia, il $_POST, $_GETe i $_FILES superglobali e la funzione filter_input_array()sono molto utili per le esigenze della persona media / laica.

Il vantaggio nascosto numero uno di $_POST(e $_GET) è che i tuoi dati di input vengono automaticamente codificati da PHP . Non hai mai pensato di doverlo fare, specialmente per i parametri della stringa di query all'interno di una richiesta GET standard.

Tuttavia, allora impari di più ...

Detto questo, man mano che avanzi nelle tue conoscenze di programmazione e desideri utilizzare l' XmlHttpRequestoggetto JavaScript (jQuery per alcuni), arriverai a vedere i limiti di questo schema.

$_POSTti limita all'uso di due tipi di media Content-Typenell'intestazione HTTP :

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

Pertanto, se si desidera inviare valori di dati a PHP sul server e visualizzarli nel $_POSTsuperglobal , è necessario codificarli sul lato client e inviare tali dati come coppie chiave / valore - un passaggio scomodo per i principianti (soprattutto quando si cerca di capire se parti diverse dell'URL richiedono forme diverse di urlencoding: normale, grezzo, ecc.).

Per tutti voi utenti jQuery, il $.ajax()metodo sta convertendo il vostro JSON in coppie chiave / valore codificate URL prima di trasmetterle al server. È possibile ignorare questo comportamento impostando processData: false. Basta leggere la documentazione $ .ajax () e non dimenticare di inviare il tipo di supporto corretto nell'intestazione Content-Type.

Codifica URL? Che diamine!!!???

In genere, se si esegue una richiesta HTTP normale, sincrona (quando l'intera pagina viene ridisegnata) con un modulo HTML, l'utente-agente (browser Web) codificherà i dati del modulo per voi. Se si desidera eseguire richieste HTTP asincrone utilizzando l' XmlHttpRequestoggetto, è necessario creare una stringa codificata e inviarla, se si desidera che tali dati $_POSTvengano visualizzati nel superglobal .

Come sei in contatto con JavaScript? :-)

La conversione da un array o un oggetto JavaScript in una stringa codificata urlata disturba molti sviluppatori (anche con nuove API come Form Data ). Preferirebbero semplicemente inviare JSON e sarebbe più efficiente per il codice client farlo.

Ricorda (occhiolino, occhiolino), lo sviluppatore web medio non impara ad usare XmlHttpRequestdirettamente l' oggetto, le funzioni globali, le funzioni di stringa, le funzioni di matrice e le espressioni regolari come te e io ;-). La codifica URL per loro è un incubo. ;-)

PHP, cosa dà?

La mancanza di gestione XML e JSON intuitiva di PHP disattiva molte persone. Penseresti che ora sarebbe parte di PHP (sospiro).

Tanti tipi di media (tipi MIME in passato)

XML, JSON e YAML hanno tutti tipi di media che possono essere inseriti in Content-Typeun'intestazione HTTP .

  • application / xml
  • applicaiton / JSON
  • application / yaml (sebbene IANA non abbia una designazione ufficiale elencata)

Guarda quanti tipi di media (precedentemente, tipi MIME) sono definiti da IANA.

Guarda quante intestazioni HTTP ci sono.

php: // input o bust

L'uso del php://inputflusso ti consente di aggirare il livello di astrazione che il PHP ha imposto al mondo da baby-sitter / mano. :-) Con un grande potere viene una grande responsabilità!

Ora, prima di gestire i valori dei dati trasmessi in streaming php://input, dovresti / devi fare alcune cose.

  1. Determinare se è stato indicato il metodo HTTP corretto (GET, POST, PUT, PATCH, DELETE, ...)
  2. Determinare se l' intestazione del tipo di contenuto HTTP è stata trasmessa.
  3. Determinare se il valore per Content-Type è il tipo di supporto desiderato.
  4. Determinare se i dati inviati sono ben formati XML / JSON / YMAL / ecc.
  5. Se necessario, converti i dati in un tipo di dati PHP: array o oggetto.
  6. Se qualcuno di questi controlli o conversioni di base fallisce, lancia un'eccezione !

E la codifica dei caratteri?

AH, HA! Sì, potresti voler inviare il flusso di dati nell'applicazione con codifica UTF-8, ma come puoi sapere se lo è o no?

Due problemi critici.

  1. Non sai quanti dati stanno arrivando php://input.
  2. Non si conosce con certezza la codifica corrente del flusso di dati.

Tenterai di gestire i dati di flusso senza sapere quanto c'è prima? Questa è un'idea terribile . Non è possibile fare affidamento esclusivamente sull'intestazione HTTP Content-Lengthper indicazioni sulla dimensione dell'input in streaming perché può essere falsificato.

Avrai bisogno di un:

  1. Algoritmo di rilevamento della dimensione del flusso.
  2. Limiti di dimensione del flusso definiti dall'applicazione (i limiti di Apache / Nginx / PHP potrebbero essere troppo ampi).

Tenterai di convertire i dati del flusso in UTF-8 senza conoscere la codifica corrente del flusso? Come? Il filtro iconv stream ( esempio di filtro iconv stream ) sembra desiderare una codifica iniziale e finale, come questa.

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

Pertanto, se sei coscienzioso, avrai bisogno di:

  1. Algoritmo di rilevamento della codifica del flusso.
  2. Algoritmo di definizione del filtro del flusso dinamico / runtime (perché non è possibile conoscere a priori la codifica iniziale).

( Aggiornamento : 'convert.iconv.UTF-8/UTF-8'forzerà tutto su UTF-8, ma devi comunque tenere conto dei caratteri che la libreria iconv potrebbe non sapere come tradurre. In altre parole, devi in ​​qualche modo definire quale azione intraprendere quando un personaggio non può essere tradotto : 1) Inserisci un personaggio fittizio, 2) Fallimento / lancio ed eccezione).

Non puoi fare affidamento esclusivamente sull'intestazione HTTP Content-Encoding, poiché ciò potrebbe indicare qualcosa di simile alla compressione come nel seguito. Questo non è ciò di cui vuoi prendere una decisione riguardo a iconv.

Content-Encoding: gzip

Pertanto, i passaggi generali potrebbero essere ...

Parte I: Richiesta HTTP relativa

  1. Determinare se è stato indicato il metodo HTTP corretto (GET, POST, PUT, PATCH, DELETE, ...)
  2. Determinare se l' intestazione del tipo di contenuto HTTP è stata trasmessa.
  3. Determinare se il valore per Content-Type è il tipo di supporto desiderato.

Parte II: Stream dati correlati

  1. Determina la dimensione del flusso di input (facoltativo, ma consigliato).
  2. Determina la codifica del flusso di input.
  3. Se necessario, converti il ​​flusso di input nella codifica dei caratteri desiderata (UTF-8).
  4. Se necessario, invertire qualsiasi compressione o crittografia a livello di applicazione, quindi ripetere i passaggi 4, 5 e 6.

Parte III: relativi al tipo di dati

  1. Determinare se i dati inviati sono ben formati XML / JSON / YMAL / ecc.

(Ricorda, i dati possono comunque essere una stringa codificata URL che devi quindi analizzare e decodificare URL).

  1. Se necessario, converti i dati in un tipo di dati PHP: array o oggetto.

Parte IV: valore relativo ai dati

  1. Filtra i dati di input.

  2. Convalida dati di input.

Adesso vedi?

Il $_POSTsuperglobal, insieme alle impostazioni php.ini per i limiti di input, sono più semplici per i non addetti ai lavori. Tuttavia, gestire la codifica dei caratteri è molto più intuitivo ed efficiente quando si usano i flussi perché non è necessario eseguire il ciclo tra superglobali (o array, in generale) per verificare i valori di input per la codifica corretta.


1
Oh wow! Questa risposta dovrebbe avere un punteggio molto più alto. Grazie mille per aver portato la luce di inondazione nel buio.
Lox

In ultima analisi, PHP farebbe bene ad aggiornare i valori di default di base. Tuttavia, è un errore logico accoppiare un metodo di richiesta HTTP con una struttura di dati con lo stesso nome ($ _GET, $ _POST). Ciò che conta è (1) il metodo di richiesta HTTP desiderato e (2) sono i dati di richiesta con quella richiesta (Content-Type). Quindi, come per Perl, devi vedere che puoi essere la vittima volontaria delle opinioni dei creatori / manutentori del linguaggio.
Anthony Rutledge,

0

Quindi ho scritto una funzione che otterrebbe i dati POST dal flusso di input php: // .

Quindi la sfida qui era passare al metodo di richiesta PUT, DELETE O PATCH e ottenere comunque i dati di post che sono stati inviati con quella richiesta.

Sto condividendo questo forse per qualcuno con una sfida simile. La funzione di seguito è quella che mi è venuta in mente e funziona. Spero possa essere d'aiuto!

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

-5

Semplice esempio di come usarlo

 <?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;  }  
   ?>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.