Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Infinite loop in foreach on a simplexmlelement

I face a particular problem, for wich i did not find much references. I found simpleXMLElement attributes and foreach which describe the same problem, but it hasn't been answer for a year, and do not have any answer.

For a bit of context, i'm trying to use PHPOffice/PHPExcel, and when loading a xlsx file, this framework loop on a simplexmlelement on lines 364 to 376 of Excel2007.php, available at https://github.com/PHPOffice/PHPExcel/blob/1.8/Classes/PHPExcel/Reader/Excel2007.php, of which the few lines are extracted here (i added the var_dump and echo):

foreach ( $colourScheme as $k => $xmlColour ) {
    var_dump ( $xmlColour ); echo 'NEWVAR';
    $themePos = array_search ( $k, $themeOrderArray );
    if (! $themePos) {
        $themePos = $themeOrderAdditional ++;
    }
    if (isset ( $xmlColour->sysClr )) {
        $xmlColourData = $xmlColour->sysClr->attributes ();
        $themeColours [$themePos] = $xmlColourData ['lastClr'];
    } elseif (isset ( $xmlColour->srgbClr )) {
        $xmlColourData = $xmlColour->srgbClr->attributes ();
        $themeColours [$themePos] = $xmlColourData ['val'];
    }
}

The $colourScheme var is a SimpleXMLElement, which contains 12 SimpleXMLElements, and all thoses 12 have a SimpleXMLElement 0 as child, except one that have "srgbClr".

When I try to debug it, the $k var gets updated on 2 iterations of the loop, and then does not change furthermore (and there are 12 occurences in $colorScheme), thus looping infinitely (or at least until the time limit, and i tried with a very long value).

If I comment the following lines of my php.ini (I'm working with a 64b version of wampServer, on Windows 7, starting debugging with eclisePDT debug icon as web application), the infinite loop does not appear to happen, and the file seems to be well transformed.

; XDEBUG Extension

zend_extension = "D:/path/to/wamp/bin/php/php5.5.12/zend_ext/php_xdebug-2.2.5-5.5-vc11-x86_64.dll"
;
[xdebug]
xdebug.remote_enable = on
xdebug.remote_host = "localhost"
xdebug.remote_port = 9000
xdebug.remote_handler = "dbgp"
xdebug.remote_mode = req
xdebug.profiler_enable = On
xdebug.profiler_enable_trigger = off
xdebug.profiler_output_name = cachegrind.out.%t.%p
xdebug.profiler_output_dir = "D:/path/to/tmp/tmp"
xdebug.show_local_vars = on
xdebug.collect_params = on

EDIT : a bit more information. Everything seems to work fine (php.ini commented or not) until i add a breakpoint in eclipse and ask for debugging my page. Once i add a breakpoint, the infinite loop occur, and i get stoped by the maximum execution time or maximum memory limit. I tested it with a small file (20KB), and it seem to work, debugging or not, and a bigger one (750KB), and it fail. While stepping, i can definitely see that my key $k does not get updated after the 2nd iteration.

If anyone have an idea, or anything that could help me debug without infinite looping, it would be great !

Many thanks for your time and your help !

like image 911
Matthieu Borgraeve Avatar asked Nov 24 '15 13:11

Matthieu Borgraeve


1 Answers

I just recently got done doing some similar work in that same file. One thing that took me a long time to realize is there is a bug in the SimpleXML extension that can lead to this: https://bugs.php.net/bug.php?id=55098

What seemed to work for me is instead of doing:

foreach ($colourScheme as $k => $xmlColour) {
   ...
}

Try using the children() method:

foreach ($colourScheme->children() as $k => $xmlColour) {
   ...
}

Another thing to try is wrapping it with a count before trying to loop over it:

if ($colourScheme->count() > 0) {
   ...
}

The problem here is that a SimpleXML object is not an array and doesn't always behave the way you might think it does. Even the documentation for children(), which is probably the same behavior as the casting occuring in a foreach loop, says that a SimpleXML element will be returned whether there are children or not. Hence you keep iterating on the same object over and over again (infinite loop):

enter image description here

If all else fails, you can convert it to a real array and handle it that way:

$xml = simplexml_load_string($xmlstring);
$json = json_encode($xml);
$array = json_decode($json,TRUE);

References:

http://php.net/manual/en/simplexmlelement.children.php http://php.net/manual/en/simplexmlelement.count.php

like image 59
Jeremy Harris Avatar answered Sep 22 '22 20:09

Jeremy Harris