2010-05-28 11 views
5

Me encontré con un problema hoy en el que estaba pasando una clave con el valor establecido en una matriz vacía a http_build_query(). Ej .:http_build_query ignora la clave si el valor es una matriz vacía. ¿Cómo es esto no es un error?

$args = array("foo", "bar", array(), "baz"); 
$qs = http_build_query($args); 
echo $qs; // outputs 0=foo&1=bar&3=baz, I expected 0=foo&1=bar&2=&3=baz 

Esto presenta un problema para mí, ya que estoy pasando algunos datos a una API interna a través de HTTP y tengo que tirar de todos los argumentos a cabo en el otro lado.

Bueno, busqué en Google esto y se me ocurrió el siguiente informe de error: http://bugs.php.net/bug.php?id=50407. La breve respuesta de un administrador es: "No establecer es lo mismo que configurarlo vacío. No hay errores".

¿Alguien puede explicarme por qué esto no es un error? ¿Alguien tiene alguna idea para una solución, aparte del truco de establecer un valor arbitrario en un lado e interpretar ese valor como un valor vacío en el otro?

EDITAR

Aquí es por lo que creo que es un error:

$args = array("foo", "bar", array(), "baz"); 
$qs = http_build_query($args); 
parse_str($qs, $query); 
echo ($args == $query); // false, I expect it to be true 

supongo que tal vez sea ingenuo de mi parte a considerar parse_str() y http_build_query() a ser inversos el uno del otro.

Estoy publicando mi solución actual de "lame hack" como respuesta a continuación.

+0

Entonces, ¿vas a decir que tu API confiará ciegamente en que los datos provienen de HTTP sin validación? Suena interesante ... –

+2

no parece que diga nada en absoluto sobre lo que sucederá con los datos dentro de la API. – Johrn

+3

@Col. Shrapnel: Eso es todo un salto. Solo quiere poder pasar una variable con un valor vacío. – webbiedave

Respuesta

2

¿Alguien puede explicarme por qué esto es no es un error?

Técnicamente, no creo que deba etiquetarse como un error. Más bien, es solo cómo diseñaron la función para comportarse, ya sea que otros estén en desacuerdo con esa decisión o no.

Su API solo pudo comprobar con if (empty($_POST['2']))

+0

Acabo de editar mi pregunta con más detalles acerca de por qué creo que es un error. – jsdalton

+0

No veo cómo su código agregado lo redefine como un error. Es una decisión de comportamiento con la que usted y muchos otros no están de acuerdo. – webbiedave

+0

Bastante justo. "Error" es quizás la elección incorrecta de las palabras. Es una decisión de diseño deficiente IMO, porque creo que las funciones que codifican y decodifican datos deben ser reversibles (cuando sea posible al menos). – jsdalton

0

Ésta es mi solución actual "truco cojo". Tenga en cuenta que tenía que tener en cuenta la posibilidad de matrices anidadas, por lo que mi ejemplo matriz original es ligeramente diferente de lo que he publicado en la pregunta:

$args = array("foo", "bar", array("red", "blue", array(), "green"), "baz"); 
$original_array = $args; // save it to compare later 
function replace_empty_array_with_fake_string(&$value, $key) { 
    if (is_array($value)) { 
     if (empty($value)) { 
      $value = 'array()'; 
     } else { 
      array_walk($value, 'replace_empty_array_with_fake_string'); 
     } 

    } 
} 
array_walk($args, 'replace_empty_array_with_fake_string'); 
$qs = http_build_query($args); 

// convert the query string back to an array, this would happen on the "other side" 
parse_str($qs, $query); 
function replace_fake_string_with_empty_array(&$value, $key) { 
    if ($value == 'array()') { 
     $value = array(); 
    } 
    if (is_array($value)) { 
     array_walk($value, 'replace_fake_string_with_empty_array'); 
    } 
} 
array_walk($query, 'replace_fake_string_with_empty_array'); 
echo ($original_array == $query); // true 

