Si paso variables de PHP con .
sus nombres a través de $ _GET, PHP las reemplaza automáticamente con _
caracteres. Por ejemplo:
<?php
echo "url is ".$_SERVER['REQUEST_URI']."<p>";
echo "x.y is ".$_GET['x.y'].".<p>";
echo "x_y is ".$_GET['x_y'].".<p>";
... genera lo siguiente:
url is /SpShipTool/php/testGetUrl.php?x.y=a.b
x.y is .
x_y is a.b.
... mi pregunta es la siguiente: ¿hay alguna forma de que esto se detenga? No puedo por mi vida averiguar qué he hecho para merecer esto
La versión de PHP con la que estoy ejecutando es 5.2.4-2ubuntu5.3.
Respuestas:
Aquí está la explicación de PHP.net de por qué lo hace:
Eso es de http://ca.php.net/variables.external .
Además, según este comentario, estos otros caracteres se convierten en guiones bajos:
Entonces, parece que está atascado, por lo que tendrá que convertir los guiones bajos de nuevo en puntos en su script usando la sugerencia de dawnerd (aunque solo usaría str_replace ).
fuente
Pregunta respondida hace mucho tiempo, pero en realidad hay una mejor respuesta (o solución alternativa). PHP le permite en el flujo de entrada sin procesar , por lo que puede hacer algo como esto:
$query_string = file_get_contents('php://input');
que le dará la matriz $ _POST en formato de cadena de consulta, puntos como deberían ser.
Luego puede analizarlo si lo necesita (según el comentario de POSTER )
<?php // Function to fix up PHP's messing up input containing dots, etc. // `$source` can be either 'POST' or 'GET' function getRealInput($source) { $pairs = explode("&", $source == 'POST' ? file_get_contents("php://input") : $_SERVER['QUERY_STRING']); $vars = array(); foreach ($pairs as $pair) { $nv = explode("=", $pair); $name = urldecode($nv[0]); $value = urldecode($nv[1]); $vars[$name] = $value; } return $vars; } // Wrapper functions specifically for GET and POST: function getRealGET() { return getRealInput('GET'); } function getRealPOST() { return getRealInput('POST'); } ?>
Muy útil para los parámetros OpenID, que contienen tanto '.' y '_', ¡cada uno con un cierto significado!
fuente
file_get_contents("php://input")
con$_SERVER['QUERY_STRING']
.$_SERVER['COOKIES']
Resaltando una respuesta real de Johan en un comentario anterior: acabo de envolver toda mi publicación en una matriz de nivel superior que evita por completo el problema sin requerir un procesamiento pesado.
En la forma en que lo haces
<input name="data[database.username]"> <input name="data[database.password]"> <input name="data[something.else.really.deep]">
en vez de
<input name="database.username"> <input name="database.password"> <input name="something.else.really.deep">
y en el controlador de publicaciones, simplemente desenvuélvalo:
$posdata = $_POST['data'];
Para mí, este fue un cambio de dos líneas, ya que mis puntos de vista estaban completamente basados en plantillas.
FYI. Estoy usando puntos en mis nombres de campo para editar árboles de datos agrupados.
fuente
El funcionamiento de esta función es una buena idea que se me ocurrió durante mis vacaciones de verano de 2013.
Cumple con los estándares y tiene soporte profundo para arreglos, por ejemplo
a.a[x][b.a]=10
. Se utilizaparse_str()
detrás de escena con un preprocesamiento específico.function fix($source) { $source = preg_replace_callback( '/(^|(?<=&))[^=[&]+/', function($key) { return bin2hex(urldecode($key[0])); }, $source ); parse_str($source, $post); $result = array(); foreach ($post as $key => $val) { $result[hex2bin($key)] = $val; } return $result; }
Y luego puedes aplicarlo así, dependiendo de la fuente:
$_POST = fix(file_get_contents('php://input')); $_GET = fix($_SERVER['QUERY_STRING']); $_COOKIE = fix($_SERVER['HTTP_COOKIE']);
Para PHP por debajo de 5.4: use en
base64_encode
lugar debin2hex
y enbase64_decode
lugar dehex2bin
.fuente
a[2][5]=10
producearray(1) { ["a"]=> array(1) { [2]=> array(1) { [5]=> string(2) "10" } } }
.Esto sucede porque un punto es un carácter no válido en el nombre de una variable, la razón por la cual se encuentra muy profundamente en la implementación de PHP, por lo que no hay soluciones fáciles (todavía).
Mientras tanto, puede solucionar este problema de la siguiente manera:
php://input
de datos POST o$_SERVER['QUERY_STRING']
de datos GETLa siguiente función de conversión (PHP> = 5.4) codifica los nombres de cada par clave-valor en una representación hexadecimal y luego realiza una regular
parse_str()
; una vez hecho esto, revierte los nombres hexadecimales a su forma original:function parse_qs($data) { $data = preg_replace_callback('/(?:^|(?<=&))[^=[]+/', function($match) { return bin2hex(urldecode($match[0])); }, $data); parse_str($data, $values); return array_combine(array_map('hex2bin', array_keys($values)), $values); } // work with the raw query string $data = parse_qs($_SERVER['QUERY_STRING']);
O:
// handle posted data (this only works with application/x-www-form-urlencoded) $data = parse_qs(file_get_contents('php://input'));
fuente
bin2hex()
idea de la mía, así que, ¿podemos dejar esta pelea sin sentido?Este enfoque es una versión alterada de Rok Kralj, pero con algunos ajustes para que funcione, para mejorar la eficiencia (evita devoluciones de llamada innecesarias, codificación y decodificación en claves no afectadas) y para manejar correctamente las claves de matriz.
Se encuentra disponible una esencia con pruebas y cualquier comentario o sugerencia es bienvenida aquí o allá.
public function fix(&$target, $source, $keep = false) { if (!$source) { return; } $keys = array(); $source = preg_replace_callback( '/ # Match at start of string or & (?:^|(?<=&)) # Exclude cases where the period is in brackets, e.g. foo[bar.blarg] [^=&\[]* # Affected cases: periods and spaces (?:\.|%20) # Keep matching until assignment, next variable, end of string or # start of an array [^=&\[]* /x', function ($key) use (&$keys) { $keys[] = $key = base64_encode(urldecode($key[0])); return urlencode($key); }, $source ); if (!$keep) { $target = array(); } parse_str($source, $data); foreach ($data as $key => $val) { // Only unprocess encoded keys if (!in_array($key, $keys)) { $target[$key] = $val; continue; } $key = base64_decode($key); $target[$key] = $val; if ($keep) { // Keep a copy in the underscore key version $key = preg_replace('/(\.| )/', '_', $key); $target[$key] = $val; } } }
fuente
parse_str
llamada volverá a codificar url. ¿Qué estás intentando analizar que aún no está codificado?La razón por la que esto sucede es debido a la antigua funcionalidad register_globals de PHP. Los . El carácter no es un carácter válido en un nombre de variable, por lo que PHP lo convierte en un guión bajo para asegurarse de que haya compatibilidad.
En resumen, no es una buena práctica hacer puntos en las variables de URL.
fuente
Si busca alguna forma de hacer que PHP deje de reemplazar literalmente '.' caracteres en matrices $ _GET o $ _POST, entonces una de esas formas es modificar la fuente de PHP (y en este caso es relativamente sencillo).
ADVERTENCIA: ¡La modificación de la fuente PHP C es una opción avanzada!
También vea este informe de error de PHP que sugiere la misma modificación.
Para explorar, necesitará:
.
cheque de reemplazoEl cambio de fuente en sí es trivial e implica actualizar solo la mitad de una línea en
main/php_variables.c
:.... /* ensure that we don't have spaces or dots in the variable name (not binary safe) */ for (p = var; *p; p++) { if (*p == ' ' /*|| *p == '.'*/) { *p='_'; ....
Nota: en comparación con el original
|| *p == '.'
se ha comentadoSalida de ejemplo:
dado un QUERY_STRING de
a.a[]=bb&a.a[]=BB&c%20c=dd
, la ejecución<?php print_r($_GET);
ahora produce:Notas:
parse_str()
y (si no está disponible) recurrir a métodos más lentos.fuente
Mi solución a este problema fue rápida y sucia, pero todavía me gusta. Simplemente quería publicar una lista de nombres de archivo que se verificaron en el formulario. Solía
base64_encode
codificar los nombres de archivo dentro del marcado y luego simplemente los decodificababase64_decode
antes de usarlos.fuente
Después de ver la solución de Rok, se me ocurrió una versión que aborda las limitaciones en mi respuesta a continuación, crb arriba y también la solución de Rok. Vea mi versión mejorada .
La respuesta de @ crb anterior es un buen comienzo, pero hay un par de problemas.
La siguiente solución resuelve ambos problemas ahora (tenga en cuenta que se ha actualizado desde que se publicó originalmente). Esto es aproximadamente un 50% más rápido que mi respuesta anterior en mis pruebas, pero no manejará situaciones en las que los datos tienen la misma clave (o una clave que se extrae de la misma manera, por ejemplo, foo.bar y foo_bar se extraen como foo_bar).
<?php public function fix2(&$target, $source, $keep = false) { if (!$source) { return; } preg_match_all( '/ # Match at start of string or & (?:^|(?<=&)) # Exclude cases where the period is in brackets, e.g. foo[bar.blarg] [^=&\[]* # Affected cases: periods and spaces (?:\.|%20) # Keep matching until assignment, next variable, end of string or # start of an array [^=&\[]* /x', $source, $matches ); foreach (current($matches) as $key) { $key = urldecode($key); $badKey = preg_replace('/(\.| )/', '_', $key); if (isset($target[$badKey])) { // Duplicate values may have already unset this $target[$key] = $target[$badKey]; if (!$keep) { unset($target[$badKey]); } } } }
fuente
%20
también es un carácter especial que se convierte en subrayado. 2. Su código procesa previamente todos los datos, ya quepreg_match_all
tiene que escanear todo, aunque usted diga que no lo hace. 3. El código de falla en ejemplos como este:a.b[10]=11
.preg_match_all
tiene que "procesar" una cadena, no extraer y reprocesar todas las claves y valores no afectados, por lo que también está un poco desviado. Dicho esto, su enfoqueparse_string
parece un enfoque interesante que, con un poco de ajuste, podría ser mejor :)O(n)
. De hecho, está empeorando la complejidad al usar lain_array()
función, como se señaló anteriormente.in_array
todos modos. Además, si desea ejecutar algunas pruebas, notará que lo anterior sigue siendo significativamente más rápido; no O (n) vs O (n ^ 2), pero un enfoque lineal aún puede ser más rápido que otro ... y este es;)Bueno, la función que incluyo a continuación, "getRealPostArray ()", no es una solución bonita, pero maneja matrices y admite ambos nombres: "alpha_beta" y "alpha.beta":
<input type='text' value='First-.' name='alpha.beta[a.b][]' /><br> <input type='text' value='Second-.' name='alpha.beta[a.b][]' /><br> <input type='text' value='First-_' name='alpha_beta[a.b][]' /><br> <input type='text' value='Second-_' name='alpha_beta[a.b][]' /><br>
mientras que var_dump ($ _ POST) produce:
'alpha_beta' => array (size=1) 'a.b' => array (size=4) 0 => string 'First-.' (length=7) 1 => string 'Second-.' (length=8) 2 => string 'First-_' (length=7) 3 => string 'Second-_' (length=8)
var_dump (getRealPostArray ()) produce:
'alpha.beta' => array (size=1) 'a.b' => array (size=2) 0 => string 'First-.' (length=7) 1 => string 'Second-.' (length=8) 'alpha_beta' => array (size=1) 'a.b' => array (size=2) 0 => string 'First-_' (length=7) 1 => string 'Second-_' (length=8)
La función, por lo que vale:
function getRealPostArray() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') {#Nothing to do return null; } $neverANamePart = '~#~'; #Any arbitrary string never expected in a 'name' $postdata = file_get_contents("php://input"); $post = []; $rebuiltpairs = []; $postraws = explode('&', $postdata); foreach ($postraws as $postraw) { #Each is a string like: 'xxxx=yyyy' $keyvalpair = explode('=',$postraw); if (empty($keyvalpair[1])) { $keyvalpair[1] = ''; } $pos = strpos($keyvalpair[0],'%5B'); if ($pos !== false) { $str1 = substr($keyvalpair[0], 0, $pos); $str2 = substr($keyvalpair[0], $pos); $str1 = str_replace('.',$neverANamePart,$str1); $keyvalpair[0] = $str1.$str2; } else { $keyvalpair[0] = str_replace('.',$neverANamePart,$keyvalpair[0]); } $rebuiltpair = implode('=',$keyvalpair); $rebuiltpairs[]=$rebuiltpair; } $rebuiltpostdata = implode('&',$rebuiltpairs); parse_str($rebuiltpostdata, $post); $fixedpost = []; foreach ($post as $key => $val) { $fixedpost[str_replace($neverANamePart,'.',$key)] = $val; } return $fixedpost; }
fuente
Usando crb, quería recrear la
$_POST
matriz como un todo, aunque tenga en cuenta que aún tendrá que asegurarse de que está codificando y decodificando correctamente tanto en el cliente como en el servidor. Es importante comprender cuándo un personaje es verdaderamente inválido y realmente válido . Además la gente debe todavía y siempre escapar de los datos del cliente antes de usarlo con cualquier comando de la base de datos sin excepción .<?php unset($_POST); $_POST = array(); $p0 = explode('&',file_get_contents('php://input')); foreach ($p0 as $key => $value) { $p1 = explode('=',$value); $_POST[$p1[0]] = $p1[1]; //OR... //$_POST[urldecode($p1[0])] = urldecode($p1[1]); } print_r($_POST); ?>
Recomiendo usar esto solo para casos individuales, de repente, no estoy seguro de los puntos negativos de poner esto en la parte superior de su archivo de encabezado principal.
fuente
Mi solución actual (basada en respuestas al tema anterior):
function parseQueryString($data) { $data = rawurldecode($data); $pattern = '/(?:^|(?<=&))[^=&\[]*[^=&\[]*/'; $data = preg_replace_callback($pattern, function ($match){ return bin2hex(urldecode($match[0])); }, $data); parse_str($data, $values); return array_combine(array_map('hex2bin', array_keys($values)), $values); } $_GET = parseQueryString($_SERVER['QUERY_STRING']);
fuente