简体   繁体   中英

PHP RecursiveDirectoryIterator return but do not traverse directory matching “x”

I am writing a disk catalogue application in PHP. My script loops over directories, storing all filenames and metadata in a database. There are certain directories I do not want to travel down. I want the iterator to simply return the names of those directories as though they are files, then move onto the next sibling. I have implemented a RecursiveCallbackFilterIterator that allows omitting directories based on a matching filename pattern:

$filter = array(".app");

$files = new RecursiveIteratorIterator(
    new RecursiveCallbackFilterIterator(
        new RecursiveDirectoryIterator(
            $zpath,
            RecursiveDirectoryIterator::SKIP_DOTS
            ),
        function ($current, $key, $iterator) use ($filter) {
            $match = 0;
            foreach ($filter as $skip) {
                if (substr($current->getBaseName(), -4, 4) == $skip) {
                    $match = 1;
                    }
                }
            if ($match) {
                return false;
                } else {
                return true;
                }
            }
        ),
    RecursiveIteratorIterator::SELF_FIRST,
    RecursiveIteratorIterator::CATCH_GET_CHILD
    );

foreach ($files as $splFileInfo) {
    $path = $splFileInfo->getRealPath();
    echo $path."\n";
    }

My question is, how do I modify this code so that directories matching the pattern are included in the result set, but not returned to the iterator for further traversal?

So far, all examples of RecursiveCallbackFilterIterator I've found show some variation on the above (eg, omit certain files or directories). I simply want to return the directory name if it matches the pattern, then move on to the next sibling.

In other words, I need to turn this:

File1.txt
File2.txt
Folder1/
Folder1/FileA.txt
Folder1/FileB.txt
MyThing.app/
MyThing.app/Contents/
Mything.app/Contents/Manifest.plist
Mything.app/Menu.nib
Portfolio.zip
Zee.txt

Into this:

File1.txt
File2.txt
Folder1/
Folder1/FileA.txt
Folder1/FileB.txt
MyThing.app
Portfolio.zip
Zee.txt

I've created an eval.in to test this, though on this env I cannot create Directories, so I've testet only with files, but should work the same with dir too.

file_put_contents("./file2.txt", "test");
file_put_contents("./Zee.txt", "test");
file_put_contents("./fileA.txt", "test");
file_put_contents("./fileB.txt", "test");
file_put_contents("./manifest.plist", "test");
file_put_contents("./manifest.app", "test");
file_put_contents("./MyApp.app", "test");
file_put_contents("./Menu.nib", "test");
$zpath=realpath("./");

$filter = array(".app");
$appFolders =array();


$files = new RecursiveIteratorIterator(
    new RecursiveCallbackFilterIterator(
    new RecursiveDirectoryIterator(
        $zpath,
        RecursiveDirectoryIterator::SKIP_DOTS
        ),
    function ($current, $key, $iterator) use ($filter) {   
        foreach ($filter as $skip) {
            preg_match_all("(".$skip.")", $current->getRealPath(), $result);
            if (!empty($result[0])) {                    
                $GLOBALS["appFolders"][] =$current->getRealPath();
                return false;
            }
        }
        return true;
     }
  ),
  RecursiveIteratorIterator::SELF_FIRST,
  RecursiveIteratorIterator::CATCH_GET_CHILD
  );

  echo "\nFiles:\n";
foreach ($files as $splFileInfo) {
    $path = $splFileInfo->getRealPath();
    echo $path."\n";
}
echo "\nAppFolders:\n";
foreach ($appFolders  as $app){
    echo $app."\n";
}

And the output is:

Files:
/tmp/execpad-4917ea112c86/file2.txt
/tmp/execpad-4917ea112c86/input-4917ea112c86
/tmp/execpad-4917ea112c86/manifest.plist
/tmp/execpad-4917ea112c86/Menu.nib
/tmp/execpad-4917ea112c86/output-4917ea112c86
/tmp/execpad-4917ea112c86/fileA.txt
/tmp/execpad-4917ea112c86/fileB.txt
/tmp/execpad-4917ea112c86/Zee.txt
/tmp/execpad-4917ea112c86/source-4917ea112c86

AppFolders:
/tmp/execpad-4917ea112c86/MyApp.app
/tmp/execpad-4917ea112c86/manifest.app

Thanks to Edwin's answer, I was able to create this code that is working perfectly. I thought I would share it here in case it is helpful to anyone else. The key was that I needed to learn more about the methods available to splFileInfo, particularly Path. By checking Path, it is possible to know if the parent, and not the filename, contains a wildcard. Combining this with fnmatch, we can then surmise if a file is downstream from a ".app" dir, and skip that branch entirely, while still including the parent. Thanks Edwin!

// Do not descend into matching directories
$wopt_nodescend = array("*.app", "*.sparsebundle");

// Ignore matching files and directories
$wopt_ignore = array(".DS_Store", "*.jdk");

$nodescended = 0;
$ignored = 0;
$files = new RecursiveIteratorIterator(
    new RecursiveCallbackFilterIterator(
        new RecursiveDirectoryIterator(
            $zpath,
            RecursiveDirectoryIterator::SKIP_DOTS
            ),
        function ($current, $key, $iterator) use ($wopt_ignore, $wopt_nodescend) {
            global $nodescended, $ignored;
            $clean = true;
            if (is_array($wopt_ignore)) {
                foreach ($wopt_ignore as $wildcard) {
                    if (fnmatch($wildcard, $current->getFilename())) {
                        $clean = false;
                        $ignored++;
                        echo "Skipping: ".$current->getFilename()."\n";
                        }
                    }
                }
            if (is_array($wopt_nodescend)) {
                foreach ($wopt_nodescend as $wildcard) {
                    if (fnmatch($wildcard, $current->getPath())) {
                        $clean = false;
                        $nodescended++;
                        echo "Nodescending: ".$current->getFilename()."\n";
                        }
                    }
                }
            return $clean;
            }
        ),
    RecursiveIteratorIterator::SELF_FIRST,
    RecursiveIteratorIterator::CATCH_GET_CHILD
    );

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM