PHP “php: // input” vs $ _POST

243

Fui orientado a usar o método em php://inputvez de $_POSTinteragir com solicitações Ajax do JQuery. O que eu não entendo são os benefícios de usar isso vs o método global de $_POSTor $_GET.

Lee
fonte
2
Eu costumava usar "hacks" para receber chamadas de ajax no lado do PHP antes de tropeçar neste post e ler suas respostas impressionantes! Para outras pessoas com o mesmo problema no futuro, espero que os mecanismos de pesquisa também leiam meu comentário! :)
aderchox 21/09/18

Respostas:

484

A razão é que php://input retorna todos os dados brutos após os cabeçalhos HTTP da solicitação, independentemente do tipo de conteúdo.

O superglobal do PHP $_POSTapenas deve agrupar dados que sejam

  • application/x-www-form-urlencoded (tipo de conteúdo padrão para postagens simples) ou
  • multipart/form-data (usado principalmente para upload de arquivos)

Isso ocorre porque esses são os únicos tipos de conteúdo que devem ser suportados pelos agentes do usuário . Portanto, o servidor e o PHP tradicionalmente não esperam receber nenhum outro tipo de conteúdo (o que não significa que não possam).

Portanto, se você simplesmente POSTAR um bom e velho HTML form, a solicitação se parece com isso:

POST /page.php HTTP/1.1

key1=value1&key2=value2&key3=value3

Mas se você trabalha muito com o Ajax, esse probaby também inclui a troca de dados mais complexos com tipos (string, int, bool) e estruturas (matrizes, objetos); portanto, na maioria dos casos, o JSON é a melhor opção. Mas uma solicitação com uma carga JSON se pareceria com isso:

POST /page.php HTTP/1.1

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

O conteúdo agora seria application/json(ou pelo menos nenhum dos mencionados acima), portanto, o $_POSTww-wrapper do PHP ainda não sabe como lidar com isso (ainda).

Os dados ainda estão lá, você simplesmente não pode acessá-los através do wrapper. Portanto, você deve buscá-lo no formato bruto com file_get_contents('php://input')( desde que não seja multipart/form-datacodificado) ).

Também é assim que você acessaria dados XML ou qualquer outro tipo de conteúdo não padrão.

Quasdunk
fonte
40
+1 para "Isto também é como você
acessaria
@Quasdank estou enviando JSON do aplicativo Android para o servidor php xampp na nuvem ( stackoverflow.com/questions/36558261/… ), mas não consegui fazê-lo funcionar quando tentei file_get_contents ('php: // input'), que simplesmente retorna string (0). Isso costumava funcionar na minha máquina local, mas não funciona quando eu a implantei na nuvem. Você poderia me ajudar por favor?
precisa saber é o seguinte
1
Vale ressaltar que o uso do objeto XMLHttpRequest em uma solicitação AJAX para PHP não significa que é necessário postar JSON. É uma sobrecarga extra, mas o JavaScript do lado do cliente pode ser convertido para o formato application / x-www-form-urlencoded. No entanto, a tradução pode não ser do tipo de dados puro .
Anthony Rutledge
É necessário dizer que o limite de dois tipos de conteúdo reconhecidos é amplamente histórico. Nada impede que o PHP reconheça, ou seja, application/jsoncomo fonte de dados válida para o $_POSTarray. E há até pedidos publicados especificamente para esse suporte.
AnrDaemon 19/03/19
Oi @quasdunk você pode por favor me ajude neste magento.stackexchange.com/questions/296960/...
Nagaraju K
53

php://inputpode fornecer os bytes brutos dos dados. Isso é útil se os dados POSTed forem uma estrutura codificada em JSON, que geralmente é o caso de uma solicitação AJAX POST.

Aqui está uma função para fazer exatamente isso:

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

A $_POSTmatriz é mais útil quando você manipula dados de valor-chave de um formulário, enviado por um POST tradicional. Isso funciona apenas se os dados do POSTed estiverem em um formato reconhecido, normalmente application/x-www-form-urlencoded(consulte http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 para obter detalhes).

Rob Agar
fonte
7
Vale a pena notar que, se você passar truecomo o segundo parâmetro json_decode, ele retornará uma matriz associativa.
Vahid Amiri
28

Se os dados da postagem estiverem malformados, $ _POST não conterá nada. No entanto, php: // input terá a string malformada.

Por exemplo, existem alguns aplicativos ajax que não formam a sequência correta de valor-chave de postagem para fazer upload de um arquivo e apenas despejam todo o arquivo como dados de postagem, sem nomes de variáveis ​​ou qualquer coisa. $ _POST estará vazio, $ _FILES também vazio e php: // input conterá o arquivo exato, escrito como uma string.

