Si paso campos de solicitud o cookies con un punto/punto en sus nombres, PHP los reemplaza automáticamente con guiones bajos. Por ejemplo, si pongo este código en https://example.com/test.php?x.y=a.b
:
<?php
echo $_SERVER['REQUEST_URI'];
echo $_GET['x.y'];
echo $_GET['x_y'];
la salida es:
/test.php?x.y=a.b
a.b
Está ahí cualquier forma en que puedo prevenir este comportamiento?
paige ruten
Aquí está la explicación de PHP.net de por qué lo hace:
Puntos en nombres de variables entrantes
Por lo general, PHP no altera los nombres de las variables cuando se pasan a un script. Sin embargo, debe tenerse en cuenta que el punto (punto, punto final) no es un carácter válido en un nombre de variable de PHP. Por la razón, míralo:
<?php $varname.ext; /* invalid variable name */ ?>
Ahora, lo que ve el analizador es una variable llamada $varname, seguida por el operador de concatenación de cadenas, seguido por la cadena desnuda (es decir, una cadena sin comillas que no coincide con ninguna clave conocida o palabras reservadas) ‘ext’. Obviamente, esto no tiene el resultado esperado.
Por esta razón, es importante tener en cuenta que PHP reemplazará automáticamente cualquier punto en los nombres de las variables entrantes con guiones bajos.
eso es de http://ca.php.net/variables.external.
También, según este comentario estos otros caracteres se convierten en guiones bajos:
La lista completa de caracteres de nombre de campo que PHP convierte a _ (guion bajo) es la siguiente (no solo un punto):
- chr(32) ( ) (espacio)
- chr(46) (.) (punto)
- cro(91) ([) (open square bracket)
- chr(128) – chr(159) (various)
So it looks like you’re stuck with it, so you’ll have to convert the underscores back to dots in your script using dawnerd’s suggestion (I’d just use str_replace though.)
-
This is a great explanation of why, but doesn’t answer the original question of “is there any way to get it to stop”; other answers below do provide an answer to the original question.
– El YoboAug 10, 2013 at 15:38
-
@ElYobo, @JeremyRuten; good explanation of why? I’m using PHP 5.4 and PHP is still doing this. I’d also love to know why its not deprecated yet. I can only see two reasons for keeping it; register_globals (deprecated since 5.3), and for convenience in ~doing what register globals does manually (in which case the burden should be on the person doing that to map var names how they see fit IMO).
– spinkusMar 23, 2014 at 2:03
-
Backwards compatibility I assume? Good point, with register globals going the way of the dodo this strange “functionality” could go likewise.
– El YoboApr 23, 2014 at 6:49
-
With php7, register globals already rode off into the sunset but the problem is still present.
– magallanesJan 17, 2016 at 22:55
crb
Long-since answered question, but there is actually a better answer (or work-around). PHP lets you at the raw input stream, so you can do something like this:
$query_string = file_get_contents('php://input');
which will give you the $_POST array in query string format, periods as they should be.
You can then parse it if you need (as per POSTer’s comment)
<?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 ($pares como $par) { $nv = explotar("=", $par); $nombre = urldecode($nv[0]); $valor = urldecode($nv[1]); $vars[$name] = $valor; } devuelve $vars; } // Funciones de contenedor específicamente para GET y POST: function getRealGET() { return getRealInput('GET'); } function getRealPOST() { return getRealInput('POST'); } ?>
Muy útil para los parámetros de OpenID, que contienen tanto ‘.’ y ‘_’, ¡cada uno con un cierto significado!
-
Para hacer que esto funcione con parámetros GET, reemplace
file_get_contents("php://input")
con$_SERVER['QUERY_STRING']
.– Sarel Botha
27 de septiembre de 2012 a las 22:22
-
Y puede hacer lo mismo para las cookies usando
$_SERVER['COOKIES']
– Marcin
14/10/2012 a las 16:09
-
Este es un buen comienzo, pero hay un par de problemas con él. No maneja valores de matriz (por ejemplo, foo.bar[]=blarg no terminará como una matriz, terminará como una variable escalar llamada foo.bar[]). También tiene muchos gastos generales ya que reprocesa todos los valores, independientemente de si hay un período en ellos o no.
– El Yobo
3 de agosto de 2013 a las 1:45
-
Vea mi solución a continuación, que soluciona los problemas con la implementación de Rok.
– El Yobo
10 de agosto de 2013 a las 15:37
-
Por alguna razón $query_string = file_get_contents(‘php://input’); me devuelve una cadena vacía.
– Cris Prince
27 de febrero de 2014 a las 2:07
Destacando 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 necesidad de un procesamiento pesado.
En la forma que haces
<input name="data[database.username]">
<input name="data[database.password]">
<input name="data[something.else.really.deep]">
en lugar de
<input name="database.username">
<input name="database.password">
<input name="something.else.really.deep">
y en el controlador de publicación, simplemente desenvuélvalo:
$posdata = $_POST['data'];
Para mí, esto fue un cambio de dos líneas, ya que mis puntos de vista estaban totalmente basados en plantillas.
para tu información Estoy usando puntos en los nombres de mis campos para editar árboles de datos agrupados.
-
De hecho, es una solución muy elegante y práctica, con el beneficio adicional de mantener los datos del formulario bien espaciados.
– robinmitra
13 mayo 2015 a las 20:20
-
Esto resuelve completamente el problema y debería haber sido la respuesta aceptada.
– Brian Klug
26 mayo 2019 a las 17:52
Rok Kralj
¿Quieres una solución que sea conforme a las normas, y trabaja con arreglos profundos (Por ejemplo: ?param[2][5]=10
) ?
Para solucionar todas las fuentes posibles de este problema, puede aplicar en la parte superior de su código PHP:
$_GET = fix( $_SERVER['QUERY_STRING'] );
$_POST = fix( file_get_contents('php://input') );
$_COOKIE = fix( $_SERVER['HTTP_COOKIE'] );
El funcionamiento de esta función es una buena idea que se me ocurrió durante mis vacaciones de verano de 2013. No se desanime por una simple expresión regular, simplemente toma todos los nombres de las consultas, los codifica (para que se conserven los puntos) y luego usa una normal parse_str()
función.
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;
}
Jacobo
Esto sucede porque un punto es un carácter no válido en el nombre de una variable, el razón para lo cual se encuentra muy profundo 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:
- Acceder a los datos de consulta sin procesar a través de
php://input
para datos POST o$_SERVER['QUERY_STRING']
para obtener datos - Usando una función de conversión.
La 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 operación 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'));
-
Sin embargo, ¿qué pasaría si esto necesitara usarse para otra cosa que se envió y realmente necesito el _ en la variable?
– robo
21 de enero de 2013 a las 5:01
-
@Rob He agregado el resultado en función de su pregunta; funciona como se esperaba, porque no toco los guiones bajos.
– Jacobo
21 de enero de 2013 a las 5:02
-
Nota: Esta es una solución editada que luego copió mi código y mi idea (ver registro de cambios). Debería ser eliminado por los moderadores.
– Rok Kralj
22 de julio de 2014 a las 22:25
-
Aparentemente fue lo suficientemente bueno para ti tomar el
bin2hex()
una idea mía, así que ¿podemos dejar esta enemistad sin sentido?– Jacobo
23 de julio de 2014 a las 0:38
-
@RokKralj Espero que no estés insinuando que mi respuesta anterior estaba perfectamente bien; seguro que parecía estar bien hasta que me di cuenta de un descuido importante que se solucionó en una serie de ediciones, la última compartiendo la misma expresión regular para hacer coincidir los nombres en una cadena codificada de URL; Sinceramente, no puedo pensar en otra forma de escribir esa expresión, o lo habría hecho ahora para detener las acusaciones.
– Jacobo
23 de julio de 2014 a las 8:30
Este enfoque es una versión alterada del 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.
A esencia con las pruebas está disponible y cualquier comentario o sugerencia es bienvenido 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;
}
}
}
-
Sin embargo, ¿qué pasaría si esto necesitara usarse para otra cosa que se envió y realmente necesito el _ en la variable?
– robo
21 de enero de 2013 a las 5:01
-
@Rob He agregado el resultado en función de su pregunta; funciona como se esperaba, porque no toco los guiones bajos.
– Jacobo
21 de enero de 2013 a las 5:02
-
Nota: Esta es una solución editada que luego copió mi código y mi idea (ver registro de cambios). Debería ser eliminado por los moderadores.
– Rok Kralj
22 de julio de 2014 a las 22:25
-
Aparentemente fue lo suficientemente bueno para ti tomar el
bin2hex()
una idea mía, así que ¿podemos dejar esta enemistad sin sentido?– Jacobo
23 de julio de 2014 a las 0:38
-
@RokKralj Espero que no estés insinuando que mi respuesta anterior estaba perfectamente bien; seguro que parecía estar bien hasta que me di cuenta de un descuido importante que se solucionó en una serie de ediciones, la última compartiendo la misma expresión regular para hacer coincidir los nombres en una cadena codificada de URL; Sinceramente, no puedo pensar en otra forma de escribir esa expresión, o lo habría hecho ahora para detener las acusaciones.
– Jacobo
23 de julio de 2014 a las 8:30
jeremy privett
La razón por la que esto sucede es por la antigua función de registro global de PHP. El . 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 poner puntos en las variables de URL.
-
Tampoco es una buena idea tener register_globals activado. De hecho, debería apagarse ahora mismo si es posible.
– amanecer
16 de septiembre de 2008 a las 1:57
-
register_globals está de hecho desactivado, como está predeterminado en PHP5. > El . El carácter no es un carácter válido en un nombre de variable Desafortunadamente, no busco usar esto como un nombre de variable (lo mantengo como una clave en el diccionario $_GET), por lo que esta ‘consideración’ en PHP no agrega valor 🙁 Ah bueno…
– Dave Carpeneto
16 de septiembre de 2008 a las 2:03
-
No importa si register_globals está activado o desactivado. PHP aún realiza los reemplazos.
– Jeremy Privett
16 de septiembre de 2008 a las 2:09
.. ¿Por qué no conviertes todos los puntos en algún tipo de token, como por ejemplo, en (~#~) y luego lo publicas? Al recibir los vars, puede volver a convertirlos… Esto se debe a que a veces NECESITAMOS publicar guiones bajos… y los perderíamos si reconvirtiéramos todos los “_” a “.”s…
– Fernando
22 de septiembre de 2011 a las 19:20
Desde la consulta de recuperación en sí, puede concatenar el nombre de usuario como “concat (nombre, ‘_’, apellido) como nombre de usuario.
– Kaspar María
21 de enero de 2013 a las 4:49
@Kaspar Mary … la base de datos está configurada para tener columnas de nombre de usuario y estado y los nombres de usuario se almacenan como nombre.apellido, por lo que no puedo usar ningún concat en sql ya que ya están concatenados con un .
– robo
21 de enero de 2013 a las 5:00
@Crisp ¡Gracias por el comentario! (at) Rob problema interesante
– hek2mgl
21 de enero de 2013 a las 5:01
¿Por qué no hay un comentario de eliminación? 🙂
– Cris Prince
27 de febrero de 2014 a las 2:06