Plány na nový File Finder

David Grudl
Nette Core | 8099
+
+14
-

Jedna z knihoven, které už moře let plánuju předělat, je Finder. Narazil jsem totiž časem na různé situace, kdy mi nevyhovoval.

V současnosti funguje takto:

Finder::findFiles('*.php', '*.inc')->in('/foo');   // hledá /foo/*.php a /foo/*.inc
Finder::findFiles('*.php', '*.inc')->from('/foo'); // hledá navíc v podadresářích
// nebo Finder::find() a Finder::findDirectories()

plus vyloučení:

Finder::findFiles('*.php', '*.inc')
	->exclude('temp.php') // vyloučení názvu souboru
	->from('/foo')
	->exclude('vendor') // vyloučení cesty, nebude prohledávat např. v /foo/sub/vendor

V metodě in() a from() musí být platný adresář, ve find*() a exclude() mohou být souborové/adresářové masky.

Masky a cesty všude

Líbilo by se mi, kdyby šel konfigurovat podobně jako gitignore. Včetně podpory pro wildcard **, který představuje libovolně mnoho adresářů. Pokud zapíšu třeba foo/**/file.php, znamená to jak foo/sub/file.php, tak i foo/file.php nebo foo/sub1/sub2/file.php.

Plán je, že by ve všech zmíněných metodách mohla být maska i adresář. Třeba ve findFiles():

Finder::findFiles('tests/*.inc')->from('/foo');    // *.inc kdekoliv ve /foo/**/tests
Finder::findFiles('**/tests/*.inc')->from('/foo'); // ekvivalent předchozího
Finder::findFiles('./tests/*.inc')->from('/foo');  // *.inc jen ve /foo/tests

Maska v in() a from():

Finder::findFiles('*.php')->in('/foo/*/vendor');
Finder::findFiles('*.php')->in('/foo/*/vendor/**'); // totéž jako from('/foo/*/vendor')
Finder::findFiles('*.php')->from('/foo/**/vendor'); // najde i /foo/sub1/sub2/vendor/sub3/file.php

Vlastně by klidně mohlo jít zapsat i stručné:

Finder::findFiles('/foo/**/*.php');

A nakonec masky a adresáře ve vyloučení:

Finder::findFiles('*.php')->from('/foo')->exclude('tests/*.inc');    // vynechá *.inc v tests/
Finder::findFiles('*.php')->from('/foo')->exclude('**/tests/*.inc'); // ekvivalent předchozího
Finder::findFiles('*.php')->from('/foo')->exclude('./tests/*.inc');  // vynechá *.inc jen ve /foo/tests/
Finder::findFiles('*.php')->from('/foo')->exclude('bar/');  // vynechá jen adresáře bar/ při procházení

FileInfo

Vrácené soubory budou objekty Nette\Utils\FileInfo, což je potomek původního SplFileInfo, ale přidává důležité metody getRelativePath() a getRelativePathname(), které mi vždycky chyběly. Přidám asi i read a write pro přečtení/zápis obsahu souboru.

Další věci

Metody pro přidání cest a masek půjde volat vícekrát. Protože nejde volat vícekrát statickou metodu jako např. findFiles(), přidám metody files() a directories():

(new Finder)->files('*.php')->files('*.inc')->in('/foo')->from('/bar');

Také by bylo možné říct, že chci hledat soubory dle určité masky v jednom adresáři a dle jiné masky v jiném adresáři. Mohlo by to vypadat nějak takto:

($finder = new Finder)
	->files('*.php')->in('/foo')
	->append()
	->files('*.inc')->from('/bar');

Do výsledku je tak možné přidat třeba jeden soubor navíc:

$finder = Finder::findFiles('*.php')->from('/foo')
	->append(__FILE__);

Dále lze přeskakovat adresáře, které nelze číst pomocí $finder->ignoreUnreadableDirs() (uvažuju, že by to mohlo být default) a třídit adresáře a v nich soubory pomocí $finder->sortByName().

Máte k tomu nějaké nápady nebo něco dalšího vám u Finderu chybí?

Polki
Člen | 553
+
0
-

Chtěl jsem jej použít jen jednou.
Potřeboval jsem řadit soubory.
To neuměl, tak jsem ho nepoužil.

Za mě top.

Editoval Polki (4. 1. 2022 23:16)

Milo
Nette Core | 1283
+
+6
-

Jednoznačně 👍

