通过SPL迭代器递归扫描目录的标准方法是:
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($files as $file) {
print $file->getPathname() . PHP_EOL;
}
我想要一组可组合的过滤器应用于我的递归文件搜索。我正在使用RecursiveDirectoryIterator
来扫描目录结构。
我想在我的目录结构中应用多个过滤器。
我的设置代码:
$filters = new FilterRuleset(
new RecursiveDirectoryIterator($path)
);
$filters->addFilter(new FilterLapsedDirs);
$filters->addFilter(new IncludeExtension('wav'));
$files = new RecursiveIteratorIterator(
$filters, RecursiveIteratorIterator::CHILD_FIRST
);
我以为我可以使用规则集来应用N个过滤器:
class FilterRuleset extends RecursiveFilterIterator {
private $filters = array();
public function addFilter($filter) {
$this->filters[] = $filter;
}
public function accept() {
$file = $this->current();
foreach ($this->filters as $filter) {
if (!$filter->accept($file)) {
return false;
}
}
return true;
}
}
我设置的过滤无法按预期工作。当我检查FilterRuleset
中的过滤器时,它们会在第一次调用时填充,然后在后续调用中为空。它好像在内部RecursiveIteratorIterator
正在重新实例化我的FilterRuleset
。
public function accept() {
print_r($this->filters);
$file = $this->current();
foreach ($this->filters as $filter) {
if (!$filter->accept($file)) {
return false;
}
}
return true;
}
输出:
Array
(
[0] => FilterLapsedDirs Object
(
)
[1] => IncludeExtension Object
(
[ext:private] => wav
)
)
Array
(
)
Array
(
)
Array
(
)
Array
(
)
Array
(
)
Array
(
)
我使用的是PHP 5.1.6,但已经在5.4.14上进行了测试,并没有区别。有什么想法吗?
答案 0 :(得分:6)
当我检查FilterRuleset中的过滤器时,它们会在第一次调用时填充,然后在后续调用时填充。它好像在内部RecursiveIteratorIterator重新实例化我的FilterRuleset。
是的,情况确实如此。每次进入子目录时,数组都是空的,因为每个递归迭代器规则,递归过滤器迭代器需要提供子迭代器。
所以你在这里有两个选择:
getChildren()
返回已设置过滤器的已配置FilterRuleset
递归过滤器 - 迭代器对象。我从第二个开始,因为它已经快速完成并且是正常的方法。
通过将getChildren()
父方法添加到您的班级来覆盖它。然后你获取父项的结果(这是子项的新FilterRuleset
并设置私有成员。这在PHP中是可能的(如果你想知道他的作品是因为它是私人成员)因为它在类层次结构的同一级别上。然后你只需返回并完成:
class FilterRuleset extends RecursiveFilterIterator
{
private $filters = array();
...
public function getChildren() {
$children = parent::getChildren();
$children->filters = $this->filters;
return $children;
}
}
另一个(第一个)变体是你基本上"降级"它到了#34; flat"过滤器,即标准FilterIterator
。因此,您首先使用RecursiveIteratorIterator
进行递归迭代,然后将其包装到filter-iterator中。由于树已经被早期的迭代器遍历了,所以不再需要所有这些递归的东西了。
首先将其转换为FilterIterator
:
class FilterRuleset extends FilterIterator
{
...
}
唯一的变化是你在课堂上的延伸。并且您以稍微不同的顺序实例化:
$path = __DIR__;
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
$filtered = new FilterRuleset($files);
$filtered->addFilter(Accept::byCallback(function () {
return true;
}));
foreach ($filtered as $file) {
echo $file->getPathname(), PHP_EOL;
}
我希望这些例子清楚。如果您使用这些并遇到问题(或者即使没有),也欢迎提供反馈。
啊,在我忘记它之前:以下是我在上面的例子中用来创建过滤器的模拟:
class Accept
{
private $callback;
public function __construct($callback) {
$this->callback = $callback;
}
public function accept($subject) {
return call_user_func($this->callback, $subject);
}
public static function byCallback($callback) {
return new self($callback);
}
}