Me han indicado que use el método en php://input
lugar de $_POST
interactuar con las solicitudes de Ajax de JQuery. Lo que no entiendo es los beneficios de usar esto frente al método global de $_POST
o $_GET
.
Me han indicado que use el método en php://input
lugar de $_POST
interactuar con las solicitudes de Ajax de JQuery. Lo que no entiendo es los beneficios de usar esto frente al método global de $_POST
o $_GET
.
Respuestas:
La razón es que php://input
devuelve 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) omultipart/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-data
esté codificado ).
Así es también como accedería a datos XML o cualquier otro tipo de contenido no estándar.
application/json
como fuente de datos válida para la $_POST
matriz. E incluso hay solicitudes publicadas para específicamente ese soporte.
php://input
puede 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 $_POST
matriz 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).
true
como segundo parámetro a json_decode
, devolverá una matriz asociativa.
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.
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
, $_GET
y $_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.
Dicho esto, a medida que avanzas en tu conocimiento de programación y quieres usar el XmlHttpRequest
objeto de JavaScript (jQuery para algunos), ves la limitación de este esquema.
$_POST
te limita al uso de dos tipos de medios en el Content-Type
encabezado HTTP :
application/x-www-form-urlencoded
ymultipart/form-data
Por lo tanto, si desea enviar valores de datos a PHP en el servidor y hacer que se muestren en el $_POST
superglobal , 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.
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 XmlHttpRequest
objeto, debe crear una cadena codificada en urlen y enviarla, si desea que esos datos se muestren en el $_POST
superglobal .
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 XmlHttpRequest
objeto directamente, funciones globales, funciones de cadena, funciones de matriz y expresiones regulares como usted y yo ;-). Urlencoding para ellos es una pesadilla. ;-)
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).
XML, JSON y YAML tienen tipos de medios que se pueden colocar en un Content-Type
encabezado HTTP .
Mire cuántos tipos de medios (anteriormente, tipos MIME) están definidos por IANA.
Mira cuántos encabezados HTTP hay.
El uso de la php://input
transmisió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.
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?
php://input
.¿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-Length
encabezado HTTP para obtener orientación sobre el tamaño de la entrada transmitida porque puede ser falsificada.
Necesitarás un:
¿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:
( 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-Encoding
encabezado 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
Parte I: Solicitud HTTP relacionada
Parte II: flujo de datos relacionados
Parte III: Tipo de datos relacionado
(Recuerde, los datos aún pueden ser una cadena codificada de URL que luego debe analizar y decodificar URL).
Parte IV: Relacionado con el valor de los datos
Filtrar datos de entrada.
Validar datos de entrada.
El $_POST
superglobal, 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.
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;
}
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; }
?>