3

我正在用 PHP 编写磁盘目录应用程序。我的脚本循环遍历目录,将所有文件名和元数据存储在数据库中。有些目录我不想向下移动。我希望迭代器简单地返回这些目录的名称,就好像它们是文件一样,然后移动到下一个兄弟。我已经实现了一个 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";
    }

我的问题是,如何修改此代码,使与模式匹配的目录包含在结果集中,但不返回给迭代器进行进一步遍历?

到目前为止,我发现的所有 RecursiveCallbackFilterIterator 示例都显示了上述内容的一些变化(例如,省略某些文件或目录)。如果它与模式匹配,我只想返回目录名称,然后转到下一个兄弟。

换句话说,我需要转这个:

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

进入这个:

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

2 回答 2

1

我创建了一个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";
}

输出是:

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
于 2017-05-18T08:57:16.153 回答
0

感谢 Edwin 的回答,我能够创建完美运行的代码。我想我会在这里分享它,以防它对其他人有帮助。关键是我需要更多地了解 splFileInfo 可用的方法,尤其是 Path。通过检查路径,可以知道父文件(而不是文件名)是否包含通配符。将其与 fnmatch 结合,我们可以推测文件是否位于“.app”目录的下游,并完全跳过该分支,同时仍包括父级。谢谢埃德温!

// 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
    );
于 2017-05-18T14:08:45.107 回答