SitePoint Sponsor

User Tag List

Results 1 to 10 of 10
  1. #1
    Grumpy Minimalist
    Join Date
    Jul 2006
    Location
    Ontario, Canada
    Posts
    424
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    DirectoryIterator; 2 levels deep

    Here's a list of the directories in the current folder (they are all directories, there are no files or links):
    Code:
    ./temp
    ./temp/sub1
    ./temp/sub1/lowest1
    ./temp/sub1/lowest2
    ./temp/sub2
    ./temp/sub2/lowest3
    ./temp/sub2/lowest4
    Here's the PHP script in the current folder:
    PHP Code:
    <?php
        
    //Make the iterator for the temp directory
        
    $dir = new DirectoryIterator('temp');

        
    //Print the top level name
        
    echo $dir->getBasename(), '<br>';

        
    //Loop through all the subdirectories
        
    foreach ($dir as $subdir) {
            if (!
    $subdir->isDir() || $subdir->isDot()) continue;

            
    //Print the subdirectory name
            
    echo '> '$subdir->getBaseName(), '<br>';

            
    //Loop through all the (sub)subdirectories
            
    foreach ($subdir as $subsubdir) {
                if (!
    $subsubdir->isDir() || $subsubdir->isDot()) continue;

                
    //Print the (sub)subdirectory name
                
    echo '>> '$subsubdir->getBaseName(), '<br>';
            }
        }
    ?>
    I would expect this output:
    Code:
    .
    > sub1
    >> lowest1
    >> lowest2
    > sub2
    >> lowest3
    >> lowest4
    However, the code produces this:
    Code:
    .
    > sub1
    >> sub1
    >> sub2
    In fact, it doesn't matter how many loops I nest; it stays in the same directory.

    Adding this line before the inner loop fixes the issue:
    PHP Code:
    $subdir = new DirectoryIterator($subdir->getRealPath()); 
    Where did I go wrong?

    For my purposes, I need to recurse to a depth of exactly 2; hence I am not using RecursiveDirectoryIterator.

  2. #2
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2008
    Posts
    5,757
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    foreach isn't creating a new iterator for you. That's why you needed to explicitly do it.


    PHP Code:
    foreach ($dir as $subdir) {
    }
    // equivalent to...
    $dir->rewind();
    while(
    $dir->valid()) {
        
    //$subdir = $dir->current();
        
    $dir->next();
    }





    // and
    var_dump($dir === $dir->current()); // always true

    // and thats why both echos are identical
    foreach ($dir as $subdir) {
        echo 
    $dir->getBasename();
        echo 
    $subdir->getBasename();


  3. #3
    Grumpy Minimalist
    Join Date
    Jul 2006
    Location
    Ontario, Canada
    Posts
    424
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    That makes sense. Thanks.

  4. #4
    @php.net Salathe's Avatar
    Join Date
    Dec 2004
    Location
    Edinburgh
    Posts
    1,398
    Mentioned
    65 Post(s)
    Tagged
    1 Thread(s)
    Further to crmalibu's post, your inner foreach loop would need something like:
    PHP Code:
    foreach (new DirectoryIterator($subdir->getPathname()) as $subsubdir) { 
    Also, you could change the "info class" for the DirectoryIterator to return new DirectoryIterators (look for the SplFileInfo::setInfoClass documentation):
    PHP Code:
    // ...
    $dir->setInfoClass('DirectoryIterator');
    foreach (
    $dir as $subdir) {
      
    // ...
      
    foreach ($subdir->getFileInfo() as $subsubdir) {
        
    // ...
      
    }


    Thirdly, you could also use a RecursiveDirectoryIterator and restrict the output to only two levels. For example:
    PHP Code:
    $dir = new RecursiveDirectoryIterator('temp');
    $it  = new RecursiveIteratorIterator($dirRecursiveIteratorIterator::SELF_FIRST);

    //Loop through all the subdirectories
    foreach ($it as $file) {
        
    // Restrict to two levels only and ignore non-directory and dot files 
        
    if ($it->getDepth() >= || ! $it->isDir() || $it->isDot())
            continue;
        
        
    // Print the directory name
        
    echo str_repeat('>'$it->getDepth() + 1), ' '$it->getBasename(), "\n";

    If you're lucky enough to be allowed to play with PHP 5.3, you could also make good use of the FilesystemIterator.
    Salathe
    Software Developer and PHP Manual Author.

  5. #5
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2008
    Posts
    5,757
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    lol

    PHP Code:
    class CallbackFilterIterator extends FilterIterator {
        protected 
    $callback;
        public function 
    __construct(Iterator $it$callback) {
            
    parent::__construct($it);
            
    $this->callback $callback;
        }
        public function 
    accept() {
            return 
    call_user_func($this->callback$this->getInnerIterator());
        }
    }

    $it = new CallbackFilterIterator(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST), function($it) {
        return !
    $it->isDot() && $it->getDepth() < 2;
    });

    foreach(
    $it as $fileInfo){
        echo 
    "{$fileInfo->getPathName()}\n";


  6. #6
    Twitter: @AnthonySterling silver trophy AnthonySterling's Avatar
    Join Date
    Apr 2008
    Location
    North-East, UK.
    Posts
    6,111
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by crmalibu View Post
    lol

    PHP Code:
    class CallbackFilterIterator extends FilterIterator {
        protected 
    $callback;
        public function 
    __construct(Iterator $it$callback) {
            
    parent::__construct($it);
            
    $this->callback $callback;
        }
        public function 
    accept() {
            return 
    call_user_func($this->callback$this->getInnerIterator());
        }
    }

    $it = new CallbackFilterIterator(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST), function($it) {
        return !
    $it->isDot() && $it->getDepth() < 2;
    });

    foreach(
    $it as $fileInfo){
        echo 
    "{$fileInfo->getPathName()}\n";

    Bravo, now that I DO like!

    Although, I'm still unsure of the use of lambda functions. Just when I'm starting to get used to the syntax too!
    @AnthonySterling: I'm a PHP developer, a consultant for oopnorth.com and the organiser of @phpne, a PHP User Group covering the North-East of England.

  7. #7
    Grumpy Minimalist
    Join Date
    Jul 2006
    Location
    Ontario, Canada
    Posts
    424
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Wow! This could quickly spiral into another Write a FOR loop in PHP that prints the numbers 0-9 to the page.

    Not that that's a bad thing; I'd love to bring back my genetic algorithm classes for this problem!

  8. #8
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2008
    Posts
    5,757
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Tarh View Post
    Not that that's a bad thing; I'd love to bring back my genetic algorithm classes for this problem!
    That was the one I remembered most lol.

  9. #9
    @php.net Salathe's Avatar
    Join Date
    Dec 2004
    Location
    Edinburgh
    Posts
    1,398
    Mentioned
    65 Post(s)
    Tagged
    1 Thread(s)
    Quote Originally Posted by Tarh View Post
    I'd love to bring back my genetic algorithm classes for this problem!
    Please do.
    Salathe
    Software Developer and PHP Manual Author.

  10. #10
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2008
    Posts
    5,757
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok, this is scary, because I had never seen this before I wrote that code. I was looking for a way to filter before all the recursion actually happened, and stumbled upon this

    http://www.php.net/~helly/php/ext/sp...nc-source.html


Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •