php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #71454 foreach last element is copy of penultimate, with array of DateIntervals
Submitted: 2016-01-26 11:51 UTC Modified: 2016-01-26 13:59 UTC
From: qdinar at gmail dot com Assigned:
Status: Not a bug Package: Date/time related
PHP Version: Irrelevant OS: windows 10
Private report: No CVE-ID: None
 [2016-01-26 11:51 UTC] qdinar at gmail dot com
Description:
------------
foreach through array of DateIntervals gives penultimate element instead of last element

Test script:
---------------
$intervals=array(1,2,3,4,5,6);
foreach($intervals as &$interv){
	$interv = new DateInterval('PT'.$interv.'M');	
}
$time = new DateTime('6:20');
foreach($intervals as $interv){
	echo ','.$interv->format('%i');
}


Expected result:
----------------
,1,2,3,4,5,6

Actual result:
--------------
,1,2,3,4,5,5

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-01-26 11:54 UTC] qdinar at gmail dot com
i have found a quick fix / workaround for this bug: use foreach($intervals as &$interv) (ie &$interv also in second foreach).
 [2016-01-26 13:59 UTC] rasmus@php.net
-Status: Open +Status: Not a bug
 [2016-01-26 13:59 UTC] rasmus@php.net
This is completely expected. If you unroll your loops you can see why. eg.

$interv = & $intervals[0];
$interv = & $intervals[1];
...
$interv = & $intervals[6];

Now you are at the end of your first loop and as you can see $interv is a reference to $intervals[6]; So at this point if you were to do:

$interv = 123;

it would be equivalent to doing:

$intervals[6] = 123;

So now when we unroll your 2nd loop you are doing:

$interv = $intervals[0];
$interv = $intervals[1];
...
$interv = $intervals[6];

which since $interv is a reference to $intervals[6] is actually doing:

$intervals[6] = $intervals[0];
$intervals[6] = $intervals[1];
...
$intervals[6] = $intervals[5]; // which is 5, of course
$intervals[6] = $intervals[6]; // still 5

It has to work this way because a loop has to be equivalent to its unrolled version. If you want to avoid side-effects like that, unset($interv) before the second loop. This is documented on http://php.net/foreach
 
PHP Copyright © 2001-2021 The PHP Group
All rights reserved.
Last updated: Fri Sep 17 08:03:36 2021 UTC