PHP "php: // input" vs $ _POST


243

Me han indicado que use el método en php://inputlugar de $_POSTinteractuar con las solicitudes de Ajax de JQuery. Lo que no entiendo es los beneficios de usar esto frente al método global de $_POSTo $_GET.


2
Solía ​​usar "hacks" para recibir llamadas ajax en el lado de PHP antes de tropezar con esta publicación y leer sus increíbles respuestas. Para otras personas que tengan el mismo problema en el futuro, ¡espero que los motores de búsqueda también lean mi comentario! :)
aderchox

Respuestas:


484

La razón es que php://inputdevuelve todos los datos en bruto después de los encabezados HTTP de la solicitud, independientemente del tipo de contenido.

El PHP superglobal $_POST, solo se supone que envuelve datos que son

  • application/x-www-form-urlencoded (tipo de contenido estándar para publicaciones de formulario simples) o
  • multipart/form-data (utilizado principalmente para cargar archivos)

Esto se debe a que estos son los únicos tipos de contenido que deben ser compatibles con los agentes de usuario . Por lo tanto, el servidor y PHP tradicionalmente no esperan recibir ningún otro tipo de contenido (lo que no significa que no puedan).

Entonces, si simplemente PUBLICA un buen HTML antiguo form, la solicitud se ve así:

POST /page.php HTTP/1.1

key1=value1&key2=value2&key3=value3

Pero si está trabajando mucho con Ajax, este probaby también incluye el intercambio de datos más complejos con tipos (string, int, bool) y estructuras (arrays, objetos), por lo que en la mayoría de los casos JSON es la mejor opción. Pero una solicitud con una carga JSON se vería así:

POST /page.php HTTP/1.1

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

El contenido ahora sería application/json(o al menos ninguno de los mencionados anteriormente), por lo que PHP $_POST-wrapper no sabe cómo manejar eso (todavía).

Los datos todavía están allí, simplemente no puede acceder a ellos a través del contenedor. Por lo tanto, debe buscarlo usted mismo en formato sin formato con file_get_contents('php://input')( siempre que no multipart/form-dataesté codificado ).

Así es también como accedería a datos XML o cualquier otro tipo de contenido no estándar.


40
+1 para "Así es como accedería a datos XML o cualquier otro tipo de contenido no estándar"
mandza

@Quasdank Estoy enviando JSON desde la aplicación de Android al servidor php xampp en la nube ( stackoverflow.com/questions/36558261/… ) pero no pude hacerlo funcionar cuando probé file_get_contents ('php: // input'), que simplemente devuelve la cadena (0). Esto solía funcionar en mi máquina local, pero no funciona cuando lo implementé en la nube. ¿Usted me podría ayudar por favor?
The_Martian

1
Vale la pena señalar que el uso del objeto XMLHttpRequest en una solicitud AJAX a PHP no significa que uno deba publicar JSON. Es una sobrecarga adicional, pero su JavaScript del lado del cliente puede convertirse al formato application / x-www-form-urlencoded. Sin embargo, la traducción puede no ser del tipo de datos puro .
Anthony Rutledge

Es necesario decir que el límite de dos tipos de contenido reconocidos es en gran parte histórico. Nada impide que PHP lo reconozca, es decir, application/jsoncomo fuente de datos válida para la $_POSTmatriz. E incluso hay solicitudes publicadas para específicamente ese soporte.
AnrDaemon

Hola @quasdunk, ¿puedes ayudarme en este magento.stackexchange.com/questions/296960/…
Nagaraju K

53

php://inputpuede darle los bytes sin procesar de los datos. Esto es útil si los datos PUBLICADOS son una estructura codificada JSON, que a menudo es el caso de una solicitud POST AJAX.

Aquí hay una función para hacer exactamente eso:

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

La $_POSTmatriz es más útil cuando maneja datos clave-valor de un formulario, enviado por un POST tradicional. Esto solo funciona si los datos PUBLICADOS están en un formato reconocido, por lo general application/x-www-form-urlencoded(consulte http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 para más detalles).


77
Vale la pena señalar que si pasa truecomo segundo parámetro a json_decode, devolverá una matriz asociativa.
Vahid Amiri

28

Si los datos de la publicación tienen un formato incorrecto, $ _POST no contendrá nada. Sin embargo, php: // input tendrá la cadena con formato incorrecto.

Por ejemplo, hay algunas aplicaciones ajax que no forman la secuencia correcta de clave-valor de publicación para cargar un archivo, y simplemente vuelcan todo el archivo como datos de publicación, sin nombres de variables ni nada. $ _POST estará vacío, $ _FILES también estará vacío y php: // input contendrá el archivo exacto, escrito como una cadena.