Es de suponer que podría subir con una cadena más arbitraria que "array() "para usar como marcador de posición.

Lame, lo sé.

+0

Sería más corto simplemente reimplementar http_build_query. Estaba a punto de recomendar http_build_str() desde la extensión pecl_http. Pero eso tampoco funciona allí. – mario

+0

cualquier cuerpo conoce la diferencia entre http_build_query y http_build_str ?? –

1

He vuelto a implementar http_build_query para dejar objetos/matrices vacíos en la cadena de consulta devuelta (con el sufijo '='). He aumenté un poco de la funcionalidad por defecto, así que todo en todo:

  • Mantiene objetos vacíos y matrices
  • cambiado la enc_type por defecto a RFC3986 (correspondiente a las edades)
  • Se ha añadido un argumento separador de clave-valor (capacidad de anular el valor por defecto '=')
  • Elimina índices numéricos para los pares de clave y valor indexado numéricamente-

no he probado esto en un entorno de producción (sin idea acerca perfor mance o bugs), y no está optimizado, pero está muy bien explicado.

function buildQuery($input,$numeric_prefix='', 
     $arg_separator='&',$enc_type=2, 
     $keyvalue_separator='=',$prefix='') { 
    if (is_array($input)) { 
     $arr = array(); 
     foreach ($input as $key => $value) { 
      $name = $prefix; 
      if (strlen($prefix)) { 
       $name .= '['; 
       if (!is_numeric($key)) { 
        $name .= $key; 
       } 
       $name .= ']'; 
      } else { 
       if (is_numeric($key)) { 
        $name .= $numeric_prefix; 
       } 
       $name .= $key; 
      } 
      if ((is_array($value) || is_object($value)) && count($value)) { 
       $arr[] = buildQuery($value,$numeric_prefix, 
         $arg_separator,$enc_type, 
         $keyvalue_separator,$name); 
      } else { 
       if ($enc_type === 2) { 
        $arr[] = rawurlencode($name) 
         .$keyvalue_separator 
         .rawurlencode($value?:''); 
       } else { 
        $arr[] = urlencode($name) 
         .$keyvalue_separator 
         .urlencode($value?:''); 
       } 
      } 
     } 
     return implode($arg_separator,$arr); 
    } else { 
     if ($enc_type === 2) { 
      return rawurlencode($input); 
     } else { 
      return urlencode($input); 
     } 
    } 
} 

Ejemplo:

$arr = array(
     'hello' => 'world', 
     'colors' => array('red','green','blue'), 
     'emptyArr' => array(), 
     'nested' => array(
      'empty' => array(), 
      'fruits' => array('orange','banana','apple'), 
      'curly' => 'sue', 
      'assoc' => array('a'=>'alpha','b'=>'bravo','c'=>'charlie') 
     ) 
    ); 

echo buildQuery($arr); 

Salidas: hello=world&colors%5B%5D=red&colors%5B%5D=green&colors%5B%5D=blue&emptyArr=&nested%5Bempty%5D=&nested%5Bfruits%5D%5B%5D=orange&nested%5Bfruits%5D%5B%5D=banana&nested%5Bfruits%5D%5B%5D=apple&nested%5Bcurly%5D=sue&nested%5Bassoc%5D%5Ba%5D=alpha&nested%5Bassoc%5D%5Bb%5D=bravo&nested%5Bassoc%5D%5Bc%5D=charlie espero que este encuentre bien a alguien.

0

Usted puede simplemente caminar por los Parámetros de consulta, si matriz vacía, utilice "[]" lugar, como este:

function walkCriteria(&$criteria) { 
    array_walk($criteria, function (&$val) { 
     if ($val === []) { 
      $val = "[]"; 
     } else if (is_array($val)) { 
      walkCriteria($val); 
     } 
    }); 
} 

No utilice array_walk_recursive. Porque caminará en la matriz vacía y no hará nada.

Cuestiones relacionadas