简体   繁体   English

PHP RecursiveDirectoryIterator返回但不遍历匹配“x”的目录

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

I am writing a disk catalogue application in PHP. 我正在用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: 我已经实现了一个RecursiveCallbackFilterIterator,它允许根据匹配的文件名模式省略目录:

$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). 到目前为止,我发现RecursiveCallbackFilterIterator的所有示例都显示了上面的一些变化(例如,省略某些文件或目录)。 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. 我已经创建了一个eval.in来测试它,虽然在这个环境中我无法创建目录,所以我只测试文件,但是也应该和dir一样。

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. 感谢Edwin的回答,我能够创建完美的代码。 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. 关键是我需要更多地了解splFileInfo可用的方法,特别是Path。 By checking Path, it is possible to know if the parent, and not the filename, contains a wildcard. 通过检查Path,可以知道父文件,而不是文件名,是否包含通配符。 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. 将此与fnmatch相结合,我们可以推测,如果文件位于“.app”目录的下游,则完全跳过该分支,同时仍包括父级。 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
    );

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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