5

I know what you're thinking. "I can answer this question so quick! It's the parse_str function!" But I've got a little twist for you.

You see, PHP converts the names of all query data into valid PHP variable names. What? Even when they're not being stored as variables? Where is that documented? It isn't.

$payload = 'data.name=value&data.other=value2';
parse_str($payload, $values);
var_export($values);
// Outputs:  array('data_name' => 'value', 'data_other' => 'value2')
// Expected: array('data.name' => 'value', 'data.other' => 'value2')

How can I parse a query string without this happening?

6
  • in this particular example, what do you expect/final output to get anyway? Commented Nov 26, 2014 at 0:01
  • Added the Expected: line for you. Commented Nov 26, 2014 at 0:05
  • I have this fails and more.. And after I'm migrate to JSON configs. Commented Nov 26, 2014 at 0:11
  • 1
    if it seems that it doesn't do it the way you wanted it, then you could just write/roll up your own. Commented Nov 26, 2014 at 0:14
  • You've also provided a weak input test case. What are the key value pairs separated by? Commented Nov 26, 2014 at 0:15

2 Answers 2

3

You can avoid PHP's renaming of parameter names by parsing the query string yourself. Split the string on & and = characters, and then urldecode() each part:

<?php

  $payload = 'data.name=value&data.other=value2';

  $values = array();

  $nv_strings = explode ('&', $payload);
  foreach ($nv_strings as $s) {
    $nv = explode ('=', $s, 2);
    $name = urldecode ($nv[0]);
    $value = (isset ($nv[1]) ? urldecode ($nv[1]) : null);
    $values[$name] = $value;
  }

  print_r ($values);

//  Array
//  (
//      [data.name] => value
//      [data.other] => value2
//  )

If your query string contains duplicate keys, the above code will overwrite the previous keys. This also can be solved:

<?php

  $payload = 'key=val1&key=val2&key=val3';

  $values = array();

  $nv_strings = explode ('&', $payload);
  foreach ($nv_strings as $s) {
    $nv = explode ('=', $s, 2);
    $name = urldecode ($nv[0]);
    $value = (isset ($nv[1]) ? urldecode ($nv[1]) : null);
    $values[] = array ($name => $value);
  }

  print_r ($values);

//  Array
//  (
//      [0] => Array ([key] => val1)
//      [1] => Array ([key] => val2)
//      [2] => Array ([key] => val3)
//  )

You may want to consolidate the duplicate keys into subarrays:

<?php

  $payload = 'key1=val1&key1=val2&key2=val3';

  $values = array();

  $nv_strings = explode ('&', $payload);
  foreach ($nv_strings as $s) {
    $nv = explode ('=', $s, 2);
    $name = urldecode ($nv[0]);
    $value = (isset ($nv[1]) ? urldecode ($nv[1]) : null);
    if (isset ($values[$name])) {
      if (is_array ($values[$name])) {
        $values[$name][] = $value;
      } else {
        $values[$name] = array ($values[$name], $value);
      }
    } else {
      $values[$name] = $value;
    }
  }

  print_r ($values);

//  Array
//  (
//      [key1] => Array ([0] => val1
//                       [1] => val2)
//      [key2] => val3
//  )

You can simplify the code somewhat by always using subarrays:

<?php

  $payload = 'key1=val1&key1=val2&key2=val3';

  $values = array();

  $nv_strings = explode ('&', $payload);
  foreach ($nv_strings as $s) {
    $nv = explode ('=', $s, 2);
    $name = urldecode ($nv[0]);
    $value = (isset ($nv[1]) ? urldecode ($nv[1]) : null);
    if (isset ($values[$name])) {
      $values[$name][] = $value;
    } else {
      $values[$name] = array ($value);
    }
  }

  print_r ($values);

//  Array
//  (
//      [key1] => Array ([0] => val1
//                       [1] => val2)
//      [key2] => Array ([0] => val3)
//  )
Sign up to request clarification or add additional context in comments.

Comments

1

It is documented completely in PHP Documentation for the function parse_str If the output does not work for your particular use case, then you must use another function, or create your own. The key here is that this function parses the string into variables.

parse_str — Parses the string into variables Parses str as if it were the query string passed via a URL and sets variables in the current scope.

To parse the string you provided, this would work:

$payload = 'data.name=value';
$map = array();
$vals = preg_split('/=/', $payload);
$i= 0;
while($i < count($vals)) {
  $map[$vals[$i]] = $vals[++$i];
  $i++;
}

var_dump($map);

3 Comments

Your quote just says it parses the string the same as a query passed via a URL. Is it documented that the behavior I describe applies to that string too?
@meustrus Re read it. It parses the string into variables in the current scope. so, $values['data_name'] would exist.
This is a bizarre way of doing it. What's wrong with explode()? How do you split up multiple params separated by &? Why the complicated loop with iteration going on in two places when a foreach would suffice?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.