22

Primero, una verdad básica sobre PHP.

PHP no fue diseñado para darle explícitamente una interfaz REST pura (GET, POST, PUT, PATCH, DELETE) para manejar solicitudes HTTP .

Sin embargo, las $_POST, $_GETy $_FILES superglobales , y la función filter_input_array()son muy útiles para las necesidades / de de una persona promedio laico.

La ventaja oculta número uno de $_POST(y $_GET) es que sus datos de entrada son codificados automáticamente por PHP . Nunca piensa en tener que hacerlo, especialmente para los parámetros de cadena de consulta dentro de una solicitud GET estándar.

Sin embargo, entonces aprendes más ...

Dicho esto, a medida que avanzas en tu conocimiento de programación y quieres usar el XmlHttpRequestobjeto de JavaScript (jQuery para algunos), ves la limitación de este esquema.

$_POSTte limita al uso de dos tipos de medios en el Content-Typeencabezado HTTP :

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

Por lo tanto, si desea enviar valores de datos a PHP en el servidor y hacer que se muestren en el $_POSTsuperglobal , debe codificarlos en el lado del cliente y enviar dichos datos como pares clave / valor, un paso inconveniente para los novatos (especialmente cuando se trata de averiguar si diferentes partes de la URL requieren diferentes formas de codificación urinaria: normal, sin formato, etc.).

Para todos los usuarios de jQuery, el $.ajax()método es convertir su JSON en pares clave / valor codificados con URL antes de transmitirlos al servidor. Puede anular este comportamiento configurando processData: false. Simplemente lea la documentación de $ .ajax () y no olvide enviar el tipo de medio correcto en el encabezado Content-Type.

Codificación de URL? ¿¿¿¡¡¡Que demonios!!!???

Por lo general, si está realizando una solicitud HTTP normal, sincrónica (cuando se vuelve a dibujar toda la página) con un formulario HTML, el agente de usuario (navegador web) codificará sus datos de formulario por usted. Si desea realizar solicitudes HTTP asincrónicas utilizando el XmlHttpRequestobjeto, debe crear una cadena codificada en urlen y enviarla, si desea que esos datos se muestren en el $_POSTsuperglobal .

¿Qué tan en contacto estás con JavaScript? :-)

La conversión de una matriz u objeto de JavaScript a una cadena codificada urlen molesta a muchos desarrolladores (incluso con nuevas API como Form Data ). Prefieren simplemente poder enviar JSON, y sería más eficiente que el código del cliente lo haga.

Recuerde (guiño, guiño), el desarrollador web promedio no aprende a usar el XmlHttpRequestobjeto directamente, funciones globales, funciones de cadena, funciones de matriz y expresiones regulares como usted y yo ;-). Urlencoding para ellos es una pesadilla. ;-)

PHP, ¿qué da?

La falta de PHP de manejo intuitivo de XML y JSON apaga a muchas personas. Pensarías que ahora sería parte de PHP (suspiro).

Tantos tipos de medios (tipos MIME en el pasado)

XML, JSON y YAML tienen tipos de medios que se pueden colocar en un Content-Typeencabezado HTTP .

  • aplicación / xml
  • aplicación / json
  • application / yaml (aunque IANA no tiene una designación oficial en la lista)

Mire cuántos tipos de medios (anteriormente, tipos MIME) están definidos por IANA.

Mira cuántos encabezados HTTP hay.

php: // input o busto

El uso de la php://inputtransmisión le permite sortear el nivel de abstracción de cuidado de niños / manos que PHP ha forzado en el mundo. :-) ¡Con un gran poder viene una gran responsabilidad!

Ahora, antes de lidiar con los valores de datos transmitidos php://input, debe / debe hacer algunas cosas.

  1. Determine si se ha indicado el método HTTP correcto (GET, POST, PUT, PATCH, DELETE, ...)
  2. Determine si se ha transmitido el encabezado HTTP Content-Type .
  3. Determine si el valor para Content-Type es el tipo de medio deseado.
  4. Determine si los datos enviados están bien formados XML / JSON / YMAL / etc.
  5. Si es necesario, convierta los datos a un tipo de datos PHP: matriz u objeto.
  6. Si alguno de estos controles básicos o conversiones falla, ¡lanza una excepción !

¿Qué pasa con la codificación de caracteres?

AH, HA! Sí, es posible que desee que el flujo de datos que se envía a su aplicación esté codificado en UTF-8, pero ¿cómo puede saber si es o no?

