𝌔 Fossil Hub
Login | Fossil manual |
array traversal tutorial

Check-in [a3783c540a]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:array traversal visualization 0.1
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:a3783c540a3c0c1bbad632c98b5aca0083bd868a
User & Date: mario 2012-01-10 05:22:35
Context
2012-01-10
05:23
phptags -w -c check-in: 601bdec8cb user: mario tags: trunk
05:22
array traversal visualization 0.1 check-in: a3783c540a user: mario tags: trunk
05:22
initial empty check-in check-in: bfc469bf05 user: mario tags: trunk
Changes

Added index.php.











































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
<!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>";
         }
     ?>

     <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, '\'\\')."'}");
}

?>

Added layout.css.



































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
html,body {
   padding: 50pt;
   background: #e0e3e0;
   font: 17px Arial, Helvetica, sans-serif;
   line-height: 170%;
   color:#4b4b4b;
}

h1 {
   font-size: 42pt;
}

tt {
   background: #d7d9d7;
   padding: 2px;
}

a {
   text-decoration: none;
}

.array, .object {
   padding: 10pt;
   color: #111;
   border: 2px #dedede dashed;
   font-family: monospace;
}
.array-data, .object-data {
   padding-left: 15pt;
}
.key {
   background: #cec;
   padding: 2px;
}
.key:hover {
   background: #beb;
}
.value {
   background: #ddf;
   padding: 2px 10px;
}
.value:hover {
   background: #ccf;
}

.block.level-1 {
   background: #dfdfdf;
   border-color: #dcdcdc;
}
.block.level-2 {
   background: #dcdcdc;
   border-color: #d9d9d9;
}
.block.level-3 {
   background: #d9d9d9;
   border-color: #d6d6d6;
}
.block.level-4 {
   background: #d6d6d6;
   border-color: #d3d3d3;
}
.block.level-5 {
   background: #d3d3d3;
   border-color: #d0d0d0;
}
.block.level-6 {
   background: #d0d0d0;
   border-color: #cccccc;
}
.block.level-7 {
   background: #cccccc;
   border-color: #c9c9c9;
}
.block.level-8 {
   background: #c9c9c9;
   border-color: #b6b6b6;
}
.block.level-9 {
   background: #c6c6c6;
   border-color: #c3c3c3;
}
.block.level-10 {
   background: #c3c3c3;
   border-color: #c0c0c0;
}
.block.level-11 {
   background: #c0c0c0;
   border-color: #bcbcbc;
}



.tooltip {
   color: #fff; 
   font-weight: 800;
   background: #333;
   display:none;
   font-size:16px;
   min-width:340px;
   height:30px;
   padding:10px 15px;
   border: 3px solid #eee;
   border-radius: 7pt;
   box-shadow: 10px 10px 5px #aaa;
}
.tooltip-arrow img {
   position: relative;
   top: 50px;
   left: 120px;
}
.tooltip-arrow img {
   position: absolute;
}

Added print_r.php.

















































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
<?php
/**
 * print_r converter - convert print_r() to php variable code
 *
 * Author: hakre <hakre.wordpress.com>
 * Copyright (c) 2011, some rights reserved
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * known:
 *   - string values over multiple lines not supported.
 *   - limited support for objects, stdClass only.
 *
 * best php codepad in town:
 * >> http://codepad.viper-7.com/ <<
 *   +++ php 5.3 support
 *    +++ working regex
 *     +++ html and source view
 *
 * CHANGES:
 * 0.1.1 - allow more whitespace in array-open.
 *       - remove , at the end of values.
 * 0.1.0 - version 0.1.0, fixed some minor issues.
 * 0.0.9 - support for stdClass objects.
 * 0.0.8 - form was closed too early, fixed.
 * 0.0.7 - textarea for output.
 *       - clear / undo clear.
 * 0.0.6 - deal with empty values in parser via state.
 * 0.0.5 - button tooltips.
 *       - input sanitization now upfront.
 *       - html and css updates.
 *       - change output variable-name from $var to $array
 * 0.0.3 - github link opened in frameset eventually, fixed.
 * 0.0.2 - tokenizer less-strict whitespace dealing for array open and close
 *       - cache last input value into cookie
 *       - typo in tokenizer class name, fixed
 *
 * @author hakre
 * @license GPL v3+
 * @version 0.1.1
 * @date 2011-07-24
 */

header('Content-Type: text/html; charset=utf-8');

/* input: either post or cached in cookie */
$i = isset($_POST['i'])? (string)$_POST['i']:'';
if (($ilen = strlen($i)) > $imaxlen = 4096)
    die(sprintf('Maximum input length (post) of %d exceeded: %d.', $imaxlen, $ilen));
$requestHasCookieData = false;
if (!empty($i))
{
    setcookie('i', $i);
    $requestHasCookieData = true;
} elseif (!empty($_COOKIE['i'])) {
    $i = $_COOKIE['i'];
    if (($ilen = strlen($i)) > $imaxlen)
        die(sprintf('Maximum input (cookie) length of %d exceeded: %d.', $imaxlen, $ilen));
    else
        $requestHasCookieData = true;
}

isset($_POST['c']) && $i = '';
$canUndo = $requestHasCookieData && isset($_POST['c']);

/**
 * print_r regex Tokenizer
 */
class PrintrTokenizer implements Iterator
{
    private $tokens = array(
        'array-open' => 'Array\s*\(\s?$',
        'object-open' => 'stdClass Object\s*\($',
        'key' => '\s*\[[^\]]+\]',
        'map' => ' => ',
        'array-close' => '\s*\)\s?$',
        'value' => '(?<= => )[^\n]*$'
    );
    private $buffer;
    private $offset;
    private $index;
    private $current;
    public function __construct($buffer)
    {
        $this->buffer = $buffer;
    }
    private function match($def, $at)
    {
        $found = preg_match(
            "~$def~im", $this->buffer, $match, PREG_OFFSET_CAPTURE, $at
        );
        if (false === $found) die('Regex error.');

        $return = 0;
        if ($found && $at === $match[0][1])
            $return = strlen($match[0][0]);

        return $return;
    }
    private function matchLargest($at)
    {
        $match = $max = 0;
        foreach($this->tokens as $name => $def)
        {
            ($len = $this->match($def, $at))
            && $len > $max
            && ($max = $len)
            && ($match = $name);
        }
        return $match ? array($match, $at, $max) : null;
    }
    public function current()
    {
        return $this->current;
    }
    public function key()
    {
        return $this->index;
    }
    public function next()
    {
        $current = $this->matchLargest($this->offset);
        ($current)
            && ($current = array_merge($current, array(substr($this->buffer, $this->offset, $current[2]))))
            && ($this->offset += $current[2])
            ;
        $this->current = $current;
        $this->index++;
    }
    public function valid()
    {
        return !(null === $this->current);
    }
    public function rewind()
    {
        $this->offset = 0;
        $this->next();
        $this->index = 0;
    }
}

/**
 * print_r Parser
 */
function PrintrParser($buffer) {
    $result = null;
    $rP = &$result;
    $rS = array();
    $level = 0;
    $len = strlen($buffer);
    $offset = 0;
    $tokens = new PrintrTokenizer($buffer);
    $state = 0; // 1: map
    foreach($tokens as $index => $tokenData) {
        list($token, $offset, $length, $text) = $tokenData;
        switch($token)
        {
            case 'array-open':
                $rP = array();
                $state = 0;
                break;
            case 'object-open':
                $rP = new stdClass();
                $state = 0;
                break;
            case 'key':
                if (1 === $state) { // empty value
                    $rP = '';
                    $rSi = count($rS)-1;
                    $rP = &$rS[$rSi];
                    unset($rS[$rSi]);
                    $state = 0;
                }
                $key = preg_replace('~\[(.*)\]~', '$1', trim($text));
                ((string)(int)$key === $key) && $key = (int)$key;
                if (is_object($rP))
                    $rP->$key = null;
                else
                    $rP[$key] = null;
                break;
            case 'map':
                $rS[count($rS)] = &$rP;
                if (is_object($rP))
                    $rP = &$rP->$key;
                else
                    $rP = &$rP[$key];
                $state = 1;
                break;
            case 'value':
                if (is_string($text) && ($text = rtrim($text)) && ',' === substr($text,-1))
                    $text = substr($text, 0, -1);
                ((string)(int)$text === $text) && $text = (int)$text;
                $rP = $text;
                # fall-through intended
            case 'array-close':
                $rSi = count($rS)-1;
                $rP = &$rS[$rSi];
                unset($rS[$rSi]);
                $state = 0;
                break;
        }
    }
    return $result;
}

Added tooltip.png.

cannot compute difference between binary files