Finder: Dosya Arama

Belirli bir maskeyle eşleşen dosyaları bulmanız mı gerekiyor? Finder size bu konuda yardımcı olacaktır. Dizin yapısında gezinmek için çok yönlü ve hızlı bir araçtır.

Kurulum:

composer require nette/utils

Örnekler, oluşturulmuş bir takma ad varsayar:

use Nette\Utils\Finder;

Kullanım

Öncelikle, Nette\Utils\Finder kullanarak geçerli dizindeki .txt ve .md uzantılı dosya adlarını nasıl listeleyebileceğinizi gösterelim:

foreach (Finder::findFiles(['*.txt', '*.md']) as $name => $file) {
	echo $file;
}

Arama için varsayılan dizin geçerli dizindir, ancak bunu in() veya from() metotlarıyla değiştirebilirsiniz. $file değişkeni, birçok yararlı metoda sahip FileInfo sınıfının bir örneğidir. $name anahtarında, dosyanın yolu bir karakter dizisi olarak bulunur.

Ne Aranacak?

findFiles() metodunun yanı sıra, yalnızca dizinleri arayan findDirectories() ve her ikisini de arayan find() metotları da vardır. Bu metotlar statiktir, bu nedenle bir örnek oluşturmadan çağrılabilirler. Maske parametresi isteğe bağlıdır, belirtmezseniz her şey aranır.

foreach (Finder::find() as $file) {
	echo $file; // şimdi tüm dosyalar ve dizinler yazdırılacak
}

files() ve directories() metotlarını kullanarak neyin aranacağını daha fazla belirleyebilirsiniz. Metotlar tekrar tekrar çağrılabilir ve parametre olarak bir maske dizisi de verilebilir:

Finder::findDirectories('vendor') // tüm dizinler
	->files(['*.php', '*.phpt']); // artı tüm PHP dosyaları

Statik metotlara alternatif olarak, new Finder kullanarak bir örnek oluşturmak (bu şekilde oluşturulan yeni nesne hiçbir şey aramaz) ve files() ve directories() kullanarak neyin aranacağını belirtmektir:

(new Finder)
	->directories()      // tüm dizinler
	->files('*.php');    // artı tüm PHP dosyaları

