Best arrays questions in November 2011

Strange behaviour after loop by reference - Is this a PHP bug?

62 votes

I just had some very strange behavior with a simple php script I was writing. I reduced it to the minimum necessary to recreate the bug:

<?php

$arr = array("foo",
             "bar",
             "baz");

foreach ($arr as &$item) { /* do nothing by reference */ }
print_r($arr);

foreach ($arr as $item) { /* do nothing by value */ }
print_r($arr); // $arr has changed....why?

?>

This outputs:

Array
(
    [0] => foo
    [1] => bar
    [2] => baz
)
Array
(
    [0] => foo
    [1] => bar
    [2] => bar
)

Is this a bug or some really strange behavior that is supposed to happen?

After the first foreach loop, $item is still a reference to some value which is also being used by $arr[2]. So each foreach call in the second loop, which does not call by reference, replaces that value, and thus $arr[2], with the new value.

So loop 1, the value and $arr[2] become $arr[0], which is 'foo'.
Loop 2, the value and $arr[2] become $arr[1], which is 'bar'.
Loop 3, the value and $arr[2] become $arr[2], which is 'bar' (because of loop 2).

The value 'baz' is actually lost at the first call of the second foreach loop.

See this debug page.

This is the behavior of a referenced item, and not a bug. It would be similar to running something like:

for ($i = 0; $i < count($arr); $i++) { $item = $arr[$i]; }

A foreach loop isn't special in nature in which it can ignore referenced items. It's simply setting that variable to the new value each time like you would outside of a loop.

PHP url to array

11 votes

I have these urls from a web log:

http://www.domain.com/page?s=194&client=151678&m=a4a&v=1&g=54
http://www.domain.com/page?s=51&client=617171&m=b4z&v=8&g=97

How can I convert this URL in an array in PHPso i will have an array like this

array(
 'page' => array(
   's' => 194,
   'client' => 151678
   'm' => 'a4a',
   'v' => 1,
   'g' => 54
 )
 etc..
)

then later I can loop to that array to find duplicates or validate the information correctly.

There may be a better way to do this but this is what I came up with.

<?php

    $url = 'http://www.domain.com/page?s=194&client=151678&m=a4a&v=1&g=54';
    $remove_http = str_replace('http://', '', $url);
    $split_url = explode('?', $remove_http);
    $get_page_name = explode('/', $split_url[0]);
    $page_name = $get_page_name[1];

    $split_parameters = explode('&', $split_url[1]);

    for($i = 0; $i < count($split_parameters); $i++) {
        $final_split = explode('=', $split_parameters[$i]);
        $split_complete[$page_name][$final_split[0]] = $final_split[1];
    }

    var_dump($split_complete);

?>

http://codepad.org/xTsAks46

How to split string to 2D array with Regex?

6 votes

I've got a problem that seems simple on the face of it but has defeated my meager regex skills. I have a string that I need to convert to an array and then process the values accordingly, which is simple enough, but the format of the string cannot be changed (it is generated elsewhere) and the logic of it has me baffled.

The string is:

[6] [2] [3] 12.00; [5] [4]

It's basically a set of ids and decimal values (in this case id 3 == 12.00). The quantity of ids could change at any moment and decimal values could be in any or all of the ids.

In an ideal world I would have the following array:

Array (
   [0] => Array (
             [id]  => 6
             [num] => 
          )
   [1] => Array (
             [id]  => 2
             [num] => 
          ) 
   [2] => Array (
             [id]  => 3
             [num] => 12.00 
          )
   Etc...

Do any of you regex wizards know how this can be accomplished with less swearing than I've been able to achieve?

I have thus far been able to extract the id's using:

preg_match_all('@\[(.*?)\]@s', $string, $array);

and the decimals using:

preg_match_all('/([0-9]+[,\.]{1}[0-9]{2})/', $string, $array);

but lose the correlation between id's and values.

Example:

<?php

$string = '[6] [2] [3] 12.00; [5] [4]';

preg_match_all('/\[(?P<id>\d+)\](?: (?P<num>[\d\.]+);)?/', $string, $matches, PREG_SET_ORDER);

var_dump($matches);

Output:

array(5) {
  [0]=>
  array(3) {
    [0]=>
    string(3) "[6]"
    ["id"]=>
    string(1) "6"
    [1]=>
    string(1) "6"
  }
  [1]=>
  array(3) {
    [0]=>
    string(3) "[2]"
    ["id"]=>
    string(1) "2"
    [1]=>
    string(1) "2"
  }
  [2]=>
  array(5) {
    [0]=>
    string(10) "[3] 12.00;"
    ["id"]=>
    string(1) "3"
    [1]=>
    string(1) "3"
    ["num"]=>
    string(5) "12.00"
    [2]=>
    string(5) "12.00"
  }
  [3]=>
  array(3) {
    [0]=>
    string(3) "[5]"
    ["id"]=>
    string(1) "5"
    [1]=>
    string(1) "5"
  }
  [4]=>
  array(3) {
    [0]=>
    string(3) "[4]"
    ["id"]=>
    string(1) "4"
    [1]=>
    string(1) "4"
  }
}