October 29, 2010

Classes
Tags blog

Directory Tree in XML

<p>As a fan of XML, <a href=http://en.wikipedia.org/wiki/Document_Object_Model>the DOM</a>, and interesting design pat

As a fan of XML, the DOM, and interesting design patterns, when recently asked for a way to retrieve a representation of a public directory tree on an iPhone app, I decided to code something a little more elegant than printing raw XML or, worse, some custom format. My first assumption was that I'd be building an actual DOM tree, then outputting formatted XML from there. This way, I'd be assured my output was always valid XML.

I then needed to decide how to recurse the directory structure. There are several ways to approach this, but since I needed an XML element for each file or directory that was read, I decided to do the reading, recursing, and node creation all in the same place: in a constructor for an element. To achieve this, I extended the PHP DOMElement class to a new class called FileSystemNode, and overrode the constructor with my new application-specific logic.

class FileSystemElement extends DOMElement {
    function __construct($parent, $path) {
 
        // Remove trailing slash, if any
        $path = rtrim($path, '/');
 
        // Call the parent constructor with the tag name (probably "file" or "dir")
        parent::__construct(filetype($path));
 
        // Append this new element to its parent
        $parent->appendChild($this);
 
        // Add the name of this file or directory as an attribute
        $this->setAttribute('name', end(explode('/', $path)));
 
        // If this is a readable directory...
        if (is_dir($path) && $handle = opendir($path)) {
 
            // read each file and directory it contains...
            while ($file = readdir($handle)) {
 
                // make sure it's not a self- or parent-reference ("." or "..")
                if ($file[0] != '.') {
 
                    // and construct it as another element
                    new FileSystemElement($this, "$path/$file");
                }
            }
            closedir($handle);
        }
    }
}

With the reading of the file system and the construction of the DOM tree done, all that's left is to create the DOM Document to create the tree into, and format and print the output appropriately. For my example output below, I'm outputting the files and directories of a Mint installation.

$document = new DOMDocument(); 
 
$root = new FileSystemElement($document, 'mint');
$document->formatOutput = true;
print($document->saveXML());

This produces the below output (which has been clipped to a reasonable length).

 version="1.0"?>
 name="mint">
   name="pepper">
     name="shauninman">
       name="geomint">
         name="readme.txt"/>
         name="blank.gif"/>
         name="class.php"/> 
        ...

And we're done! Are there any problems with this approach, or things you would do differently? Let me know in the comments!