Search code examples
phpsubdirectorydepthrecursiveiterator

PHP, RecursiveIteratorIterator, depth of 2 only


I would like to retrieve all subdirectories of a directory, but only those subdirectories that have a depth of 2 only.

By now, I've written this code, which works fine.

        $directory="./test_dir";
        $maxDepth=2;

        $iterator = new \RecursiveIteratorIterator(
                        new \RecursiveDirectoryIterator(
                            $directory, 
                            \FilesystemIterator::SKIP_DOTS
                        ),
                    \RecursiveIteratorIterator::SELF_FIRST,
        );

        $iterator->setMaxDepth($maxDepth);

        foreach ($iterator as $file) {
            if($iterator->getDepth()===2){
                echo $file->getPathname() . "\n";
            }
        }

However, I'm wondering if I can eliminate the need for test in the foreach loop:

            if($iterator->getDepth()===2){
                ...
            }

by directly filtering it using a RecursiveFilterIterator when I instantiate $iterator.

I think I need to create a class that extends RecursiveFilterIterator, and define a condition in accept method, like in this exemple (comment in the php manual).However, I'm a bit confused about how to proceed or if it's possible.


Solution

  • I do not know if it is possible to get the current depth of an iterator item with RecursiveFilterIterator. In your case the item would be an SplFileInfo instance, which does not know anything about its file's depth.

    You could wrap the directory iterator in a filter iterator instead:

    $directory = './test_dir';
    $depth = 2;
    
    $iterator = new CallbackFilterIterator(
        new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($directory, FilesystemIterator::SKIP_DOTS),
            RecursiveIteratorIterator::SELF_FIRST,
        ),
        fn(SplFileInfo $file, string $key, RecursiveIteratorIterator $it) => $it->getDepth() === $depth
    );
    
    foreach ($iterator as $file) {
        echo $file->getPathname() . "\n";
    }
    

    I assume that not setting setMaxDepth makes the whole thing slower should there be huge amounts of subdirectories and files on level 3 and more. In such a case it might make sense to set it:

    $recursiveIteratorIterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($directory, FilesystemIterator::SKIP_DOTS),
        RecursiveIteratorIterator::SELF_FIRST,
    );
    $recursiveIteratorIterator->setMaxDepth($depth);
    
    $iterator = new CallbackFilterIterator(
        $recursiveIteratorIterator,
        fn(SplFileInfo $file, string $key, RecursiveIteratorIterator $it) => $it->getDepth() === $depth
    );