Finder: Busca de arquivos
Precisa encontrar arquivos que combinem com uma determinada máscara? O Finder pode lhe ajudar. É uma ferramenta versátil e rápida para navegar na estrutura do diretório.
Instalação:
composer require nette/utils
Os exemplos assumem que um pseudônimo foi criado:
use Nette\Utils\Finder;
Usando
Primeiro, vamos ver como você pode usar Nette\Utils\Finder para listar os nomes dos arquivos com as
extensões .txt
e .md
no diretório atual:
foreach (Finder::findFiles(['*.txt', '*.md']) as $name => $file) {
echo $file;
}
O diretório padrão para a busca é o diretório atual, mas você pode mudá-lo usando os métodos in() ou from(). A variável $file
é uma instância da classe FileInfo com muitos métodos úteis. A chave $name
contém o caminho para o arquivo como
uma string.
O que pesquisar?
Além do método findFiles()
, há também findDirectories()
, que busca somente diretórios, e
find()
, que busca ambos. Estes métodos são estáticos, portanto, podem ser chamados sem criar uma instância.
O parâmetro da máscara é opcional, se você não especificá-lo, tudo é pesquisado.
foreach (Finder::find() as $file) {
echo $file; // agora todos os arquivos e diretórios estão listados
}
Use os métodos files()
e directories()
para adicionar o que mais procurar. Os métodos podem ser
chamados repetidamente e um conjunto de máscaras pode ser fornecido como parâmetro:
Finder::findDirectories('vendor') // todos os diretórios
->files(['*.php', '*.phpt']); // mais todos os arquivos PHP
Uma alternativa aos métodos estáticos é criar uma instância usando new Finder
(o objeto fresco criado desta
forma não procura por nada) e especificar o que procurar usando files()
e directories()
:
(new Finder)
->directories() // todos os diretórios
->files('*.php'); // mais todos os arquivos PHP
Você pode usar wildcards *
, **
, ?
and [...]
na máscara. Você pode até mesmo especificar em diretórios, por exemplo src/*.php
procurará por todos os arquivos
PHP no diretório src
.
Os links simbólicos também são considerados diretórios ou arquivos.
Onde pesquisar?
O diretório padrão de busca é o diretório atual. Você pode mudar isto usando os métodos in()
e
from()
. Como você pode ver pelos nomes dos métodos, in()
pesquisa somente o diretório atual,
enquanto from()
pesquisa também seus subdiretórios (recursivamente). Se você quiser pesquisar recursivamente no
diretório atual, você pode usar from('.')
.
Estes métodos podem ser chamados várias vezes ou você pode passar vários caminhos para eles como matrizes, então os
arquivos serão pesquisados em todos os diretórios. Se um dos diretórios não existir, um
Nette\UnexpectedValueException
é lançado.
Finder::findFiles('*.php')
->in(['src', 'tests']) // pesquisa diretamente em src/ e testes/
->from('vendor'); // pesquisas também em vendor/ subdiretórios
Os caminhos relativos são relativos ao diretório atual. Naturalmente, caminhos absolutos também podem ser especificados:
Finder::findFiles('*.php')
->in('/var/www/html');
Wildcards wildcards *
, **
, ?
can be used in the path. For
example, you can use the path src/*/*.php
para procurar por todos os arquivos PHP nos diretórios de segundo nível
no diretório src
. O personagem **
, chamado globstar, é um poderoso trunfo porque permite que você
procure também nos subdiretórios: use src/**/tests/*.php
para procurar todos os arquivos PHP no diretório
tests
localizado em src
ou em qualquer um de seus subdiretórios.
Por outro lado, os wildcards [...]
Os caracteres não são suportados no caminho, ou seja, eles não têm nenhum
significado especial para evitar comportamentos indesejados caso você procure por exemplo in(__DIR__)
e por acaso
[]
caracteres aparecem no caminho.
Ao pesquisar arquivos e diretórios em profundidade, o diretório pai é retornado primeiro e depois os arquivos contidos
nele, que podem ser revertidos com childFirst()
.
Wildcards
Você pode usar vários caracteres especiais na máscara:
*
– replaces any number of arbitrary characters (except/
)**
– substitui qualquer número de caracteres arbitrários, incluindo/
(ou seja, pode ser pesquisado em vários níveis)?
– replaces one arbitrary character (except/
)[a-z]
– substitui um caractere da lista de caracteres entre parênteses rectos[!a-z]
– substitui um caractere fora da lista de caracteres entre parênteses rectos
Exemplos de uso:
img/?.png
– arquivos com o nome de uma única letra0.png
,1.png
,x.png
, etc.logs/[0-9][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9].log
– arquivos de log no formatoYYYY-MM-DD
src/**/tests/*
– arquivos no diretóriosrc/tests
,src/foo/tests
,src/foo/bar/tests
e assim por diante.docs/**.md
– todos os arquivos com a extensão.md
em todos os subdiretórios do diretóriodocs
Excluindo
Use o método exclude()
para excluir arquivos e diretórios das buscas. Você especifica uma máscara que
o arquivo não deve corresponder. Exemplo de busca de arquivos *.txt
, exceto aqueles que contêm a letra
X
no nome:
Finder::findFiles('*.txt')
->exclude('*X*');
Use exclude()
para pular os subdiretórios navegados:
Finder::findFiles('*.php')
->from($dir)
->exclude('temp', '.git')
Filtragem
O Finder oferece vários métodos para filtrar os resultados (ou seja, reduzi-los). Você pode combiná-los e chamá-los repetidamente.
Use size()
para filtrar por tamanho de arquivo. Desta forma, encontramos arquivos com tamanhos entre 100 e
200 bytes:
Finder::findFiles('*.php')
->size('>=', 100)
->size('<=', 200);
O método date()
filtra até a data em que o arquivo foi modificado pela última vez. Os valores podem ser
absolutos ou relativos à data e hora atual, por exemplo, é assim que se encontram os arquivos modificados nas duas últimas
semanas:
Finder::findFiles('*.php')
->date('>', '-2 weeks')
->from($dir)
Ambas as funções entendem os operadores >
, >=
, <
, <=
,
=
, !=
, <>
.
O Finder também permite filtrar resultados usando funções personalizadas. A função recebe um objeto
Nette\Utils\FileInfo
como parâmetro e deve retornar true
para incluir o arquivo nos resultados.
Exemplo: procure por arquivos PHP que contenham a seqüência Nette
(não sensível a maiúsculas e
minúsculas):
Finder::findFiles('*.php')
->filter(fn($file) => strcasecmp($file->read(), 'Nette') === 0);
Filtragem de profundidade
Ao realizar buscas recorrentes, você pode definir a profundidade máxima de rastejamento usando o método
limitDepth()
. Se você definir limitDepth(1)
, somente os primeiros subdiretórios são rastreados,
limitDepth(0)
desativa a profundidade de rastreamento, e um valor de –1 cancela o limite.
O Finder permite que você use suas próprias funções para decidir qual diretório entrar quando estiver navegando.
A função recebe um objeto Nette\Utils\FileInfo
como parâmetro e deve retornar true
para entrar no
diretório:
Finder::findFiles('*.php')
->descentFilter($file->getBasename() !== 'temp');
Ordenação
O Finder também oferece várias funções para classificar os resultados.
O método sortByName()
ordena os resultados por nome de arquivo. A ordenação é natural, ou seja, trata
corretamente os números nos nomes e retorna, por exemplo, foo1.txt
antes de foo10.txt
.
O Finder também permite que você faça a classificação usando uma função personalizada. Ele toma dois objetos
Nette\Utils\FileInfo
como parâmetros e deve retornar o resultado da comparação com o operador
<=>
ou seja, -1
, 0
nebo 1
. Por exemplo, é assim que classificamos os
arquivos por tamanho:
$finder->sortBy(fn($a, $b) => $a->getSize() <=> $b->getSize());
Múltiplas buscas diferentes
Se você precisar encontrar vários arquivos diferentes em locais diferentes ou que atendam a critérios diferentes, use
o método append()
. Ele retorna um novo objeto Finder
para que você possa fazer chamadas em cadeia
pelo método :
($finder = new Finder) // armazenar o primeiro Finder na variável $finder!
->files('*.php') // procure por arquivos *.php em src/
->from('src')
->append()
->files('*.md') // em docs/ procurar por arquivos *.md
->from('docs')
->append()
->files('*.json'); // na pasta atual procurar por arquivos *.json
Alternativamente, você pode usar o método append()
para adicionar um arquivo específico (ou um conjunto de
arquivos). Em seguida, ele retorna o mesmo objeto Finder
:
$finder = Finder::findFiles('*.txt')
->append(__FILE__);
FileInfo
Nette\Utils\FileInfo é uma classe que representa um arquivo ou diretório nos resultados da busca. É uma extensão da classe SplFileInfo que fornece informações como tamanho do arquivo, data da última modificação, nome, caminho, etc.
Além disso, fornece métodos para retornar caminhos relativos, o que é útil quando se navega em profundidade:
foreach (Finder::findFiles('*.jpg')->from('.') as $file) {
$absoluteFilePath = $file->getRealPath();
$relativeFilePath = $file->getRelativePathname();
}
Você também tem métodos para ler e escrever o conteúdo de um arquivo:
foreach ($finder as $file) {
$contents = $file->read();
// ...
$file->write($contents);
}
Retorno de Resultados como um Array
Como visto nos exemplos, o Finder implementa a interface IteratorAggregate
, assim você pode usar
foreach
para pesquisar os resultados. Ele é programado para que os resultados sejam carregados apenas enquanto você
navega, assim, se você tiver um grande número de arquivos, ele não espera que todos eles sejam lidos.
Você também pode ter os resultados retornados como um conjunto de Nette\Utils\FileInfo
objetos, usando
o método collect()
. A matriz não é associativa, mas numérica.
$array = $finder->findFiles('*.php')->collect();