Wildcard ** bych využil už mockrát. Stejně tak možnost násobného volání files(). Masky ve from() a in() mi přijdou magické, ale use case se asi najde.

paranoiq
Člen | 392
+
+1
-

ahoj.
na co jsem narazil já – když jsem nedávno psal jeden nástroj a potřeboval použít nějaký finder, sáhl jsem napřed po Nette Finderu a pak po Symfomy Finderu. ani jeden neměl všechny fičůry, které jsem potřeboval, takže jsem nakonec napsal obálku nad SF (https://github.com/…leFinder.php)

za mě nejdůležitější fičůra by byla přidat samostatně konfigurovatelný baseDir, abych mohl adresáře i soubory zadávat relativně k němu

pak prohledávání adresáře bez rekurze (což možná řeší ty hvězdičky?)

pak filtrování podle hashbangu nebo mime-magic u souborů bez přípon

Felix
Nette Core | 1185
+
+4
-

Osobne by se mi hodilo napriklad mazani podle nejakych pravidel. Nebo obecne volani nejakeho callbacku.

Finder::findFiles('*.log')
  ->from('/srv/logs')
  ->exclude('.gitignore')
  ->delete()

Finder::findFiles('*.log')
  ->from('/srv/logs')
  ->exclude('.gitignore')
  ->callback(Finder::delete)
David Grudl
Nette Core | 8099
+
0
-

za mě nejdůležitější fičůra by byla přidat samostatně konfigurovatelný baseDir, abych mohl adresáře i soubory zadávat relativně k němu

Tohle mělo fungovat už dříve, tj Finder::findFiles('relative/path/*.php')->in($baseDir). Nebo jsi to myslel jinak?

pak prohledávání adresáře bez rekurze (což možná řeší ty hvězdičky?)

Tohle taky mělo fungovat už dříve, tj Finder::findFiles('*.php')->in($dir). Rekurze je from($dir)

pak filtrování podle hashbangu nebo mime-magic u souborů bez přípon

Tohle taky mělo fungovat už dříve :-), tj Finder::findFiles('*.php')->filter($mimeFilter)->in($dir). Teda s tím, že ten filter si napíšeš.

David Grudl
Nette Core | 8099
+
0
-

Felix napsal(a):

Osobne by se mi hodilo napriklad mazani podle nejakych pravidel. Nebo obecne volani nejakeho callbacku.

V podstatě na to lze taky použít filter(), asi něco jako filter('unlink').

U filtrů mi vadí, že se chová jinak za findFiles() a jinak za in()/from(), což je záměr, ale ne úplně šťastnej.

paranoiq
Člen | 392
+
+1
-

ad „Tohle mělo fungovat už dříve“ – když o tom tak přemýšlím znovu a lépe, asi byl problém spíš v tom, že jsem potřeboval prohledávat několik adresářů najednou (což teď řešíš :) krát mnoho přípon, takže tak jak to píšeš v té ukázce jsem to stejně bez nějaké obálky použít nemohl a bylo jednodušší to postavit nad SF finderem, než dělat několik dotazů Nette finderem a slučovat výsledky

ad „Tohle taky“ – asi platí to co výše. spíš jsem už zapomněl, že to jde : ]

ad „Tohle taky 2“ – jj. o obecném filtru vím

Tomáš Jacík
Člen | 146
+
0
-

Za mě super nápad, jen aby to bylo kompatibilní třeba s tím .gitignore, ať pak člověk netápe, proč mu něco nefunguje :)

David Grudl
Nette Core | 8099
+
+1
-

Nový Finder už můžete vyzkoušet v masteru!

David Grudl
Nette Core | 8099
+
+3
-

Prosím o otestování nového Finderu (jak na to).

Migrační příručka

V předchozí verzi platilo, že metody exclude a filter() fungovaly jinak, když byly zavolány před from() resp. in() a po ní. Tohle už neplatí, exclude() a filter() fungují vždy stejně. Dřívější filter() volaný až po nahradila nová metoda descentFilter().

Finder již neimplementuje rozhraní Countable.

Na Linuxu se nově chová v režimu case-sensitive.

Finder::findFiles() (nebo find() a findDirectories()) volaný bez argumentů dříve vrátil všechny soubory, teď vyhodí chybu Too few arguments.

Řetězec začínající lomítkem ve Finder::findFiles('/f*') se nově považuje za absolutní cestu, je potřeba ho nahradit např. za Finder::findFiles('./f*').