Finder: búsqueda de archivos
¿Necesita encontrar archivos que coincidan con una máscara determinada? Finder le 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 puede 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 puede cambiarlo usando los métodos in() o from(). La variable $file
es una instancia de la clase FileInfo con muchos métodos útiles. En la clave $name
se encuentra 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 especifica, 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()
puede 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']); // además de 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'); // además de todos los archivos PHP
En la máscara puede usar los comodines *
, **
, ?
y
[...]
. Incluso puede 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. Puede cambiarlo usando los métodos in()
y
from()
. Como se desprende de los nombres de los métodos, in()
busca solo en el directorio especificado,
mientras que from()
busca también en sus subdirectorios (recursivamente). Si desea buscar recursivamente en el
directorio actual, puede 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 *
, **
, ?
. Así,
por ejemplo, con la ruta src/*/*.php
puede 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
busca 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 busque, 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 puede 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 en múltiples niveles)?
– 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()
puede excluir archivos y directorios de la búsqueda. Especifica 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 específicos, use exclude()
:
Finder::findFiles('*.php')
->from($dir)
->exclude('temp', '.git');
Filtrado
Finder ofrece varios métodos para filtrar los resultados (es decir, reducirlos). Puede 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, puede establecer la profundidad máxima de navegación usando el método
limitDepth()
. Si establece 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(fn($file) => $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 necesita encontrar varios archivos diferentes en diferentes ubicaciones o que cumplan otros criterios, use 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, tiene 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 puede usar
foreach
para recorrer los resultados. Está programado de tal manera que los resultados se cargan solo durante el
recorrido, por lo que si tiene una gran cantidad de archivos, no se espera a que se lean todos.
También puede 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();