Търсачка: Търсене на файлове

Трябва да намерите файлове, отговарящи на определена маска? Търсачката може да ви помогне. Това е универсален и бърз инструмент за преглед на структурата на директориите.

Монтаж:

composer require nette/utils

Примерите предполагат, че псевдонимът вече е създаден:

use Nette\Utils\Finder;

Използване на

Първо нека видим как Nette\Utils\Finder може да се използва за изписване на имена на файлове с разширения .txt и .md в текущата директория:

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

По подразбиране за търсене се използва текущата директория, но можете да я промените чрез методите in() или from(). Променливата $file е инстанция на класа FileInfo с много полезни методи. Ключът $name съдържа пътя до файла като низ.

Какво да търсим?

В допълнение към метода findFiles() има и findDirectories(), който претърсва само директории, и find(), който претърсва и двете директории. Тези методи са статични, така че могат да бъдат извикани, без да се създава инстанция. Параметърът маска не е задължителен, ако не го посочите, ще се търси във всички директории.

foreach (Finder::find() as $file) {
	echo $file; // всички файлове и директории вече са изброени
}

Използвайте методите files() и directories(), за да добавите какво още да търсите. Методите могат да се извикват многократно, а като параметър може да се предостави масив от маски:

Finder::findDirectories('vendor') // всички директории
	->files(['*.php', '*.phpt']); // плюс всички PHP файлове

Алтернативата на статичните методи е да създадете инстанция с помощта на new Finder (свеж обект, създаден по този начин, не търси нищо) и да посочите какво да се търси с помощта на files() и directories():

(new Finder)
	->directories()   //всички директории
	->files('*.php'); // плюс всички PHP файлове