Sem nome
fonte
22

Primeiro, uma verdade básica sobre PHP.

O PHP não foi projetado para fornecer explicitamente uma interface REST pura (GET, POST, PUT, PATCH, DELETE) para lidar com solicitações HTTP .

No entanto, os $_POST, $_GETe $_FILES superglobals , e a funçãofilter_input_array() são muito úteis para as necessidades / leigos da pessoa média.

A principal vantagem oculta de $_POST(e $_GET) é que seus dados de entrada são codificados automaticamente pelo PHP . Você nunca pensa em fazê-lo, especialmente para parâmetros de sequência de consulta em uma solicitação GET padrão.

No entanto, você aprende mais ...

Dito isto, à medida que você avança no seu conhecimento de programação e deseja usar o XmlHttpRequestobjeto JavaScript (jQuery para alguns), você vê a limitação desse esquema.

$_POSTlimita você ao uso de dois tipos de mídia no Content-Typecabeçalho HTTP :

  1. application/x-www-form-urlencodede
  2. multipart/form-data

Portanto, se você deseja enviar valores de dados para o PHP no servidor e mostrá-los na $_POSTsuperglobal , deve codificá- los no lado do cliente e enviar os dados como pares de chave / valor - uma etapa inconveniente para iniciantes (especialmente ao tentar descobrir se partes diferentes do URL exigem formas diferentes de codificação de URL: normal, bruto, etc.).

Para todos os usuários do jQuery, o $.ajax()método é converter seu JSON em pares de chave / valor codificado em URL antes de transmiti-los ao servidor. Você pode substituir esse comportamento, definindo processData: false. Basta ler a documentação $ .ajax () e não se esqueça de enviar o tipo de mídia correto no cabeçalho Content-Type.

Codificação de URL? Que diabos!!!???

Normalmente, se você estiver executando solicitações HTTP normais e síncronas (quando a página inteira redesenhar) com um formulário HTML, o agente do usuário (navegador da Web) codificará os dados do formulário para você. Se você deseja fazer solicitações HTTP assíncronas usando o XmlHttpRequestobjeto, é necessário criar uma string codificada em url e enviá-la, se desejar que esses dados sejam exibidos na $_POSTsuperglobal .

Como você está em contato com o JavaScript? :-)

A conversão de uma matriz ou objeto JavaScript em uma string codificada em url incomoda muitos desenvolvedores (mesmo com novas APIs como Form Data ). Eles prefeririam apenas enviar JSON, e seria mais eficiente o código do cliente fazer isso.

Lembre-se (wink, wink), o desenvolvedor da Web comum não aprende a usar o XmlHttpRequest objeto diretamente, funções globais, funções de string, funções de array e expressões regulares como você e eu ;-). Urlencoding para eles é um pesadelo. ;-)

PHP, o que dá?

A falta de manipulação intuitiva de XML e JSON do PHP desativa muitas pessoas. Você pensaria que seria parte do PHP agora (suspiro).

Tantos tipos de mídia (tipos MIME no passado)

XML, JSON e YAML todos têm tipos de mídia que podem ser colocados em um Content-Typecabeçalho HTTP .

  • application / xml
  • applicaiton / json
  • application / yaml (embora a IANA não tenha uma designação oficial listada)

Veja quantos tipos de mídia (anteriormente, tipos MIME) são definidos pela IANA.

Veja quantos cabeçalhos HTTP existem.

php: // input ou busto

Usando o php://input fluxo permite contornar o nível de abstração de babás / mãos que o PHP impôs ao mundo. :-) Com grandes poderes vem grandes responsabilidades!

Agora, antes de lidar com valores de dados transmitidos php://input, você deve / deve fazer algumas coisas.

  1. Determine se o método HTTP correto foi indicado (GET, POST, PUT, PATCH, DELETE, ...)
  2. Determine se o cabeçalho HTTP Content-Type foi transmitido.
  3. Determine se o valor para o Tipo de conteúdo é o tipo de mídia desejado.
  4. Determine se os dados enviados estão bem formados XML / JSON / YMAL / etc.
  5. Se necessário, converta os dados em um tipo de dados PHP: matriz ou objeto.
  6. Se alguma dessas verificações ou conversões básicas falhar, crie uma exceção !

E a codificação de caracteres?

Ah, ah! Sim, você pode querer que o fluxo de dados enviado ao seu aplicativo seja codificado em UTF-8, mas como você pode saber se é ou não?