Maskede joker karakterler *, **, ? ve [...] kullanabilirsiniz. Hatta dizinleri de belirtebilirsiniz, örneğin src/*.php, src dizinindeki tüm PHP dosyalarını arar.

Sembolik bağlantılar da dizin veya dosya olarak kabul edilir.

Nerede Aranacak?

Arama için varsayılan dizin geçerli dizindir. Bunu in() ve from() metotlarıyla değiştirirsiniz. Metot adlarından da anlaşılacağı gibi, in() yalnızca belirtilen dizinde arama yapar, from() ise alt dizinlerinde de (özyinelemeli olarak) arama yapar. Geçerli dizinde özyinelemeli olarak arama yapmak istiyorsanız, from('.') kullanabilirsiniz.

Bu metotlar birden çok kez çağrılabilir veya bir dizi olarak birden çok yol iletilebilir, dosyalar daha sonra tüm dizinlerde aranır. Dizinlerden biri mevcut değilse, Nette\UnexpectedValueException istisnası fırlatılır.

Finder::findFiles('*.php')
	->in(['src', 'tests']) // doğrudan src/ ve tests/ içinde arar
	->from('vendor');      // vendor/ alt dizinlerinde de arar

Göreli yollar geçerli dizine göredir. Elbette mutlak yollar da belirtilebilir:

Finder::findFiles('*.php')
	->in('/var/www/html');

Yolda joker karakterler *, **, ? kullanmak mümkündür. Örneğin, src/*/*.php yoluyla src dizinindeki ikinci seviye dizinlerdeki tüm PHP dosyalarını arayabilirsiniz. Globstar olarak adlandırılan ** karakteri güçlü bir kozdur, çünkü alt dizinlerde de arama yapmanızı sağlar: src/**/tests/*.php ile src veya herhangi bir alt dizininde bulunan tests dizinindeki tüm PHP dosyalarını ararsınız.

Tersine, yoldaki [...] joker karakterleri desteklenmez, yani özel bir anlamı yoktur, böylece örneğin in(__DIR__) aradığınızda ve yolda tesadüfen [] karakterleri bulunursa istenmeyen davranışlar oluşmaz.

Dosyaları ve dizinleri derinlemesine ararken, önce üst dizin döndürülür ve ancak daha sonra içindeki dosyalar döndürülür, bu childFirst() ile tersine çevrilebilir.

Joker Karakterler

Maskede birkaç özel karakter kullanabilirsiniz:

  • * – herhangi bir sayıda herhangi bir karakteri değiştirir ( / hariç)
  • ** – / dahil herhangi bir sayıda herhangi bir karakteri değiştirir (yani çok seviyeli arama yapılabilir)
  • ? – herhangi bir tek karakteri değiştirir ( / hariç)
  • [a-z] – köşeli parantez içindeki karakter listesinden bir karakteri değiştirir
  • [!a-z] – köşeli parantez içindeki karakter listesi dışındaki bir karakteri değiştirir

Kullanım örnekleri:

  • img/?.png – tek harfli ada sahip dosyalar 0.png, 1.png, x.png, vb.
  • logs/[0-9][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9].log – YYYY-AA-GG formatındaki günlükler
  • src/**/tests/* – src/tests, src/foo/tests, src/foo/bar/tests vb. dizinlerdeki dosyalar.
  • docs/**.md – docs dizininin tüm alt dizinlerindeki .md uzantılı tüm dosyalar

Hariç Tutma

exclude() metodunu kullanarak dosyaları ve dizinleri aramadan hariç tutabilirsiniz. Dosyanın uymaması gereken bir maske belirtirsiniz. Örneğin, adında X harfi içerenler hariç *.txt dosyalarını arama:

Finder::findFiles('*.txt')
	->exclude('*X*');

Gezilen alt dizinleri atlamak için exclude() kullanın:

Finder::findFiles('*.php')
	->from($dir)
	->exclude('temp', '.git')

Filtreleme

Finder, sonuçları filtrelemek (yani azaltmak) için birkaç metot sunar. Bunları birleştirebilir ve tekrar tekrar çağırabilirsiniz.

size() ile dosya boyutuna göre filtreleriz. Bu şekilde 100 ila 200 bayt arasında boyuta sahip dosyaları buluruz:

Finder::findFiles('*.php')
	->size('>=', 100)
	->size('<=', 200);

date() metodu, dosyanın son değiştirilme tarihine göre filtreler. Değerler mutlak veya geçerli tarih ve saate göre göreli olabilir, örneğin bu şekilde son iki hafta içinde değiştirilen dosyaları buluruz:

Finder::findFiles('*.php')
	->date('>', '-2 weeks')
	->from($dir)

Her iki fonksiyon da >, >=, <, <=, =, !=, <> operatörlerini anlar.

Finder ayrıca sonuçları özel fonksiyonlar kullanarak filtrelemeye de olanak tanır. Fonksiyon, parametre olarak Nette\Utils\FileInfo nesnesini alır ve dosyanın sonuçlara dahil edilmesi için true döndürmelidir.

Örnek: Nette karakter dizisini içeren (büyük/küçük harf duyarsız) PHP dosyalarını arama:

Finder::findFiles('*.php')
	->filter(fn($file) => strcasecmp($file->read(), 'Nette') === 0);

Derinlemesine Filtreleme

Özyinelemeli arama yaparken, limitDepth() metodunu kullanarak maksimum gezinme derinliğini ayarlayabilirsiniz. limitDepth(1) ayarlarsanız, yalnızca ilk alt dizinler gezilir, limitDepth(0) derinlemesine gezinmeyi kapatır ve –1 değeri sınırı kaldırır.

Finder, özel fonksiyonlar kullanarak gezinme sırasında hangi dizine girileceğine karar vermenizi sağlar. Fonksiyon, parametre olarak Nette\Utils\FileInfo nesnesini alır ve dizine girilmesi için true döndürmelidir:

Finder::findFiles('*.php')
	->descentFilter($file->getBasename() !== 'temp');

Sıralama

Finder ayrıca sonuçları sıralamak için birkaç fonksiyon sunar.

sortByName() metodu, sonuçları dosya adlarına göre sıralar. Sıralama doğaldır, yani adlardaki sayıları doğru bir şekilde işler ve örneğin foo1.txt'yi foo10.txt'den önce döndürür.

Finder ayrıca özel bir fonksiyon kullanarak sıralamaya da olanak tanır. Bu fonksiyon, parametre olarak iki Nette\Utils\FileInfo nesnesi alır ve <=> operatörüyle karşılaştırma sonucunu, yani -1, 0 veya 1 döndürmelidir. Örneğin, bu şekilde dosyaları boyutlarına göre sıralarız:

$finder->sortBy(fn($a, $b) => $a->getSize() <=> $b->getSize());

Birden Fazla Farklı Arama

Farklı konumlarda veya farklı kriterleri karşılayan birden fazla farklı dosya bulmanız gerekiyorsa, append() metodunu kullanın. Yeni bir Finder nesnesi döndürür, bu nedenle metot çağrılarını zincirlemek mümkündür:

($finder = new Finder) // ilk Finder'ı $finder değişkenine kaydediyoruz!
	->files('*.php')   // src/ içinde *.php dosyalarını arıyoruz
	->from('src')
	->append()
	->files('*.md')    // docs/ içinde *.md dosyalarını arıyoruz
	->from('docs')
	->append()
	->files('*.json'); // geçerli klasörde *.json dosyalarını arıyoruz

Alternatif olarak, belirli bir dosyayı (veya bir dosya dizisini) eklemek için append() metodunu kullanabilirsiniz. O zaman aynı Finder nesnesini döndürür:

$finder = Finder::findFiles('*.txt')
	->append(__FILE__);

FileInfo

Nette\Utils\FileInfo, arama sonuçlarındaki bir dosyayı veya dizini temsil eden bir sınıftır. Dosya boyutu, son değiştirilme tarihi, adı, yolu vb. gibi bilgiler sağlayan SplFileInfo sınıfının bir uzantısıdır.

Ayrıca, derinlemesine gezinirken yararlı olan göreli yolu döndürmek için metotlar sağlar:

foreach (Finder::findFiles('*.jpg')->from('.') as $file) {
	$absoluteFilePath = $file->getRealPath();
	$relativeFilePath = $file->getRelativePathname();
}

Ayrıca dosya içeriğini okumak ve yazmak için metotlarınız vardır:

foreach ($finder as $file) {
    $contents = $file->read();
    // ...
    $file->write($contents);
}

Sonuçları Dizi Olarak Döndürme

Örneklerde görüldüğü gibi, Finder IteratorAggregate arayüzünü uygular, bu nedenle sonuçları gezinmek için foreach kullanabilirsiniz. Sonuçların yalnızca gezinme sırasında yükleneceği şekilde programlanmıştır, bu nedenle çok sayıda dosyanız varsa, hepsi okunana kadar beklemezsiniz.

Sonuçları ayrıca Nette\Utils\FileInfo nesneleri dizisi olarak collect() metoduyla döndürebilirsiniz. Dizi ilişkisel değil, sayısaldır.

$array = $finder->findFiles('*.php')->collect();
versiyon: 4.0