3

given the following code

<?php
$a = array(1,2,3,4,5,6);
$c=0;
foreach($a as $v){
    if($v==5&&$c==0){
        $c=1;
        reset($a);
    }
    var_dump($v);
}

How do I reset the pointer so it will print 1,2,3,4,5,1,2,3,4,5,6 ?

I know that in this case I can simply

<?php
$a = array(1,2,3,4,5,6);
$c=0;
for($i=0;$i<count($a);++$i){
    $v = $a[$i];
    if($v==5&&$c==0){
        $c=1;
        $i=-1;  //because of the loop ++$i
    }
    var_dump($v);
}

But I have a much more complex piece of code and the solution is not as simple as rewrite the loop (not numeric keys).

Any PHP guru out there that can help me here ?

Fabrizio
  • 3,734
  • 2
  • 29
  • 32
  • Your first example should work. `reset()` (http://php.net/manual/en/function.reset.php) is the function for the case. – Ismael Miguel Apr 13 '15 at 16:19
  • it should, but it doesn't !! – Fabrizio Apr 13 '15 at 16:24
  • fyi, How a 'foreach' actually 'works' is 'interesting, maybe: [How 'foreach' actually works](http://stackoverflow.com/questions/10057671/how-foreach-actually-works). May i suggest that you do not adjust the 'internal pointer' in a 'foreach loop'? There are other 'loop' constructs that work fine when the 'internal pointer' is reset. – Ryan Vincent Apr 13 '15 at 16:58
  • If I could make it work without reset it I would. I will check out the link, thank you – Fabrizio Apr 13 '15 at 16:59

2 Answers2

6

NOTE: Answer uses function deprecated as of PHP 7.2

As @TheodoreR.Smith pointed out in the comments. list() = each() will not work for any PHP version >= 7.2. See:

How can I update code that uses the deprecated each() function?

Original Answer for version < 7.2

As documented:

Note: 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.

http://php.net/manual/en/control-structures.foreach.php

Not sure what "unexpected behavior" is, as I've never tried this...but maybe safer to manually use each()...also more clear in your code.

reset($a);
while(list($key, $val) = each($a)) {
    if($val==5&&$c==0){
        $c=1;
        reset($a);
    }
    var_dump($val);
}

Foreach Issues Explained

I thought I had seen before that you couldn't rely on the internal pointer in the foreach...but couldn't find it in the documentation this time...only "unexpected results". However, thanks to a commenter, I found the text that used to be in the foreach:

Unless the array is referenced, foreach operates on a copy of the specified array and not the array itself. foreach has some side effects on the array pointer. Don't rely on the array pointer during or after the foreach without resetting it."

http://php.net/manual/en/control-structures.foreach.php#114759

Kevin Nelson
  • 7,613
  • 4
  • 31
  • 42
  • The `while()` with the `each()` is really the best solution here. – Ismael Miguel Apr 13 '15 at 16:23
  • I was actually going to add that to the code, that it is a feasible solution, but because the `foreach` relies on the internal array pointer I was thinking that a `reset` should have worked. Thank you – Fabrizio Apr 13 '15 at 16:23
  • @Fabrizio, yeah...it's actually confusing me a bit...I thought it had said it used a different pointer somewhere else...based on that documentation, I agree it would seem that reset() "should" work. So, I'm mostly going off the "unexpected behavior" aspect in the comments. – Kevin Nelson Apr 13 '15 at 16:25
  • @Fabrizio, okay, updated my answer with a possible solution to try...try referencing your array in the foreach. – Kevin Nelson Apr 13 '15 at 16:33
  • 1
    `Parse error: syntax error, unexpected '&' in [...][...] on line 3` --> this error occurs on your 2nd example. – Ismael Miguel Apr 13 '15 at 16:44
  • @IsmaelMiguel, thanks for the FYI -- hate examples that make me look stupid :)...that's what I get for not trying it out first...wonder why the documentation implied you could reference it then? In any case, removed that example and I'll stick with the answer that you just need to use `each()`. – Kevin Nelson Apr 13 '15 at 17:05
  • @KevinNelson I think the documentation meant `foreach($a as &$v)`. But don't worry, mine made me look stupid a little aswell. Alternatively, you can try to store a reference and reset the reference (or add an element to the beggining of the array.) – Ismael Miguel Apr 13 '15 at 17:10
  • `each` was removed in PHP 8. This answer no longer works at all. – Theodore R. Smith May 09 '23 at 02:57
  • @TheodoreR.Smith, thanks for the note. I added a warning to the answer. – Kevin Nelson May 09 '23 at 21:59
2

If you are a fan of OOP, you can use the ArrayIterator class.

It has the method rewind() which is the same as the reset() function.

$a=array(1,2,3,4,5,6);//or $a=range(1,6);

$i=new ArrayIterator($a);

$c=0;

foreach($i as $k=>$v)
{
    if($v==5&&$c==0)
    {
        $c=1;
        $i->rewind();
    }
    var_dump($v);
}

unset($i);//delete the iterator, to free memory

You can test it online on http://writecodeonline.com/php/ or on http://sandbox.onlinephpfunctions.com/

@KevinNelson's answer is the right way to go, but it won't be obvious for the untrained eye.


There's a problem with this code.
Due to the iteration, the number 1 won't be echoed the 2nd time around.

But I've got 2 franken-solutions!

Solution 1: jump over an ignorable element, using the method next()

$a=array(0,1,2,3,4,5,6);//or $a=range(0,6);

$i=new ArrayIterator($a);

$c=0;

$i->next();//jumps over the 1st element

foreach($i as $k=>$v)
{
    if($v==5&&$c==0)
    {
        $c=1;
        $i->rewind();
    }
    var_dump($v);
}

unset($i);

Solution 2: create an element with 'falsy' value (0, null, false, ''...) and ignore it inside the loop

$a=array(0,1,2,3,4,5,6);//or $a=range(0,6);

$i=new ArrayIterator($a);

$c=0;

foreach($i as $k=>$v)
{
    if($v==5&&$c==0)
    {
        $c=1;
        $i->rewind();
    }
    if($v)var_dump($v);
}

unset($i);
Community
  • 1
  • 1
Ismael Miguel
  • 4,185
  • 1
  • 31
  • 42