Dois problemas críticos.

  1. Você não sabe quantos dados estão chegando php://input.
  2. Você não sabe ao certo a codificação atual do fluxo de dados.

Você tentará manipular dados de fluxo sem saber quanto há primeiro? Essa é uma péssima ideia . Você não pode confiar exclusivamente no Content-Lengthcabeçalho HTTP para obter orientação sobre o tamanho da entrada transmitida porque pode ser falsificada.

Você vai precisar de:

  1. Algoritmo de detecção de tamanho de fluxo.
  2. Limites de tamanho de fluxo definidos pelo aplicativo (os limites do Apache / Nginx / PHP podem ser muito amplos).

Você tentará converter dados de fluxo em UTF-8 sem conhecer a codificação atual do fluxo? Quão? O filtro de fluxo iconv ( exemplo de filtro de fluxo iconv ) parece querer uma codificação inicial e final, como esta.

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

Assim, se você tiver consciência, precisará de:

  1. Algoritmo de detecção de codificação de fluxo.
  2. Algoritmo de definição de filtro de fluxo dinâmico / de tempo de execução (porque você não pode saber a codificação inicial a priori).

( Atualização : 'convert.iconv.UTF-8/UTF-8'forçará tudo para UTF-8, mas você ainda precisará considerar os caracteres que a biblioteca iconv talvez não saiba traduzir. Em outras palavras, você precisa definir como agir quando um personagem não puder ser traduzido : 1) Insira um caractere fictício, 2) Fail / throw e exceção).

Você não pode confiar exclusivamente no Content-Encodingcabeçalho HTTP , pois isso pode indicar algo como compactação, como a seguir. Não é disso que você deseja tomar uma decisão em relação ao iconv.

Content-Encoding: gzip

Portanto, as etapas gerais podem ser ...

Parte I: Solicitação HTTP Relacionada

  1. Determine se o método HTTP correto foi indicado (GET, POST, PUT, PATCH, DELETE, ...)
  2. Determine se o cabeçalho HTTP Content-Type foi transmitido.
  3. Determine se o valor para o Tipo de conteúdo é o tipo de mídia desejado.

Parte II: Dados relacionados ao fluxo

  1. Determine o tamanho do fluxo de entrada (opcional, mas recomendado).
  2. Determine a codificação do fluxo de entrada.
  3. Se necessário, converta o fluxo de entrada na codificação de caracteres desejada (UTF-8).
  4. Se necessário, inverta qualquer compactação ou criptografia no nível do aplicativo e repita as etapas 4, 5 e 6.

Parte III: Relacionado ao Tipo de Dados

  1. Determine se os dados enviados estão bem formados XML / JSON / YMAL / etc.

(Lembre-se de que os dados ainda podem ser uma string codificada em URL que você deve analisar e decodificar em URL).

  1. Se necessário, converta os dados em um tipo de dados PHP: matriz ou objeto.

Parte IV: Relacionado ao Valor dos Dados

  1. Filtre os dados de entrada.

  2. Valide os dados de entrada.

Agora você vê?

A $_POSTsuperglobal, juntamente com as configurações do php.ini para limites de entrada, são mais simples para o leigo. No entanto, lidar com a codificação de caracteres é muito mais intuitivo e eficiente ao usar fluxos, porque não há necessidade de passar por superglobais (ou matrizes, geralmente) para verificar os valores de entrada para a codificação adequada.

Anthony Rutledge
fonte
1
Oh uau! Esta resposta deve ter uma classificação muito mais alta. Muito obrigado por trazer a luz da inundação para a escuridão.
Lox
Na análise final, o PHP faria bem em atualizar os padrões básicos. No entanto, é uma falácia lógica acoplar um método de solicitação HTTP com uma estrutura de dados com o mesmo nome ($ _GET, $ _POST). O que importa é (1) o método de solicitação HTTP desejado e (2) existem dados de solicitação com essa solicitação (Tipo de Conteúdo). Portanto, como no Perl, você deve ver que pode ser vítima voluntária das opiniões dos criadores / mantenedores de idiomas.
Anthony Rutledge
0

Então, eu escrevi uma função que obteria os dados POST do fluxo de entrada php: // .

Portanto, o desafio aqui foi mudar para o método de solicitação PUT, DELETE OR PATCH e ainda obter os dados de postagem enviados com essa solicitação.

Estou compartilhando isso talvez para alguém com um desafio semelhante. A função abaixo é o que eu criei e funciona. Espero que ajude!

    /**
     * @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
fonte
-5

Exemplo simples de como usá-lo

 <?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
fonte