Pregunta

Si paso variables PHP con . en sus nombres a través de $_GET PHP los 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 esta:está ahí cualquier ¿Cómo puedo hacer que esto se detenga?No puedo ni por mi vida descubrir qué he hecho para merecer esto.

La versión de PHP que estoy ejecutando es 5.2.4-2ubuntu5.3.

¿Fue útil?

Solución

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 guión.Sin embargo, debe tenerse en cuenta que el punto (período, parada completa) no es un carácter válido en un nombre de variable 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 del operador de concatenación de cadenas, seguido de la barra (es decirCadena no cotizada que no coincide con ninguna clave conocida o palabras reservadas) 'Ext'.Obviamente, esto no tiene el resultado previsto.

Por esta razón, es importante tener en cuenta que PHP reemplazará automáticamente cualquier punto en los nombres variables entrantes con subrayos.

eso es de http://ca.php.net/variables.external.

Además, 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 _ (guión bajo) es la siguiente (no solo punto):

  • chr(32) ( ) (espacio)
  • chr(46) (.) (punto)
  • chr(91) ([) (corchete abierto)
  • chr(128) - chr(159) (varios)

Entonces parece que estás atrapado en esto, por lo que tendrás que convertir los guiones bajos nuevamente en puntos en tu secuencia de comandos usando sugerencia de Dawnerd (Yo solo usaría str_replace aunque.)

Otros consejos

Pregunta respondida hace mucho tiempo, pero en realidad hay una mejor respuesta (o solución alternativa).PHP te permite en el flujo de entrada sin procesar, entonces puedes hacer algo como esto:

$query_string = file_get_contents('php://input');

lo que le dará la matriz $_POST en formato de cadena de consulta, con los puntos como deberían ser.

Luego puede analizarlo si lo necesita (según comentario del 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'); }
?>

Enormemente útil para los parámetros de OpenID, que contienen ambos '.' ¡Y '_', cada uno con cierto significado!

Destacando una respuesta real de Johan en un comentario anterior: simplemente envolví 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 lo 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 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.

Para su información.Estoy usando puntos en los nombres de mis campos para editar árboles de datos agrupados.

El funcionamiento de esta función es un truco genial que se me ocurrió durante mis vacaciones de verano en 2013.Algún día escribiré una entrada en el blog sobre ello.

Esta solución funciona universalmente y tiene soporte de matriz profunda, por ejemplo a.a[x][b.a]=10.Usa parse_str() detrás de escena con algo de preprocesamiento.

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 llamar a esta función 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: usar base64_encode en lugar de bin2hex y base64_decode en lugar de hex2bin.

Esto sucede porque un punto es un carácter no válido en el nombre de una variable, el razón Esto se encuentra muy profundamente en la implementación de PHP, por lo que no hay soluciones fáciles (todavía).

Mientras tanto, puedes solucionar este problema de la siguiente manera:

  1. Acceder a los datos de consulta sin procesar a través de cualquiera de los dos php://input para datos POST o $_SERVER['QUERY_STRING'] para OBTENER datos
  2. 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 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'));

Este enfoque es una versión modificada del de Rok Kralj, pero con algunos ajustes para funcionar, mejorar la eficiencia (evita devoluciones de llamadas innecesarias, codificación y decodificación en claves no afectadas) y para manejar correctamente las claves de matriz.

A esencia con 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;                                              
        }                                                                      
    }                                                                          
}                                                                              

La razón por la que esto sucede es por la antigua funcionalidad Register_globals 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 incluir puntos en las variables de URL.

si buscas cualquier manera de literalmente Haga que PHP deje de reemplazar '.' Los caracteres en las matrices $ _get o $ _post, entonces una de este tipo es modificar la fuente de PHP (y en este caso es relativamente sencillo).

ADVERTENCIA:¡Modificar la fuente PHP C es una opción avanzada!

También mira esto Informe de error de PHP lo que sugiere la misma modificación.

Para explorar necesitarás:

  • descargar Código fuente C de PHP
  • desactivar el . cheque de reemplazo
  • ./configurar, hacer e implemente su compilación personalizada de PHP

El 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:comparado con el original || *p == '.' ha sido comentado


Salida de ejemplo:

dado un QUERY_STRING de a.a[]=bb&a.a[]=BB&c%20c=dd, correr <?php print_r($_GET); ahora produce:

Array
(
    [a.a] => Array
        (
            [0] => bb
            [1] => BB
        )

    [c_c] => dd
)

Notas:

  • este parche aborda únicamente la pregunta original (detiene el reemplazo de puntos, no de espacios).
  • ejecutar este parche será más rápido que las soluciones a nivel de script, pero esas respuestas puras de .php siguen siendo generalmente preferibles (porque evitan cambiar PHP).
  • en teoría, aquí es posible un enfoque de polirelleno y podría combinar enfoques: pruebe el cambio de nivel C utilizando parse_str() y (si no está disponible) recurrir a métodos más lentos.

Mi solución a este problema fue rápida y sucia, pero aún así me gusta.Simplemente quería publicar una lista de nombres de archivos que se verificaron en el formulario.solía base64_encode para codificar los nombres de archivos dentro del marcado y luego simplemente decodificarlos con base64_decode antes de usarlos.

Después de analizar la solución de Rok, se me ocurrió una versión que aborda las limitaciones de mi respuesta a continuación, la de crb anterior y también la solución de Rok.Ver un mi versión mejorada.


La respuesta de @crb arriba Es un buen comienzo, pero hay un par de problemas.

  • Lo reprocesa todo, lo cual es excesivo;Solo aquellos campos que tienen un "." en el nombre debe ser reprocesado.
  • No logra manejar matrices de la misma manera que lo hace el procesamiento PHP nativo, p.para claves como "foo.bar[]".

La siguiente solución aborda ambos problemas ahora (tenga en cuenta que se actualizó desde su publicación original).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 tengan la misma clave (o una clave que se extraiga de la misma manera, p. ej.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]);                                       
            }                                                                  
        }                                                                      
    }                                                                          
}                                                                              

Bueno, la función que incluyo a continuación, "getRealPostArray()", no es una solución bonita, pero maneja matrices y admite ambos nombres:"alfa_beta" y "alfa.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;
}

Usando crb quería recrear el $_POST Sin embargo, tenga en cuenta que aún tendrá que asegurarse de codificar y decodificar correctamente tanto en el cliente como en el servidor.Es importante entender cuando un personaje es realmente inválido y es verdaderamente válido.Además la gente debería aún y siempre escapar de los datos del cliente antes de usarlos con cualquier comando de 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.

Mi solución actual (basada en las respuestas del 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']);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top