<!DOCTYPE html>
<html>
<head>
  <title>PHP Array Traversal</title>
    <meta charset="utf-8">
    <link rel="stylesheet" href="layout.css" type="text/css" media="all">
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
    <script src="http://cdn.jquerytools.org/1.2.6/jquery.tools.min.js"></script>
</head>
<body>

  <header>
     <h1>Array Traversal</h1>
  </header>

  <article>
     <p>
     In PHP it's easy to get JSON decoded by <tt>$data = <a href="http://php.net/json_decode">json_decode(</a>$input<a href="http://php.net/json_decode">)</a></tt>.
     But many newcomers are confused by how to pick individual information from the resulting array or object
     structure then. This visualization is intended to help.
  </article>


<? if (count($_REQUEST)): ?>
  
  <section class=output>
     <p>
     Hover with the mouse over <span class=value>entries</span> or keys to see the required array or object traversal syntax:
     <p>
     <b class=key style="font-size: 125%; position: relative; top: 25px;" title='$data'>$data</b>
     <?
     
         #-- decode
         $data = ltrim($_REQUEST["json"]);
         if (@strstr("{[", $data[0])) {
             $data = json_decode($data, !empty($_REQUEST["assoc"]));
         }
         else {
             $data = print_r_decode($data);
         }
         
         #-- check
         if (!$data) {
             print "<p>Your JSON might be corrupt. See <a href=http://jsonlint.com/>JSONLint</a>. Test if you need to disable magic_quotes or something.</p>";
         }
     
         #-- print
         if ($data) {
             print_r_js($data);
         }
         
         #-- foreach
         if (@($_REQUEST["foreach"])) {
             print "<h1>How to loop over this structure</h1>\n";
             print foreach_r_js($data);
             print "<p>Note that this auto-generated loop might only work if the array/object structure is very uniform (no discrepancies in subarray schemes). It also might skip consecutive indexed array levels.</p>";
         }
         
         #-- copy link
         print "<p><br><a style='opacity:0.1' href='http://array.include-once.org/?foreach=".isset($_REQUEST["foreach"])."&assoc=".isset($_REQUEST["assoc"])."&json=".urlencode($_REQUEST["json"])."'>link</a>";
     ?>

     <article>
        <p>
        Also check the manual on <a href="http://php.net/manual/en/language.types.array.php"><tt>array</tt> syntax</a>
        and how to work with <a href="http://php.net/manual/en/language.types.object.php"><tt>objects</tt></a>.<br>
        Or how to use a <a href="http://php.net/foreach"><tt>foreach</tt></a> to loop over entries.
     </article>
  </section>

<? else: ?>
  
  <div class=input>
     <p>
     Simply paste in your JSON blob or <tt>print_r</tt> output to get a visualization.
     <form method=post>
        <p>
        <textarea cols=70 rows=15 name=json title="Paste your JSON here."></textarea>
        <p>
        <input type=submit value="Display structure" name=print_r title="Show a colorful print_r with tooltips.">
        <input type=submit value="Show foreach iteration" name=foreach style="opacity:0.7" title="Will also show an foreach() example for this array structure.">
        <label style="opacity:0.5"><input type=checkbox name=assoc> <tt title="Will return an associative array instead of decoding JSON into objects." href=http://php.net/json_decode>json_decode($json,<b>$assoc=TRUE</b>)</tt></label>
     </form>    
     
     <footer style=margin-top:100pt>
     <p>
     <h4>Disclaimer:</h4>
     <small>
     The <a href=https://gist.github.com/1102761>print_r decoding</a> is courtesy of hakre.<br>
     Printouts are obviously only an approximation, might not work in all cases.<br>
     Use at your own risk and stuff.<br>
     
  </div>

<? endif; ?>

   <script>
      $("[title]").tooltip({ offset: [-7,43], layout: "<div><span class=tooltip-arrow><img src=tooltip.png></span></div>" });
   </script>
  
</body>
</html>

<?php



/**
 * Reconstruct array structure from print_r output.
 *
 */
function print_r_decode($data) {
    include "print_r.php";
    $data = PrintrParser($data);
    if (!$data) { die("<p><h4 style='color:red;background:#fe7'>The print_r parsing failed.</h4><p>It's only an approximation, and 100% correctly transforming it back into an array is near impossible. Check that it was indeed in print_r format, not var_dump, or xdebug output. Otherwise gimme the original JSON data blob.<p>"); }
    return $data;
}


/**
 * CSSified output of an array.
 *
 */
function print_r_js($data, $path='$data', $level=1) {

    switch ($type = gettype($data)) {
    
       case "array":
          print "<div class='block array level-$level'><b>Array</b>(<div class=array-data>\n";
          foreach ($data as $key => $value) {
              $_key = key_array($key);
              $_key_nq = key_array($key, NULL);
              print "<span class='key' title='{$path}{$_key}'>$_key_nq</span> => \n";
              print_r_js($value, "{$path}{$_key}", $level+1);
          }
          print "</div>)</div>\n";
          break;

       case "object":
          print "<div class='block object level-$level'>stdClass <b>Object</b>(<div class=object-data>\n";
          foreach ($data as $key => $value) {
              $_key = key_object($key);
              $_key_ar = key_array($key, NULL);
              $_key_nq = key_object($key, NULL);
              print "<span class='key' title='$path->$_key'>$_key_ar => </span>\n";
              print_r_js($value, "{$path}->{$_key}", $level+1);
          }
          print "</div>)</div>\n";
          break;

       default:
          $data = h($data);
          print "<span class='value $type' title='$path'>$data</span><br>\n";
          break;
    }
}


/**
 * Look for numeric entries and generate some loop examples.
 *
 */
function foreach_r_js($data, $path='$data', $_valuekey='', $skip=0, $pad="", $i="i") {

    if (is_array($data) or is_object($data)) {
        foreach ($data as $key=>$value) {

            // traverse
            if (!is_int($key) or $skip) {
            
                if (is_array($data)) {
                    $_key = key_array($key);
                }
                else {
                    $_key = "->" . key_object($key);
                }
                return foreach_r_js($value, "$path$_key", "$_valuekey$_key", 0, $pad, $i);

            }
            
            // print foreach
            else {
            
                return "<pre class='block foreach level-2'>{$pad}foreach ($path as \$$i=>\$value) {\n"
                    . foreach_r_js($value, $path."[\$$i]", $_valuekey='', $skip=FALSE, "$pad    ", ++$i)
                    . "{$pad}}</pre>";
            }

        }
    }
    
    // print loop innards
    else {
    
        return "{$pad} print <span class=key title='$path'>\$value$_valuekey</span>;\n";
            
    }
}


/**
 * html escape
 */
function h($str) {
    return htmlspecialchars($str, ENT_QUOTES, "UTF-8");
}


function key_array($key, $q="'") {
    return h( is_numeric($key) ? "[$key]" : "[$q".addcslashes("$key", '\'\\')."$q]" );
}
function key_object($key) {
    return h(preg_match("/^[a-z_]\w*$/i", $key) ? $key : "{'".addcslashes($key, '\'\\')."'}");
}

?>