RecursiveFilterIterator reiniciado en RecursiveIteratorIterator?

La forma estándar de escanear recursivamente directorios a través de iteradores SPL es:

$files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::CHILD_FIRST ); foreach ($files as $file) { print $file->getPathname() . PHP_EOL; } 

Quiero un conjunto composable de filtros para aplicar a mi búsqueda recursiva de archivos. Estoy usando un RecursiveDirectoryIterator para escanear una estructura de directorio.

Quiero aplicar más de un filtro a mi estructura de directorio.

Mi código de configuración:

 $filters = new FilterRuleset( new RecursiveDirectoryIterator($path) ); $filters->addFilter(new FilterLapsedDirs); $filters->addFilter(new IncludeExtension('wav')); $files = new RecursiveIteratorIterator( $filters, RecursiveIteratorIterator::CHILD_FIRST ); 

Pensé que podría aplicar N filtros usando el conjunto de reglas:

 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; } } 

El filtro que configuré no funciona como estaba previsto. Cuando compruebo los filtros en FilterRuleset , se completan en la primera llamada y luego en blanco en las siguientes. Es como si internamente RecursiveIteratorIterator está volviendo a instanciar mi FilterRuleset .

  public function accept() { print_r($this->filters); $file = $this->current(); foreach ($this->filters as $filter) { if (!$filter->accept($file)) { return false; } } return true; } 

Salida:

 Array ( [0] => FilterLapsedDirs Object ( ) [1] => IncludeExtension Object ( [ext:private] => wav ) ) Array ( ) Array ( ) Array ( ) Array ( ) Array ( ) Array ( ) 

Estoy usando PHP 5.1.6 pero lo he probado en 5.4.14 y no hay diferencia. ¿Algunas ideas?

Cuando compruebo los filtros en FilterRuleset, se completan en la primera llamada y luego en blanco en las siguientes. Es como si internamente RecursiveIteratorIterator está volviendo a instanciar mi FilterRuleset.

Sí, este es exactamente el caso. Cada vez que ingresa en un subdirectorio, la matriz está vacía porque según las reglas iterativas recursivas, el iterador de filtro recursivo debe proporcionar los iteradores hijo.

Entonces tienes dos opciones aquí:

  1. Aplique filtros en la iteración aplanada, es decir, después del recorrido del árbol. Parece factible en su caso siempre que solo necesite filtrar cada archivo individual, no los secundarios.
  2. La forma estándar: Tenga cuidado de que getChildren() devuelva un objeto FilterRuleset recursive-filter- FilterRuleset configurado con los filtros establecidos.

Comienzo con el segundo porque está hecho rápidamente y es la manera normal de hacerlo.

getChildren() método padre getChildren() añadiéndolo a tu clase. Luego toma el resultado del padre (que es el nuevo FilterRuleset para los niños y establece el miembro privado. Esto es posible en PHP (en caso de que se pregunte si su trabajo es un miembro privado) porque está en el mismo nivel que el jerarquía de clases. Luego, simplemente regresa y listo:

 class FilterRuleset extends RecursiveFilterIterator { private $filters = array(); ... public function getChildren() { $children = parent::getChildren(); $children->filters = $this->filters; return $children; } } 

La otra (primera) variante es que básicamente la “degrada” al filtro “plano”, que es un FilterIterator estándar. Por lo tanto, primero realiza la iteración recursiva con un RecursiveIteratorIterator y luego ajusta eso en su filtro-iterador. Como el árbol ya ha sido recorrido por el iterador anterior, ya no se necesita todo este material recursivo.

Entonces, antes que nada FilterIterator en un FilterIterator :

 class FilterRuleset extends FilterIterator { ... } 

El único cambio es de lo que extiendes con esa clase. Y la instancia en un orden ligeramente diferente:

 $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; } 

Espero que estos ejemplos sean claros. Si juegas con esto y te encuentras con un problema (o incluso si no), los comentarios siempre son bienvenidos.

Ah y antes de que lo olvide: esta es la simulación que utilicé para crear filtros en mi ejemplo anterior:

 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); } }