Можете да използвате заместващи символи *, **, ? and [...] в маската. Можете дори да посочите директории, например src/*.php ще търси всички PHP файлове в директорията src.

Симлинковете също се считат за директории или файлове.

Директорията за търсене по подразбиране е текущата директория. Можете да промените това чрез методите in() и from(). Както можете да видите от имената на методите, in() търси само в текущата директория, докато from() търси и в нейните поддиректории (рекурсивно). Ако искате да търсите в текущата директория рекурсивно, можете да използвате from('.').

Можете да извикате тези методи многократно или да им предадете няколко пътя като масиви, след което ще бъдат претърсени всички директории. Ако някоя от директориите не съществува, ще се появи грешка Nette\UnexpectedValueException.

Finder::findFiles('*.php')
	->in(['src', 'tests']) // търси директно в src/ и tests/
	->from('vendor'); // търси и в поддиректориите vendor/

Относителните пътища са относителни спрямо текущата директория. Разбира се, могат да се задават и абсолютни пътища:

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

Заместващите символи *, **, ? can be used in the path. For example, you can use the path src/*/*.php за търсене на всички PHP файлове в директориите от второ ниво в директорията src. Заместващият символ **, наречен globstar, е мощен коз, тъй като ви позволява да търсите и в поддиректории: използвайте src/**/tests/*.php, за да търсите всички PHP файлове в директорията tests, намираща се в src или в някоя от нейните поддиректории.

От друга страна, заместващите знаци [...] не се поддържат в пътя, т.е. нямат специално значение, за да се избегне нежелано поведение, в случай че търсите например in(__DIR__) и в пътя се появят символите [].

Дълбоките търсения на файлове и директории първо връщат родителската директория, а след това файловете, които тя съдържа, което може да се обърне с childFirst().

Wildcards

В маската могат да се използват няколко специални символа:

  • * – replaces any number of arbitrary characters (except /)
  • ** – замества произволен брой символи, включително / (т.е. може да се извърши многостепенно търсене)
  • ? – replaces one arbitrary character (except /)
  • [a-z] – замества един символ от списък със символи в квадратни скоби
  • [!a-z] – замества един символ извън списъка със символи в квадратни скоби

Примери за употреба:

  • img/?.png – файлове с еднобуквено име 0.png, 1.png, x.png и т.н.
  • logs/[0-9][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9].log – лог файлове във формат YYYY-MM-DD
  • src/**/tests/* – файлове в директорията src/tests, src/foo/tests, src/foo/bar/tests и т.н.
  • docs/**.md – всички файлове с разширение .md във всички поддиректории на директорията docs

С изключение на

Използвайте метода exclude(), за да изключите файлове и директории от търсенето. Посочвате маска, с която файлът не трябва да съвпада. Пример за търсене на *.txt, с изключение на тези, които съдържат буквата X в името:

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

Използвайте exclude(), за да прескочите преглежданите поддиректории:

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

Филтриране

Търсачката предлага няколко метода за филтриране на резултатите (т.е. за тяхното намаляване). Те могат да се комбинират и да се извикват многократно.

Използвайте size(), за да филтрирате по размер на файла. По този начин намираме файлове с размер между 100 и 200 байта:

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

Методът date() филтрира по датата на последната промяна на файла. Стойностите могат да бъдат абсолютни или относителни спрямо текущата дата и час, например така се намират файлове, променени през последните две седмици:

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

И двете функции разбират операторите >, >=, <, <=, =, !=, <>.

Търсачката ви позволява също така да филтрирате резултатите с помощта на персонализирани функции. Функцията приема Nette\Utils\FileInfo като параметър и трябва да върне true, за да включи файла в резултатите.

Пример: търсене на PHP файлове, съдържащи символа Nette (без значение на големи и малки букви):

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

Филтриране на дълбочината

При рекурсивното търсене можете да зададете максимална дълбочина на обхождане, като използвате метода limitDepth(). Ако зададете limitDepth(1), ще бъдат обхождани само първите поддиректории, limitDepth(0) деактивира филтрирането в дълбочина, а стойността –1 отменя ограничението.

Търсачката ви позволява да използвате собствените си функции, за да решите в коя директория да влезете при сърфиране. Функцията получава обекта Nette\Utils\FileInfo като параметър и трябва да върне true, за да влезе в директорията:

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

Сортиране

Търсачката предлага и няколко функции за сортиране на резултатите.

Методът sortByName() подрежда резултатите по името на файла. Сортирането е естествено, т.е. то обработва правилно числата в имената и връща например foo1.txt преди foo10.txt.

Търсачката позволява и сортиране с потребителска функция. Той приема два обекта Nette\Utils\FileInfo като параметри и трябва да върне резултата от сравнението с оператора <=>т.е. -1, 0 nebo 1. Например, така сортираме файловете по размер:

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

Няколко различни търсения

Ако трябва да намерите няколко различни файла на различни места или отговарящи на различни критерии, използвайте метода append(). Той връща нов обект Finder, така че можете да използвате верига от извиквания на методи:

($finder = new Finder) // съхранявайте първия Finder в променливата $finder!
	->files('*.php') // търсене на *.php файлове в src/
	->from('src')
	->append()
	->files('*.md') // в документите/ търсене на *.md файлове
	->from('docs')
	->append()
	->files('*.json'); // в текущата папка търсете *.json файлове

Можете също така да използвате метода append(), за да добавите конкретен файл (или масив от файлове). След това връща същия обект Finder:

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

FileInfo

Nette\Utils\FileInfo е клас, който представя файл или директория в резултатите от търсенето. Това е разширение на класа SplFileInfo, което предоставя информация като размер на файла, дата на последна промяна, име, път и др.

Той предоставя и методи за връщане на относителни пътища, което е полезно за задълбочени търсения:

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

Имате и методи за четене и записване на съдържанието на файла:

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

Връщане на резултатите като масив

Както можете да видите от примерите, Finder имплементира интерфейса IteratorAggregate, така че можете да използвате foreach, за да преглеждате резултатите. Той е програмиран така, че резултатите се зареждат само докато преглеждате, така че ако имате голям брой файлове, няма да чака да бъдат прочетени всички.

Можете също така да върнете резултатите като масив от обекти Nette\Utils\FileInfo, като използвате метода collect(). Масивът не е асоциативен, а числов.

$array = $finder->findFiles('*.php')->collect();
версия: 4.0