PHP "php://entrada" frente a $_POST

Resuelto Lee asked hace 54 años • 6 respuestas

Se me indicó que usara el método php://inputen lugar de $_POSTinteractuar con solicitudes Ajax de JQuery. Lo que no entiendo son los beneficios de usar esto frente al método global de $_POSTo $_GET.

Lee avatar Jan 01 '70 08:01 Lee
Aceptado

El motivo es que php://inputdevuelve todos los datos sin procesar después de los encabezados HTTP de la solicitud, independientemente del tipo de contenido.

Se supone que PHP superglobal $_POSTsolo 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 hacerlo).

Entonces, si simplemente PUBLICAS un buen HTML antiguo form, la solicitud se verá así:

POST /page.php HTTP/1.1

key1=value1&key2=value2&key3=value3

Pero si trabaja mucho con Ajax, esto probablemente también incluye el intercambio de datos más complejos con tipos (cadena, int, bool) y estructuras (matrices, objetos), por lo que en la mayoría de los casos JSON es la mejor opción. Pero una solicitud con una carga útil 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 $_POSTel contenedor de PHP no sabe cómo manejar eso (todavía).

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

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

Quasdunk avatar Jan 17 '2012 11:01 Quasdunk

Primero, una verdad básica sobre PHP.

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

Sin embargo, los $_SERVER, $_COOKIE, $_POST, $_GETy $_FILES superglobales y la función filter_input_array()son muy útiles para las necesidades de la persona promedio o del profano.

La ventaja oculta número uno de $_POST(y $_GET) es que PHP decodifica automáticamente la URL de sus datos de entrada . Ni siquiera piensa en tener que hacerlo, especialmente para los parámetros de cadena de consulta dentro de una GETsolicitud estándar o los datos del cuerpo HTTP enviados con una POSTsolicitud.

Otros métodos de solicitud HTTP

Quienes estudian el protocolo HTTP subyacente y sus diversos métodos de solicitud llegan a comprender que existen muchos métodos de solicitud HTTP, incluidos los a los que a menudo se hace referencia PUT( PATCHno se utilizan en Apigee de Google) y DELETE.

En PHP, no hay funciones superglobales ni de filtro de entrada para obtener datos del cuerpo de la solicitud HTTP cuando POSTno se utilizan. ¿Qué deben hacer los discípulos de Roy Fielding? ;-)

Sin embargo, luego aprendes más...

Dicho esto, a medida que avanza en sus conocimientos de programación PHP y desea utilizar XmlHttpRequestel objeto de JavaScript (jQuery para algunos), verá la limitación de este esquema.

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

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

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

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

php://entrada, pero...

Incluso si usa php://inputen lugar de los datos del cuerpo $_POSTde su POSTsolicitud HTTP, no funcionará con un HTTP Content-Typede multipart/form-data¡Este es el tipo de contenido que usa en un formulario HTML cuando desea permitir la carga de archivos!

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

Por lo tanto, en PHP tradicional, para manejar una diversidad de tipos de contenido a partir de una POSTsolicitud HTTP, aprenderá a utilizar $_POSTo filter_input_array(POST), $_FILESy php://input. No hay forma de utilizar solo una fuente de entrada universal para POSTsolicitudes HTTP en PHP.

No puede obtener archivos a través de $_POST, filter_input_array(POST)o php://input, y no puede obtener JSON/XML/YAML en ninguno de los filter_input_array(POST)dos o $_POST.

Manual de PHP: php://entrada

php://input es una secuencia de solo lectura que le permite leer datos sin procesar del cuerpo de la solicitud ...php://input no está disponible con enctype="multipart/form-data".

¿Frameworks PHP al rescate?

Los marcos PHP como Codeigniter 4 y Laravel utilizan una fachada para proporcionar una interfaz ( IncomingRequestu Requestobjetos) más limpios que los anteriores. Es por eso que los desarrolladores profesionales de PHP utilizan marcos en lugar de PHP sin formato.

Por supuesto, si le gusta programar, puede diseñar su propio objeto de fachada para proporcionar lo que hacen los marcos. Debido a que me tomé el tiempo para investigar este problema, puedo escribir esta respuesta.

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

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

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

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

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

PHP, ¿qué pasa?

La falta de PHP de manejo intuitivo de XML y JSON desanima a muchas personas. Uno pensaría que ya 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 la IANA no tiene ninguna designación oficial en la lista)

Mire cuántos tipos de medios (anteriormente, tipos MIME) define la IANA.

Mira cuántos encabezados HTTP hay.

php://entrada o busto

El uso de la php://inputtransmisión le permite eludir el nivel de abstracción de cuidar niños/tomar las manos que PHP ha impuesto al mundo. :-) ¡Con un gran poder viene una gran responsabilidad!

Ahora, antes de ocuparse de los valores de datos transmitidos a través de php://input, debe/debe hacer algunas cosas.

  1. Determinar 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. Determinar si los datos enviados están bien formados XML/JSON/YAML/etc.
  5. Si es necesario, convierta los datos a un tipo de datos PHP: matriz u objeto.
  6. Si alguna de estas comprobaciones o conversiones básicas falla, ¡lance una excepción !

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

¡AH, JA! 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 lo está o no?

Dos problemas críticos.

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

¿Vas a intentar manejar datos de transmisión sin saber cuántos hay primero? Esa es una idea terrible . No puede confiar exclusivamente en el Content-Lengthencabezado HTTP como guía sobre el tamaño de la entrada transmitida porque puede ser falsificado.

Vas a necesitar un:

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

¿Va a intentar convertir datos de 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'

Así, 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/en tiempo de ejecución (porque no se puede conocer la codificación inicial 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 tal vez no sepa cómo traducir. En otras palabras, debe definir de alguna manera qué acción tomar cuando un carácter no se puede traducir : 1) Insertar un carácter ficticio, 2) Fallo/lanzamiento y excepción).

No puede confiar exclusivamente en el Content-Encodingencabezado HTTP, ya que esto podría indicar algo parecido a una compresión como se muestra a continuación. Esto no es sobre 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. Determinar 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: Transmisión de datos relacionados

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

Parte III: Tipo de datos relacionados

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

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

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

Parte IV: Valor de los datos relacionados

  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 profano. Sin embargo, lidiar con la codificación de caracteres es mucho más intuitivo y eficiente cuando se utilizan secuencias porque no es necesario recorrer superglobales (o matrices, en general) para verificar que los valores de entrada tengan la codificación adecuada.

Anthony Rutledge avatar Nov 19 '2019 06:11 Anthony Rutledge

php://inputpuede darle los bytes sin procesar de los datos. Esto es útil si los datos publicados son una estructura codificada en JSON, lo que suele ser el caso de una solicitud POST de AJAX.

Aquí hay una función para hacer precisamente 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 mediante un POST tradicional. Esto solo funciona si los datos publicados están en un formato reconocido, generalmente application/x-www-form-urlencoded(consulte http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 para obtener más detalles).

Rob Agar avatar Jan 17 '2012 11:01 Rob Agar

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 una secuencia de clave-valor de publicación correcta para cargar un archivo y simplemente descargan 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.

Nameless avatar Jan 17 '2012 11:01 Nameless