4

I created this array with a circular reference:

$arr = array(1 => 'one', 2 => 'two');
$arr[3] = &$arr;

I have a function that recursively prints out the values in an array, but I really couldn't solve the problem of creating a circular reference check. How can you do that?

The current function I have for printing the array is copied below. I haven't included the various attempts I made at doing the circular reference check. They mainly revolved around a strategy of maintaining a $seen array of items that have already been printed for each branch of recursion. This is because I still want to allow the printing of duplicate values, just not printing of a value if it is a parent of the current array being parsed.

The problems I had were figuring out how to add references rather than array copies to this $seen variable. But I'd be happy to use another strategy all together if it worked.

function HTMLStringify($arr)
{

    if(is_array($arr)){
        $html = '<ul>';
        foreach ($arr as $key => $value) {

            $html .= '<li>' . $key;

            if(is_array($value)){

                //Conspicuously missing is a circular reference check,
                //causing infinite recursion. After a few failed attempts
                //at checking for this (e.g. discovering that array_push doesn't take references)
                //I have left it for further study.
                //(After all, Javascript's JSON.stringify() doesn't check for circular references)
                //TODO: Check for circular references

                $html .= HTMLStringify($value, $seen);
            }
            elseif(is_numeric($value) || is_string($value) || is_null($value))
            {
                $html .= ' = ' . $value;
            }
            else
            {
                $html .= ' [couldn\'t parse ' . gettype($value) . ']';
            }

            $html .= '</li>';

        }
        $html .= '</ul>';
        return $html;
    }
    else
    {
        return null;
    }
}
Trindaz
  • 17,029
  • 21
  • 82
  • 111
  • 1
    possible duplicate of: [8460011/in-array-on-objects-with-circular-references](http://stackoverflow.com/questions/8460011/in-array-on-objects-with-circular-references) – Ryan Vincent May 27 '14 at 08:42
  • http://stackoverflow.com/questions/6292164/using-print-r-and-var-dump-with-circular-reference this may help you. – moonwave99 May 27 '14 at 14:02

2 Answers2

1

An adapted version of your code, using the strict in_array check from the answer linked by Ryan Vincent, is shown below:

function HTMLStringify($arr, array $seen = array()) {
    if (is_array($arr)) {
        $seen[] = $arr;
        $html = '<ul>';

        foreach ($arr as $key => $value) {
            $html .= '<li>' . $key;

            if (is_array($value)) {

                if (in_array($value, $seen, true)) {
                    // Deal with recursion in your own way here
                    $html .= ' [RECURSION]';
                } else {
                    $html .= HTMLStringify($value, $seen);
                }

            } elseif (is_numeric($value) || is_string($value) || is_null($value)) {
                $html .= ' = ' . $value;
            } else {
                $html .= ' [couldn\'t parse ' . gettype($value) . ']';
            }

            $html .= '</li>'; 
        }

        return $html . '</ul>';
    } else {
        return null;
    }
}

$arr = array(1 => 'one', 2 => 'two');
$arr[3] = &$arr;
echo HTMLStringify($arr);

Comparing across a number of PHP versions, it looks like this will work for PHP 5.3.15+ and PHP 5.4.5+.

Community
  • 1
  • 1
cmbuckley
  • 40,217
  • 9
  • 77
  • 91
0

i'm using this function for debugging. Also upgraded to detect recursive link.

function print_table($mixed, $level=9, $_callstack=array()){
  if($level<=0){ echo '**LIMIT**'; return; }  

  if( array_search(serialize($mixed), $_callstack)!==false){
      echo '***recursive detected***';
      return ;
  }    
  $_callstack[] = serialize($mixed);

  if(is_array($mixed)){
    echo '<table cellspacing="0" width="100%" border="1">';
    foreach($mixed as $key=>$val){
      echo '<tr><td width="20%">'.$key.'</td><td>';
      if(is_array($val)){
        print_table($val,$level-1, $_callstack);
      }elseif(is_null($val)){
        echo '<span style="color:blue">null</span>';
      }elseif($val===false){
        echo '<span style="color:red">false</span>';
      }elseif($val===true){
        echo '<span style="color:green">true</span>';
      }elseif(is_numeric($val) && $val>1000000000){
        echo $val,' <span style="color:gray">[',date('d-m-Y H:i:s',$val),']</span>';
      }elseif($val===''){
        echo '<span style="color:blue">empty string</span>';      
      }else{
        echo $val;
      }
      echo '</td></tr>';
    }
    echo '</table>';
  }else{
    var_dump($mixed);
  }
}

As you see, i collect serialaized object, then compare it. Serialization required, because simply comparsion recursive object throw a fatal error:

$arr=array(&$arr);
$arr==$arr; // Fatal error: Nesting level too deep - recursive dependency? 
// php 5.2.9

But serialization support recursive objects! So, we should compare serialaized strings, but serialization can take a lot of tima and memory. If you will find another method - let me know :)

MaxXx1313
  • 610
  • 5
  • 15