0

I want to search and list specific folders only and no matter how deep these folders are kept.

For instance, below is how I structure them,

local/
     app/
        master/
             models/
             views/
        slaves/
             models/
             views/
     scr/
     models/
     index.php

And I just want to list the folder of models into an array,

local/app/master/models/
local/app/slaves/models/
local/models/

My working code,

$directories = array();

$results = array_diff( scandir("local"), array(".", "..") );

foreach ($results as $result)
{
    if (is_dir("local/".$result)) {

        $directories[] = $result;
    }
}

var_dump($directories);

result,

array
  0 => string 'app' (length=3)
  1 => string 'models' (length=6)
  2 => string 'src' (length=3)

Any ideas?

Run
  • 54,938
  • 169
  • 450
  • 748
  • First create an array of all directories (see [recursive directory iterator](http://stackoverflow.com/questions/3556781/use-recursivedirectoryiterator-to-list-directories-and-files-into-array)) and then filter that array to only those entries you're interested in (see [filter iterator](http://php.net/manual/en/class.filteriterator.php)). – hakre Feb 15 '12 at 13:52
  • If you only want folders undr 'models', why are you using `scandir("local")` instead of `scandir("local/models")`? To list folders on any level, you'll need a recursive function. – bfavaretto Feb 15 '12 at 13:52
  • 1
    possible duplicate of [Help Using RegexIterator in PHP](http://stackoverflow.com/questions/3321547/help-using-regexiterator-in-php) – Gordon Feb 15 '12 at 13:56

3 Answers3

2

You are only scanning one level deep and are not checking for the name of the folder/file. Instead, you'll need to scan recursively. Try something like this. I haven't tested this code - but you get the idea.

$directories = array();
get_directories('local', $directories);
print_r($directories);

function get_directories($path, &$directories)
{
    $dirs = scandir($path)
    foreach($dirs as $d)
    {
        if(is_dir("$path/$d")
        {
            if($d == 'model')
                $directories[] = "$path/$d";
            elseif($d != '.' && $d != '..')
                get_directories("$path/$d", $directories);
        }
    }
}

At the same time, if you don't need to do this in PHP, but just find all directories model, you can do this easily with shell command find:

$ find local/ -name model -type d
Aleks G
  • 56,435
  • 29
  • 168
  • 265
1
// Create an object that allows us to iterate directories recursively
// Stolen from here: 
// http://www.php.net/manual/en/class.recursivedirectoryiterator.php#102587
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir),
                                          RecursiveIteratorIterator::CHILD_FIRST);

// This will hold the result
$result = array();

// Loop the directory contents
foreach ($iterator as $path) {

  // If object is a directory and matches the search term ('models')...
  if ($path->isDir() && $path->getBasename() === 'models') {

    // Add it to the result array
    $result[] = (string) $path;

  }

}

print_r($result);
DaveRandom
  • 87,921
  • 11
  • 154
  • 174
  • Hint: [SplFileInfo::getBasename](http://www.php.net/manual/en/splfileinfo.getbasename.php) – hakre Feb 15 '12 at 13:57
  • @hakre Interesting - does that basically do what the `$dir = ...` line does? The docs don't seem to suggest it does anything different to `basename()` - what does it do differently? – DaveRandom Feb 15 '12 at 14:06
  • Nice, however the memory consumption of this can be huge if he has a very large directory tree with only a few matches. – Aleks G Feb 15 '12 at 14:08
  • 1
    `if ($path->isDir() && $path->getBasename() === 'models') $result[] = (string) $path;` - you don't need to care about the type of slashes nor the rtrimming. It's just more clear what the code does as well. – hakre Feb 15 '12 at 14:08
  • @AleksG Where does that memory consumption come from? My understanding is that `RecursiveDirectoryIterator` reads the file system live as you loop it, rather than loading all info into memory... is that not correct?# – DaveRandom Feb 15 '12 at 14:10
  • @AleksG: RecursiveIteration does not consume much memory (far less than recursive function calls as SPL uses stacks internally instead of recursive function calls. Next to that, a `FilterIterator` (not shown in this answer) would further reduce the memory usage as no superset of all directories is kept in memory at all then - but this ain't the case with this answer anyway, only the wanted strings are stored into the `$result` array. – hakre Feb 15 '12 at 14:10
  • DaveRandom, @hakre I'm not worried about the RecursiveIterator, but rather about building the full array of all recursive files/directories. If I were to build such array on my music collection, it would have over a million entries. – Aleks G Feb 15 '12 at 14:12
  • @AleksG But that is exactly what the OP wants to do - build an array of all the matching objects. If that results in an enormous array, handling that problem is beyond the scope of the original question. – DaveRandom Feb 15 '12 at 14:14
  • I think I'm getting confused here - I meant the array of ALL files would be a memory hog, not the array of matches. Original hakre's comment stated "build an array of all paths". I'm not sure how internally RecursiveIterator works, but hopefully it doesn't build such an array and iterates on the fly. – Aleks G Feb 15 '12 at 14:16
  • @AleksG: No, only the directories that have `models` as basename. Everything else is not kept anywhere. The array of all files would be a non-iterative approach to make the problem less complicated and to show a way. I already linked iterators, so it was not very precise to say array in the comment. With iterators you don't have an array but you have the nice `foreach` and treat it array-like. And you don't have the recursive function calls as in your answer which often is a benefit (yours works, too, it's also solving the problem in a valid way). – hakre Feb 15 '12 at 14:16
  • @hakre: Thanks for explaining. I knew I was missing something. I never had a need to use the iterator myself, so wasn't sure how it worked. – Aleks G Feb 15 '12 at 14:24
1

Here is another solution without recursion and which will work in php 4 as well as in php 5

<?php
$dir ='local';
while($dirs = glob($dir . '/*', GLOB_ONLYDIR)) {
        $dir .= '/*';
        if (!$d) {
                $d =$dirs;
        } else {
                $d=array_merge($d,$dirs);
        }
}

$dir_to_match = 'models';

$result = array();
foreach ($d as $dir_name) {
        if (preg_match('#/' . $dir_to_match . '$#', $dir_name)) {
                $result[] = $dir_name;
        }
}
var_dump($result);
?>
alex347
  • 621
  • 7
  • 18