Making complex PHP arrays viewable
When you want to study the contents of PHP arrays, for example when you ask the API of your favourite PHP CMS a question and it returns an array in which the answer is somehow hidden, you can use PHP functions like print_r and var_dump to display the array in a way that makes it easy to study.
Let’s say you define the following array:
$foods = array('plants' => array('fruits', 'vegetables'), 'animals' => 'meat', 'mixed' => array('pies' => 'pies'));
then running print_r($foods)
will give you the following result:
Array ( [plants] => Array ( [0] => fruits [1] => vegetables ) [animals] => meat [mixed] => Array ( [pies] => pies ) )
This improves the readibility quite a bit, because the linebreaks, indentation and added information (brackets for keys, “Array” to indicate the type) all help you to visually parse the array.
When you have large arrays to study however, the usefulness of print_r
or var_dump
diminishes rapidly. It can get quite tricky to remember the indentation level of an array that spans more than a few screens.
This is where tools like Krumo come in; they will present (within a web page) an array or object (or any value really) within a collapsible format. Only when you click on a top element will it fold out to display its contents.
I needed something like Krumo, but since the latter clocks in at about 100 kilobytes, Krumo itself can become quite complex to work with if you want more than the basics. (Don’t worry if you were thinking about using Krumo, it is still unsurpassed at simply showing objects and arrays.)
Below, I present you what I came up with.
As an aside I would like to note that the complexity of one-size-fits-all products on the web is starting to annoy me.
Simple websites that only need to show a bit of text and a photo nowadays send megabytes per page view over the net. Instead of hand-writing all the required code, developers have started to opt for installing all kinds of toolkits and frameworks and libraries, just because they need one or two functions that they don’t want to write themselves. This is toxic behaviour that has all kinds of unwanted side-effects that I don’t want to go into now: training users to expect slow websites, technologising web development, making code hard to maintain, forcing site owners onto a complex (= expensive) maintenance path and so on.
Basically, if you want something simple, code it yourself.
To make an array easy to view, we use a web page as the presentation medium. This makes sense because as web developers we have servers and browsers running anyway. Also, when it is a CMS or other web-based environment that we need to debug, we can just patch into its own presentation layer.
The web gives us HTML to structure our data, CSS to alter the display and Javascript to add the interactivity. In this case the Javascript will collapse the array into a display of just its top level elements and it will make parent elements clickable so that child elements can unfold.
HTML allows you to structure elements in a hierarchy, just like arrays. Using the dl
, dt
and dd
elements for this would be preferable for accessibility reasons, but since I was making a visual tool for personal use, I used divs
. Shown here is a fragment of the array represented as HTML. The meaning of each element (the type if you will) is determined by its class. Again, I am aware this is Wrong with a capital w, and will strive to do better next time.
<div class='array_container'> <div class='array_key'>plants</div> <div class='array_value'> <div class='array_container'> <div class='array_key'>0</div> <div class='array_value'>fruits </div> <!-- /.array_value of 0 --> </div> <div class='array_container'> <div class='array_key'>1</div> <div class='array_value'>vegetables </div> <!-- /.array_value of 1 --> </div> </div> <!-- /.array_value of plants --> </div>
Then we add a minimal amount of CSS:
.array_container.closed .array_value { display: none; } .array_key { background: #ccc; min-width: 5em; cursor: pointer; } .array_value { padding-left: 2em; }
The result looks as follows in the browser:
Now if I were to place the closed class on an array_container, the page would only show the array_keys of the top level children. This is what we want, because we want a simple display of a complex array. Closing all array_containers is exactly what our Javascript does.
var elements = document.getElementsByClassName('array_container'); for (var i = 0; i < elements.length; i++) { elements.item(i).onclick = foldToggle; addClass(elements.item(i), 'closed'); } function foldToggle(e) { e.stopPropagation(); toggleClass(this, 'closed'); }
This creates a Javascript collection of all array_containers on a page. It then cycles through all the array_containers it found, binds the foldToggle
function to a mouse click and adds the class ‘closed’ to each array_container.
Because of my desire to not use any large third-party libraries, I had to write the class handling code myself:
function toggleClass(element, classToToggle) { if (hasClass(element, classToToggle)) { removeClass(element, classToToggle); } else { addClass(element, classToToggle); } } function hasClass(element, classToTest) { var classes = element.className.match(/\S+/g); if (classes !== null) { var classIndex = classes.indexOf(classToTest); if (classIndex > -1) { return true; } } return false; } function addClass(element, classToAdd) { if (!hasClass(element, classToAdd)) { element.className += ' ' + classToAdd; } } function removeClass(element, classToRemove) { var classes = element.className.match(/\S+/g); if (classes !== null) { var classIndex = classes.indexOf(classToRemove); if (classIndex > -1) { classes.splice(classIndex, 1); element.className = classes.join(' '); } } }
Even then this won’t work in Internet Explorer below version 9. Again this is acceptable in my eyes because this code is aimed at developers only. If you need that sort of support though, the getAttribute
and setAttribute
methods are your friends.
Finally, in order to convert PHP arrays to structured HTML I wrote the following PHP function:
function print_c($data, $level = 0) { if ($level > ARRAY_VIEWER_MAX_DEPTH) { print "[...]"; return false; } $data = (array) $data; foreach ($data as $key => $value) { print "\n<div class='array_container'>\n<div class='array_key'>$key</div>"; print "\n<div class='array_value'>"; if (is_array($value)) { $level++; print_c($value, $level); } else { print $value; } print "\n</div> <!-- /.array_value of $key -->"; print "\n</div>"; } }
All you need to do to use it is to call print_c($array)
from your PHP.
As you can see, if the function encounters an array, it will call itself again, and anything else will get printed as a value. This will probably go horribly wrong where a value isn’t a string, number or array. There are ways to deal with all kinds of value types, but I will leave those as an exercise for the reader.
If you want to download the complete code, check the Github repository.
Leave a Reply