2

I wanted to play around with some of PHP's iterators and managed to get a solid (from my understanding) build going. My goal was to iterate inside of a parent folder and get 2 nodes down; building a hierarchical tree array during the process. Obviously, I could do this fairly easy using glob and a couple of nested loops but I want to use Spl classes to accomplish this.

All that out of the way, I've played around with SplHeap and SplObjectStore to hierarchy and failed. What's messing with my noodle is my normal methods of recursion are failing (out-of-memory errors) and my one success falls with a recursive method that loops over each node, adding to an array. The problem with that is it ignores the setMaxDepth() method and goes through all of the children. I thought about setting a $var++ to increment through the loop, limiting to limit the nodes but I don't believe that's the "right way".

Anywho, code (sorry for any orphaned code if any - just ignore it)...

<?php
namespace Tree;

use RecursiveFilterIterator,
    RecursiveDirectoryIterator,
    RecursiveIteratorIterator;

class Filter extends RecursiveFilterIterator {
    public static $FILTERS = array(
        '.git', '.gitattributes', '.gitignore', 'index.php'
    );

    public function accept() {
        if (!$this->isDot() && !in_array($this->current()->getFilename(), self::$FILTERS))
            return TRUE;

        return FALSE;
    }
}

class DirTree {
    const MAX_DEPTH = 2;

    private static $iterator;
    private static $objectStore;

    public function __construct() {

        error_reporting(8191);
        $path       = realpath('./');

        try {

            $dirItr     = new RecursiveDirectoryIterator($path);
            $filterItr  = new Filter($dirItr);
            $objects    = new RecursiveIteratorIterator($filterItr, RecursiveIteratorIterator::SELF_FIRST);

            $objects->setMaxDepth(self::MAX_DEPTH);

            echo '<pre>';
            print_r($this->build_hierarchy($objects));

        } catch(Exception $e) {
            die($e->getMessage());
        }
    }

    public function build_hierarchy($iterator){
        $array = array();
        foreach ($iterator as $fileinfo) {

            if ($fileinfo->isDir()) {
                // Directories and files have labels
                $current = array(
                    'label' => $fileinfo->getFilename()
                );
                // Only directories have children
                if ($fileinfo->isDir()) {
                    $current['children'] = $this->build_hierarchy($iterator->getChildren());
                }
                // Append the current item to this level
                $array[] = $current;
            }
        }
        return $array;
    }
}

$d = new DirTree;
Jason Oakley
  • 91
  • 1
  • 5

1 Answers1

1

A RecursiveIteratorIterator is designed primarily to give you an iterator that behaves like an iterator over a flat list, but the flat list is actually just a sequence in the recursive traversal. It does this by internally managing a Stack of RecursiveIterators, calling getChildren() on them as necessary. A client of RecursiveIteratorIterator is only really supposed to call the normal Iterator methods like current() and next() etc...with the exception of value added methods like setMaxDepth()

Your problem is that your trying to do the recursion yourself by calling getChildren(). If you want to manually manage the recursion, that's fine - but that makes RecursiveIteratorIterator redundant. In fact, I'm really surprised calling getChildren() on RecursiveIteratorIterator didn't fatal error. That's a RecursiveIterator method. spl is probably just forwarding the method call to the inner iterator(some spl classes forward method calls to undefined methods in order to make it easy to use the Decorator design pattern).

The right way:

    $dirItr     = new RecursiveDirectoryIterator($path);
    $filterItr  = new Filter($dirItr);
    $objects    = new RecursiveIteratorIterator($filterItr, RecursiveIteratorIterator::SELF_FIRST);

    $objects->setMaxDepth(self::MAX_DEPTH);

    echo '<pre>';
    foreach ($objects as $splFileInfo) {
        echo $splFileInfo;
        echo "\n";
    }

I'm not going to get into forming the hierarchical array in some particular structure for you, but maybe this related question further helps you understand the difference between RecursiveIteratorIterator and RecursiveIterator How does RecursiveIteratorIterator work in PHP?

Community
  • 1
  • 1
goat
  • 31,486
  • 7
  • 73
  • 96