Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP behavior and arrays pointers

I was reading the PHP manual (specifically the each() function) and came across the following warning:

Caution
Because assigning an array to another variable resets the original array's pointer, our example above would cause an endless loop had we assigned $fruit to another variable inside the loop.

And an example:

<?php
$fruit = array('a' => 'apple', 'b' => 'banana', 'c' => 'cranberry');

reset($fruit);
while (list($key, $val) = each($fruit)) {
    echo "$key => $val\n";
}
?>

Okay. It makes sense. But I decided to do a simple test:

<?php
    $fruit = array('a' => 'apple', 'b' => 'banana', 'c' => 'cranberry');

    foreach ($fruit as $key => $name) {
        printf("[%s] => [%s]\n", $key, $name);
    }

    $fruit2 = $fruit;
    echo current($fruit);
?>

The result is expected: the pointer has been reset. My question is if the pointer is reset only after the end of the array?

For example:

<?php
    $fruit = array('a' => 'apple', 'b' => 'banana', 'c' => 'cranberry');

    foreach ($fruit as $key => $name) {
        printf("[%s] => [%s]\n", $key, $name);
    }

    reset($fruit);
    next($fruit)."\n";
    $fruit2 = $fruit;
    echo current($fruit);
?>

The pointer remains in the second array element ('b' => 'banana'). This behavior is characteristic of language?

Thank you and sorry for bad English.

like image 430
Luis Avatar asked Mar 16 '23 22:03

Luis


2 Answers

This behavior is characteristic of language?

The meaning of "pointer" in PHP arrays is not the same as the general meaning of "pointer" (in C/C++ or other languages that gives the programmer direct access to memory).

There are no pointers in PHP. The array data type keeps internally a cursor inside the list of values it contains. It is called the internal pointer of the array and it is modified by functions reset(), next(), prev(), end(), each() and maybe others. It can be used to iterate over the array like this:

$array = array(1, 2, 3);
while (list($key, $val) = each($array)) {
    echo($key.' => '.$val."\n");
}

There is no reliable way to iterate the array using next() or prev() because they return FALSE when there are no more elements to iterate but they also return FALSE when the value FALSE is stored as an element in the array.

They could be useful if you need to analyze only several items from the beginning (or end) of the array. F.e. let's say we have an array of integers returned by a function and we need to get the first value that is not zero.

But this goal can be accomplished even easier using foreach():

$array = array(0, 0, 0, 2, 0, 1, 0, 3);
foreach ($array as $val) {
    if ($val != 0) {
        break;
    }
}
echo($val);           // prints "2"

or array_shift():

$array = array(0, 0, 0, 2, 0, 1, 0, 3);
do {
    $val = array_shift($array);
    if ($val != 0) {
        break;
    }
} while(count($array));
echo($val);           // prints "2"

The result is expected: the pointer has been reset. My question is if the pointer is reset only after the end of the array?

The documentation of foreach() is wrong. Maybe it was correct on PHP 3 and PHP 4 but I think since the introduction of iterators in PHP 5 the behaviour of foreach() changed (to better).

It says:

When foreach first starts executing, the internal array pointer is automatically reset to the first element of the array. This means that you do not need to call reset() before a foreach loop.

As foreach relies on the internal array pointer, changing it within the loop may lead to unexpected behavior.

A simple test contradicts this statement:

$array = array(1, 3, 5, 7, 9);

foreach ($array as $val1) {
    foreach ($array as $val2) {
        echo('$val1='.$val1.'; $val2='.$val2.'; ');
    }
    echo("\n");
}

It works without problems. It should not work if foreach() is using the internal array pointer. It probably creates a copy of the pointer.

You can also try to use current(), next(), prev() or reset() inside the foreach() and you will get surprising and sometimes inconsistent results.

You better use foreach() to iterate over the arrays and do not rely on the internal pointer in any way.

The functions reset() and end() are, however, very handy when you need to get the first and the last element of the array without worrying about the keys.

like image 63
axiac Avatar answered Mar 27 '23 21:03

axiac


Yes the pointer will reset only after end of array. Since foreach takes pointer to end of array it will automatically reset after $fruit2 = $fruit; and also it will reset if you go manually at the end of array by using next() as code below

<?php
$fruit = array('a' => 'apple', 'b' => 'banana', 'c' => 'cranberry');

foreach ($fruit as $key => $name) {
    printf("[%s] => [%s]\n", $key, $name);
}

reset($fruit);
next($fruit)."\n";
next($fruit)."\n";
next($fruit)."\n";
$fruit2 = $fruit;
echo current($fruit);
?>
like image 21
abh Avatar answered Mar 27 '23 21:03

abh