php regex iterator spl

Cómo usar RegexIterator en PHP



spl (3)

Todavía tengo que encontrar un buen ejemplo de cómo usar php RegexIterator para recorrer recursivamente un directorio.

El resultado final sería que quiero especificar un directorio y encontrar todos los archivos en él con algunas extensiones dadas. Digamos, por ejemplo, solo extensiones html / php. Además, quiero filtrar carpetas del tipo .Trash-0, .Trash-500, etc.

<?php $Directory = new RecursiveDirectoryIterator("/var/www/dev/"); $It = new RecursiveIteratorIterator($Directory); $Regex = new RegexIterator($It,''/^.+/.php$/i'',RecursiveRegexIterator::GET_MATCH); foreach($Regex as $v){ echo $value."<br/>"; } ?>

Es lo que tengo hasta ahora, pero el resultado es: Error fatal: excepción no detectada ''UnexpectedValueException'' con el mensaje ''RecursiveDirectoryIterator :: __ construct (/media/hdmovies1/.Trash-0)

¿Alguna sugerencia?


Hay un par de maneras diferentes de hacer algo como esto, te daré dos enfoques rápidos para que elijas: rápido y sucio, versus más largo y menos sucio (aunque es viernes por la noche, así que podemos irnos) un poquito loco).

1. Rápido (y sucio)

Esto implica simplemente escribir una expresión regular (podría dividirse en múltiples) para usarla para filtrar la colección de archivos de una sola vez.

(Solo las dos líneas comentadas son realmente importantes para el concepto).

$directory = new RecursiveDirectoryIterator(__DIR__); $flattened = new RecursiveIteratorIterator($directory); // Make sure the path does not contain "/.Trash*" folders and ends eith a .php or .html file $files = new RegexIterator($flattened, ''#^(?:[A-Z]:)?(?:/(?!/.Trash)[^/]+)+/[^/]+/.(?:php|html)$#Di''); foreach($files as $file) { echo $file . PHP_EOL; }

Este enfoque tiene una serie de problemas, aunque es rápido implementar siendo solo un trazador de líneas (aunque la expresión regular podría ser difícil de descifrar).

2. Menos rápido (y menos sucio)

Un enfoque más reutilizable es crear un par de filtros a medida (utilizando expresiones regulares, o lo que quiera) para reducir gradualmente la lista de elementos disponibles en el RecursiveDirectoryIterator inicial a solo aquellos que desee. El siguiente es solo un ejemplo, escrito rápidamente solo para usted, de la extensión del RecursiveRegexIterator .

Comenzamos con una clase base cuyo trabajo principal es mantener un control de la expresión regular con la que queremos filtrar, todo lo demás se difiere de nuevo al RecursiveRegexIterator . Tenga en cuenta que la clase es abstract ya que en realidad no hace nada útil: el filtrado real debe ser realizado por las dos clases que ampliarán este. Además, se puede llamar FilesystemRegexFilter pero no hay nada que lo fuerce (en este nivel) a filtrar las clases relacionadas con el sistema de archivos (hubiera elegido un nombre mejor, si no tuviera tanto sueño).

abstract class FilesystemRegexFilter extends RecursiveRegexIterator { protected $regex; public function __construct(RecursiveIterator $it, $regex) { $this->regex = $regex; parent::__construct($it, $regex); } }

Estas dos clases son filtros muy básicos, que actúan sobre el nombre del archivo y el nombre del directorio, respectivamente.

class FilenameFilter extends FilesystemRegexFilter { // Filter files against the regex public function accept() { return ( ! $this->isFile() || preg_match($this->regex, $this->getFilename())); } } class DirnameFilter extends FilesystemRegexFilter { // Filter directories against the regex public function accept() { return ( ! $this->isDir() || preg_match($this->regex, $this->getFilename())); } }

Para poner esto en práctica, lo siguiente itera recursivamente sobre los contenidos del directorio en el que reside el script (¡siéntase libre de editar esto!) Y filtra las carpetas .Trash (asegurándose de que los nombres de las carpetas coincidan con la expresión regular especialmente diseñada) y aceptando solo archivos PHP y HTML.

$directory = new RecursiveDirectoryIterator(__DIR__); // Filter out ".Trash*" folders $filter = new DirnameFilter($directory, ''/^(?!/.Trash)/''); // Filter PHP/HTML files $filter = new FilenameFilter($filter, ''//.(?:php|html)$/''); foreach(new RecursiveIteratorIterator($filter) as $file) { echo $file . PHP_EOL; }

De particular interés es que dado que nuestros filtros son recursivos, podemos elegir jugar con cómo iterar sobre ellos. Por ejemplo, podríamos limitarnos fácilmente a solo escanear hasta 2 niveles de profundidad (incluida la carpeta de inicio) haciendo:

$files = new RecursiveIteratorIterator($filter); $files->setMaxDepth(1); // Two levels, the parameter is zero-based. foreach($files as $file) { echo $file . PHP_EOL; }

También es súper fácil agregar aún más filtros (instanciando más de nuestras clases de filtrado con diferentes expresiones regulares, o creando nuevas clases de filtrado) para necesidades de filtrado más especializadas (por ejemplo, tamaño de archivo, longitud de ruta completa, etc.).

PS Hmm esta respuesta balbucea un poco; Traté de mantenerlo lo más conciso posible (incluso eliminando vastas franjas de super-balbuceo). Disculpas si el resultado neto deja la respuesta incoherente.


Los documentos no son de mucha ayuda. Hay un problema al utilizar una expresión regular para ''no coincide'' aquí, pero primero ilustraremos un ejemplo de trabajo:

<?php //we want to iterate a directory $Directory = new RecursiveDirectoryIterator("/var/dir"); //we need to iterate recursively $It = new RecursiveIteratorIterator($Directory); //We want to stop decending in directories named ''.Trash[0-9]+'' $Regex1 = new RecursiveRegexIterator($It,''%([^0-9]|^)(?<!/.Trash-)[0-9]*$%''); //But, still continue on doing it **recursively** $It2 = new RecursiveIteratorIterator($Regex1); //Now, match files $Regex2 = new RegexIterator($It2,''//.php$/i''); foreach($Regex2 as $v){ echo $v."/n"; } ?>

El problema es que la .Trash[0-9]{3} no coincide con .Trash[0-9]{3} : la única forma en que sé cómo hacer coincidir negativamente el directorio es coincidir con el final de la cadena $ , y luego afirmar con un lookbehind (?<!/foo) ''si no está precedido por'' / foo ''.

Sin embargo, como .Trash[0-9]{1,3} no es de longitud fija, no podemos usarlo como una aseveración lookbehind. Desafortunadamente, no hay ''coincidencia invertida'' para un RegexIterator. Pero quizás haya más personas expertas en regex que yo sepa cómo hacer coincidir ''cualquier cadena que no termine con .Trash[0-9]+

editar : lo obtuve ''%([^0-9]|^)(?<!/.Trash-)[0-9]*$%'' como una expresión regular haría el truco.


Una mejora de salathe, sería olvidarse de la clase abstracta personalizada. Simplemente use un buen OOP en PHP y extienda RecursiveRegexIterator directamente en su lugar:

Aquí está el filtro de archivos

class FilenameFilter extends RecursiveRegexIterator { // Filter files against the regex public function accept() { return ! $this->isFile() || parent::accept(); } }

Y el filtro Directorio

class DirnameFilter extends RecursiveRegexIterator { // Filter directories against the regex public function accept() { return ! $this->isDir() || parent::accept(); } }