Dos problemas críticos

  1. No sabes cuántos datos están llegando php://input.
  2. No sabe con certeza la codificación actual de la secuencia de datos.

¿Vas a tratar de manejar los datos del flujo sin saber cuánto hay primero? Esa es una idea terrible . No puede confiar exclusivamente en el Content-Lengthencabezado HTTP para obtener orientación sobre el tamaño de la entrada transmitida porque puede ser falsificada.

Necesitarás un:

  1. Algoritmo de detección de tamaño de flujo.
  2. Límites de tamaño de secuencia definidos por la aplicación (los límites de Apache / Nginx / PHP pueden ser demasiado amplios).

¿Intentará convertir los datos de la transmisión a UTF-8 sin conocer la codificación actual de la transmisión? ¿Cómo? El filtro de flujo iconv ( ejemplo de filtro de flujo iconv ) parece querer una codificación inicial y final, como esta.

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

Por lo tanto, si eres concienzudo, necesitarás:

  1. Algoritmo de detección de codificación de flujo.
  2. Algoritmo de definición de filtro de flujo dinámico / tiempo de ejecución (porque no puede conocer la codificación de inicio a priori).

( Actualización : 'convert.iconv.UTF-8/UTF-8'forzará todo a UTF-8, pero aún debe tener en cuenta los caracteres que la biblioteca iconv podría no saber traducir. En otras palabras, tiene que definir qué acción tomar cuando un carácter no se puede traducir : 1) Insertar un personaje ficticio, 2) Fallar / lanzar y excepción).

No puede confiar exclusivamente en el Content-Encodingencabezado HTTP , ya que esto podría indicar algo como la compresión como se muestra a continuación. Esto no es lo que desea tomar una decisión con respecto a iconv.

Content-Encoding: gzip

Por lo tanto, los pasos generales podrían ser ...

Parte I: Solicitud HTTP relacionada

  1. Determine si se ha indicado el método HTTP correcto (GET, POST, PUT, PATCH, DELETE, ...)
  2. Determine si se ha transmitido el encabezado HTTP Content-Type .
  3. Determine si el valor para Content-Type es el tipo de medio deseado.

Parte II: flujo de datos relacionados

  1. Determine el tamaño de la secuencia de entrada (opcional, pero recomendado).
  2. Determine la codificación de la secuencia de entrada.
  3. Si es necesario, convierta la secuencia de entrada a la codificación de caracteres deseada (UTF-8).
  4. Si es necesario, revierta cualquier compresión o cifrado de nivel de aplicación, y luego repita los pasos 4, 5 y 6.

Parte III: Tipo de datos relacionado

  1. Determine si los datos enviados están bien formados XML / JSON / YMAL / etc.

(Recuerde, los datos aún pueden ser una cadena codificada de URL que luego debe analizar y decodificar URL).

  1. Si es necesario, convierta los datos a un tipo de datos PHP: matriz u objeto.

Parte IV: Relacionado con el valor de los datos

  1. Filtrar datos de entrada.

  2. Validar datos de entrada.

Ahora ves

El $_POSTsuperglobal, junto con la configuración de php.ini para los límites de entrada, son más simples para el lego. Sin embargo, lidiar con la codificación de caracteres es mucho más intuitivo y eficiente cuando se usan flujos porque no hay necesidad de recorrer superglobales (o matrices, en general) para verificar los valores de entrada para la codificación adecuada.


1
¡Oh wow! Esta respuesta debería tener una calificación mucho más alta. Muchas gracias por traer la luz de inundación a la oscuridad.
Lox

En el análisis final, PHP haría bien en actualizar los valores predeterminados de la base. Sin embargo, es lógicamente una falacia acoplar un método de solicitud HTTP con una estructura de datos del mismo nombre ($ _GET, $ _POST). Lo que importa es (1) el método de solicitud HTTP deseado y (2) si hay datos de solicitud con esa solicitud (Tipo de contenido). Por lo tanto, como con Perl, debes ver que puedes ser una víctima voluntaria de las opiniones de los creadores / mantenedores del lenguaje.
Anthony Rutledge

0

Entonces escribí una función que obtendría los datos POST del flujo de entrada php: // .

Entonces, el desafío aquí fue cambiar al método de solicitud PUT, DELETE O PATCH, y aún así obtener los datos de publicación que se enviaron con esa solicitud.

Estoy compartiendo esto quizás para alguien con un desafío similar. La función a continuación es lo que se me ocurrió y funciona. ¡Espero que ayude!

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

Ejemplo simple de cómo 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;  }  
   ?>
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.