Finder: búsqueda de archivos
¿Necesitas encontrar archivos que coincidan con una máscara determinada? Finder te ayudará con eso. Es una herramienta versátil y rápida para navegar por la estructura de directorios.
Instalación:
composer require nette/utils
Los ejemplos asumen que se ha creado un alias:
use Nette\Utils\Finder;
Uso
Primero, mostraremos cómo puedes usar Nette\Utils\Finder para listar los nombres de los archivos
con extensiones .txt
y .md
en el directorio actual:
foreach (Finder::findFiles(['*.txt', '*.md']) as $name => $file) {
echo $file;
}
El directorio predeterminado para la búsqueda es el directorio actual, pero puedes cambiarlo usando los métodos in() o from(). La variable $file
es una instancia de la clase FileInfo con muchas métodos útiles. En la clave $name
está la ruta al archivo como una
cadena.
¿Qué buscar?
Además del método findFiles()
, también existe findDirectories()
, que busca solo directorios, y
find()
, que busca ambos. Estos métodos son estáticos, por lo que se pueden llamar sin crear una instancia. El
parámetro con la máscara es opcional, si no lo especificas, se buscará todo.
foreach (Finder::find() as $file) {
echo $file; // ahora se imprimirán todos los archivos y directorios
}
Con los métodos files()
y directories()
puedes especificar qué más buscar. Los métodos se pueden
llamar repetidamente y como parámetro también se puede pasar un array de máscaras:
Finder::findDirectories('vendor') // todos los directorios
->files(['*.php', '*.phpt']); // más todos los archivos PHP
Una alternativa a los métodos estáticos es crear una instancia usando new Finder
(un objeto recién creado de
esta manera no busca nada) y especificar qué buscar usando files()
y directories()
:
(new Finder)
->directories() // todos los directorios
->files('*.php'); // más todos los archivos PHP
En la máscara puedes usar los comodines *
, **
, ?
y
[...]
. Incluso puedes especificar directorios, por ejemplo, src/*.php
buscará todos los archivos PHP en
el directorio src
.
Los enlaces simbólicos también se consideran directorios o archivos.
¿Dónde buscar?
El directorio predeterminado para la búsqueda es el directorio actual. Puedes cambiarlo usando los métodos in()
y from()
. Como se desprende de los nombres de los métodos, in()
busca solo en el directorio dado,
mientras que from()
busca también en sus subdirectorios (recursivamente). Si quieres buscar recursivamente en el
directorio actual, puedes usar from('.')
.
Estos métodos se pueden llamar varias veces o pasarles varias rutas como un array, los archivos se buscarán entonces en
todos los directorios. Si alguno de los directorios no existe, se lanzará una excepción
Nette\UnexpectedValueException
.
Finder::findFiles('*.php')
->in(['src', 'tests']) // busca directamente en src/ y tests/
->from('vendor'); // busca también en los subdirectorios de vendor/
Las rutas relativas son relativas al directorio actual. Por supuesto, también se pueden especificar rutas absolutas:
Finder::findFiles('*.php')
->in('/var/www/html');
En la ruta es posible usar los comodines comodines *
, **
,
?
. Así, por ejemplo, con la ruta src/*/*.php
puedes buscar todos los archivos PHP en los directorios de
segundo nivel dentro del directorio src
. El carácter **
, llamado globstar, es una baza poderosa, ya que
permite buscar también en subdirectorios: con src/**/tests/*.php
buscas todos los archivos PHP en el directorio
tests
ubicado en src
o en cualquiera de sus subdirectorios.
Por el contrario, los comodines [...]
no están soportados en la ruta, es decir, no tienen un significado
especial, para evitar comportamientos no deseados en caso de que busques, por ejemplo, in(__DIR__)
y casualmente en
la ruta aparezcan los caracteres []
.
Al buscar archivos y directorios en profundidad, primero se devuelve el directorio padre y luego los archivos contenidos en
él, lo cual se puede invertir usando childFirst()
.
Comodines
En la máscara puedes usar varios caracteres especiales:
*
– reemplaza cualquier número de caracteres cualesquiera (excepto/
)**
– reemplaza cualquier número de caracteres cualesquiera incluyendo/
(es decir, se puede buscar multinivel)?
– reemplaza un carácter cualquiera (excepto/
)[a-z]
– reemplaza un carácter de la lista de caracteres entre corchetes[!a-z]
– reemplaza un carácter fuera de la lista de caracteres entre corchetes
Ejemplos de uso:
img/?.png
– archivos con nombre de una sola letra0.png
,1.png
,x.png
, etc.logs/[0-9][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9].log
– logs en formatoYYYY-MM-DD
src/**/tests/*
– archivos en el directoriosrc/tests
,src/foo/tests
,src/foo/bar/tests
y así sucesivamente.docs/**.md
– todos los archivos con extensión.md
en todos los subdirectorios del directoriodocs
Exclusión
Con el método exclude()
puedes excluir archivos y directorios de la búsqueda. Especificas una máscara que el
archivo no debe cumplir. Ejemplo de búsqueda de archivos *.txt
excepto aquellos que contienen la letra
X
en el nombre:
Finder::findFiles('*.txt')
->exclude('*X*');
Para omitir la navegación por subdirectorios, usa exclude()
:
Finder::findFiles('*.php')
->from($dir)
->exclude('temp', '.git')
Filtrado
Finder ofrece varios métodos para filtrar los resultados (es decir, reducirlos). Puedes combinarlos y llamarlos repetidamente.
Con size()
filtramos por tamaño de archivo. Así encontramos archivos con un tamaño en el rango de 100 a
200 bytes:
Finder::findFiles('*.php')
->size('>=', 100)
->size('<=', 200);
El método date()
filtra por fecha de última modificación del archivo. Los valores pueden ser absolutos
o relativos a la fecha y hora actuales, por ejemplo, así encontramos archivos modificados en las últimas dos semanas:
Finder::findFiles('*.php')
->date('>', '-2 weeks')
->from($dir)
Ambas funciones entienden los operadores >
, >=
, <
, <=
,
=
, !=
, <>
.
Finder también permite filtrar los resultados usando funciones personalizadas. La función recibe como parámetro un objeto
Nette\Utils\FileInfo
y debe devolver true
para que el archivo sea incluido en los resultados.
Ejemplo: búsqueda de archivos PHP que contienen la cadena Nette
(independientemente de
mayúsculas/minúsculas):
Finder::findFiles('*.php')
->filter(fn($file) => strcasecmp($file->read(), 'Nette') === 0);
Filtrado en profundidad
Durante la búsqueda recursiva, puedes establecer la profundidad máxima de navegación usando el método
limitDepth()
. Si estableces limitDepth(1)
, solo se navega por los primeros subdirectorios,
limitDepth(0)
desactiva la navegación en profundidad y el valor –1 cancela el límite.
Finder permite, usando funciones personalizadas, decidir en qué directorio entrar durante la navegación. La función recibe
como parámetro un objeto Nette\Utils\FileInfo
y debe devolver true
para entrar en el directorio:
Finder::findFiles('*.php')
->descentFilter($file->getBasename() !== 'temp');
Ordenación
Finder también ofrece varias funciones para ordenar los resultados.
El método sortByName()
ordena los resultados por nombres de archivo. La ordenación es natural, es decir, maneja
correctamente los números en los nombres y devuelve, por ejemplo, foo1.txt
antes de foo10.txt
.
Finder también permite ordenar usando una función personalizada. Esta recibe como parámetro dos objetos
Nette\Utils\FileInfo
y debe devolver el resultado de la comparación con el operador <=>
, es
decir, -1
, 0
o 1
. Por ejemplo, así ordenamos los archivos por tamaño:
$finder->sortBy(fn($a, $b) => $a->getSize() <=> $b->getSize());
Múltiples búsquedas diferentes
Si necesitas encontrar varios archivos diferentes en diferentes ubicaciones o que cumplan otros criterios, usa el método
append()
. Devuelve un nuevo objeto Finder
, por lo que es posible encadenar llamadas a métodos:
($finder = new Finder) // ¡guardamos el primer Finder en la variable $finder!
->files('*.php') // en src/ buscamos archivos *.php
->from('src')
->append()
->files('*.md') // en docs/ buscamos archivos *.md
->from('docs')
->append()
->files('*.json'); // en la carpeta actual buscamos archivos *.json
Alternativamente, se puede usar el método append()
para añadir un archivo específico (o un array de archivos).
Entonces devuelve el mismo objeto Finder
:
$finder = Finder::findFiles('*.txt')
->append(__FILE__);
FileInfo
Nette\Utils\FileInfo es una clase que representa un archivo o directorio en los resultados de la búsqueda. Es una extensión de la clase SplFileInfo, que proporciona información como el tamaño del archivo, fecha de última modificación, nombre, ruta, etc.
Además, proporciona métodos para devolver la ruta relativa, lo cual es útil durante la navegación en profundidad:
foreach (Finder::findFiles('*.jpg')->from('.') as $file) {
$absoluteFilePath = $file->getRealPath();
$relativeFilePath = $file->getRelativePathname();
}
Además, tienes disponibles métodos para leer y escribir el contenido del archivo:
foreach ($finder as $file) {
$contents = $file->read();
// ...
$file->write($contents);
}
Devolución de resultados como array
Como se ha visto en los ejemplos, Finder implementa la interfaz IteratorAggregate
, por lo que puedes usar
foreach
para recorrer los resultados. Está programado de tal manera que los resultados se cargan solo durante el
recorrido, por lo que si tienes una gran cantidad de archivos, no se espera a que se lean todos.
También puedes obtener los resultados como un array de objetos Nette\Utils\FileInfo
, usando el método
collect()
. El array no es asociativo, sino numérico.
$array = $finder->findFiles('*.php')->collect();