[2010–09–15] Třída Nette\Finder pro procházení adresářovou strukturou

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
David Grudl
Nette Core | 8107
+
0
-

Snad dva roky jsem měl u metody Nette\Tools::glob() sloužící k rekurzivnímu procházení adresářů poznámku TODO: předělat na RecursiveDirectoryIterator. Problémem byl v návrhu API. Existuje totiž spousta variant toho, co a jak hledat, které soubory vracet, které adresáře vracet a které procházet rekurzivně. Nebo naopak kterým se vyhýbat. Dále jsou tu specifické situace, jako například RobotLoader, který při procházení adresářovou strukturou načítá soubory netterobots.txt a teprve za chodu zjišťuje dodatečná pravidla procházení, atd. Otázkou bylo, jak to navrhnout univerzálně, srozumitelně a bez WTF faktorů.

Výsledkem snažení je třída Nette\Finder, jejíž API není dokonalé, ale je zatím asi to nejpoužitelnější, k jakému jsem se dopracoval. Jde o DSL, takže tu neplatí standardní konvence pro pojmenování metod.

// nerekurzivní hledání souborů txt
foreach (Finder::findFiles('*.txt')->in($dir) as $file) {
}

// rekurzivní hledání souborů txt
foreach (Finder::findFiles('*.txt')->from($dir) as $file) {
}

// rekurzivní hledání souborů *.txt kromě těch, co obsahují v názvu X
foreach (Finder::findFiles('*.txt')->exclude('*X*')
	->from($dir) as $file) {
}

// rekurzivní hledání souborů *.txt co obsahují v názvu číslo
foreach (Finder::findFiles('*[0-9]*.txt')
	->from($dir) as $file) {
}

// rekurzivní hledání souborů *.txt umístěných v adresáři
// začínajícím na "c" ale nikoliv "cc"
foreach (Finder::findFiles('c*/*.txt')->exclude('cc*/*')
	->from($dir) as $file) {
}

Omezit hloubku procházení lze metodou limitDepth().

Kromě souborů lze hledat i adresáře Finder::findDirectories('subdir*') nebo obojí Finder::find('file.txt'). V tomto případě se maska vztahuje na soubory, nikoliv adresáře.

Adresáře, kterým se chceme zcela vyhnout, uvedeme za klauzulí „from“:

foreach (Finder::findFiles('*.php')->from($dir)->exclude('temp', '.git') as $file) {
}

Zajímavou možností je hledat více masek nebo dokonce procházet více adresářů v rámci jedné iterace:

foreach (Finder::findFiles('*.txt', '*.php')->in($dir1, $dir2) as $file) {
}

přičemž parametry lze zadat i ve formě polí:

foreach (Finder::findFiles($masks)->in($dirs) as $file) {
}

Dále je možné výsledky filtrovat. Například podle velikosti:

// vrátí soubory v rozmezí 100B až 200B
foreach (Finder::findFiles('*.php')->size('>=', 100)->size('<=', 200) as $file) {
}

nebo data změny

// vratí soubory změněné v posledních dvou týdnech
foreach (Finder::findFiles('*.php')->date('>', new DateTime('- 2 weeks')) as $file) {
}

// nebo také
foreach (Finder::findFiles('*.php')->date('>', '- 2 weeks') as $file) {
}

nebo použít vlastní callback:

// soubory PHP s počtem řádku větším než 1000
$finder = Finder::findFiles('*.php')->filter(function($file) {
	return count(file($file->getPathname())) > 1000;
});

Využít lze i extension method:

Finder::extensionMethod('dimensions', function($finder, $width, $height){
	if (!preg_match('#^(\D+)(\d+)(px)?$#i', $width, $mW) || !preg_match('#^(\D+)?(\d+)(px)?$#i', $height, $mH)) {
		throw new \InvalidArgumentException('Dimensions predicate format invalid.');
	}
	return $finder->filter(function($file) use ($mW, $mH) {
		return $file->getSize() >= 12 && ($size = getimagesize($file->getPathname()))
			&& (!$mW || Tools::compare($size[0], $mW[1], $mW[2]))
			&& (!$mH || Tools::compare($size[1], $mH[1], $mH[2]));
	});
});


// najdi obrázky s rozměry většími než 50px x 50px
foreach (Finder::findFiles('*')->dimensions('>50', '>50')->from($dir) as $file) {
}

Předpokládám, že API Finderu ještě dozná změn, takže čekám na vaše podněty.


Mimochodem, Finder je první třída v Nette plně využívající closures. Nicméně udělat jsem převodník closure pro PHP 5.2, takže funguje ve všech verzích.

Honza Marek
Člen | 1664
+
0
-

Pěkná hračka.

Patrik Votoček
Člen | 2221
+
0
-

Jestli to takhle půjde se všema novinkama v 1.0. Tak letos dooprvady ruším vánoce.

Ale bych jenom nechválil. Poslední dobou celkem hodně pracuju s obrázky. Jde něják specifikovat filter který by mě vrátil obrázek za takovýchto podmínek:

$basePath = DATA_DIR . "/images/foo";
$path = $basePath . ".gif";
if (!file_exists($path)) {
	$path = $basePath . ".png";
	if (!file_exists($path)) {
		$path = $basePath . ".jpg";
		if (!file_exists($path)) {
			$path = DATA_DIR . "/images/blank.png"; // 404 image
		}
	}
}
$image = Nette\Image::fromFile($path);

Editoval vrtak-cz (15. 9. 2010 15:29)

westrem
Člen | 398
+
0
-

vrtak-cz napsal

Tak neviem ci spravne chapem, ale nie je to co ty potrebujes a pytas sa ci bude finder vediet trochu mimo jeho kompetenciu/povodny zamer? Tebe ide primarne o zistenie, aka verzia obrazka existuje a ak ziadna neexistuje vratit blank.

Finder je podla mna urceny na zistovanie obsahu adresara, ktory splnuje nejake validacne podmienky.

Avsak aby som netaral len do vetra.

  1. David pise, ze je mozne pouzit vlastny filter (vid priklady)
  2. Na tomto riadku ma David TODO s regexpami takze ja osobne (ak teda spravne chapem tvoj poziadavok) si viem predstavit ako to napisat ako regexp.

David Grudl napsal

Uzasna vec! Sam som o nieco podobne uz raz pokusal ale padlo to na tom ako predavat rozne filtracne podmienky. Prvotny nastrel API je podla mna hodne dobry, chcelo by to mozno ale aj funkcie na zistovanie poctu najdenych suborov. Tiez mi trochu nie je jasne nasledne rekurzivne iterovanie alebo iterovanie ked pouzijem viac $dirs.

Dalsia featuresa by mohla byt moznost pridania radenia vystupu (podla datumu vytvorenia, zmeny, velkosti, abecedne) – mmtalne je ak som spravne pozeral zdrojak dostupne len CHILD_FIRST.

Trosku WTF faktor mi pride excludes. Excluduje sa na kazdej urovni alebo len na prvej? Viem si predstavit vyuzitie obojeho.

westrem
Člen | 398
+
0
-

vrtak-cz napsal(a):

Jestli to takhle půjde se všema novinkama v 1.0. Tak letos dooprvady ruším vánoce.

Ale bych jenom nechválil. Poslední dobou celkem hodně pracuju s obrázky. Jde něják specifikovat filter který by mě vrátil obrázek za takovýchto podmínek:

$basePath = DATA_DIR . "/images/foo";
$path = $basePath . ".gif";
if (!file_exists($path)) {
	$path = $basePath . ".png";
	if (!file_exists($path)) {
		$path = $basePath . ".jpg";
		if (!file_exists($path)) {
			$path = DATA_DIR . "/images/blank.png"; // 404 image
		}
	}
}
$image = Nette\Image::fromFile($path);

A nechcem vrtat ale toto ide podla mna napisat aj krajsie a prehladnejsie:

$basePath = DATA_DIR . "/images/foo";
switch (TRUE) {
	case file_exists($path = $basePath . '.gif'):
		break;
	case file_exists($path = $basePath . '.png'):
		break;
	case file_exists($path = $basePath . '.jpg'):
		break;
	default:
		$path = DATA_DIR . "/images/blank.png";
		break;
}
$image = Nette\Image::fromFile($path);

Ako v podstate je to jedno, len tolko vnorenych if-ov mi pride hodne neprehladnych.

David Grudl
Nette Core | 8107
+
0
-

Tohle skutečně s nástrojem pro iterování nad adresářovou strukturou nesouvisí…

Patrik Votoček
Člen | 2221
+
0
-

Já se ptal z důvodu jestli to umožňuje i takovéto „pokročilejší funkce“.

David Grudl
Nette Core | 8107
+
0
-

Tak samozřejmě můžeš udělat nějaké foreach(Finder::findFiles('foo.gif', 'foo.png', 'foo.jpg')->in(DATA_DIR . "/images"), ale je to kanón na vrabce.

hason
Člen | 23
+
0
-

Projekty jako Doctrine2 a Symfony2 navzájem využívají své kódy a nevymýšlejí je znovu. Škoda, že se tento trend ještě nedostal k Nette – http://fabien.potencier.org/…d-your-files

Editoval hason (16. 9. 2010 23:23)

Blizzy
Člen | 149
+
0
-

Projekty jako Dibi nebo Texy také využívají část Nette kódu.

Jenomže Symfony 2 je konkurence, takže od něj Nette kopírovat nemůže. Případný kód např. ze Symfony 2 si programátor musí dokopírovat sám, do distribuce se asi nikdy nedostane.

iguana007
Člen | 970
+
0
-

Trošku jsem si s Finderem pohrál a nejsem si jist, zda-li něco dělám špatně nebo je to bug, ale pořád mi vrací to co nechci. Jde mi o to, aby mi Finder vrátil všechny soubory dané složky, které jsou starší 20ti minut a začínají názvem „cropped_“.

Tady je můj testovací kód:

Debug::barDump(new DateTime('- 20 minutes'));
foreach (Finder::findFiles('cropped_*')->in(WWW_DIR.'/download/')->date('<', new DateTime('- 20 minutes')) as $file) {
	Debug::barDump($file->getFilename().' - '.date('Y-m-d H:i:s',$file->getMTime()));
}

Který mi vrací toto:
http://ukaz.at/vn
tj. všechny soubory, sice správně filtrované podle názvu, ale ne podle data.
Když změním < na > v metodě date tak dostavám totožný dump.

David Grudl
Nette Core | 8107
+
0
-

hason napsal(a):

Projekty jako Doctrine2 a Symfony2 navzájem využívají své kódy a nevymýšlejí je znovu.

Jinými slovy proč programovat Nette, když je tu Symfony, že? Vůbec nerozumím, co ti vadí. Díval jsem se na ten kód v Symfony, musí být alespoň 10× pomalejší než Finder v Nette, omezení hloubky procházení funguje tak, že projde kompletní strukturu a vše, co je pod limitem, jen zahazuje. Víc to studovat nechci, ale nevyhovuje to standardu kvality Nette. Takže mám přebírat nekvalitní kód?

David Grudl
Nette Core | 8107
+
0
-

iguana007 napsal(a):

Trošku jsem si s Finderem pohrál a nejsem si jist, zda-li něco dělám špatně nebo je to bug

Ono je to takto:

Finder::findFiles('cropped_*') // nasleduji podminky pro soubory
	->in(WWW_DIR.'/download/') // nasleduji podminky pro prochazene adresare

takže tu podmínku „date“ uveď hned za findFiles.

iguana007
Člen | 970
+
0
-

Jo toto zabralo, díky! :)

Patrik Votoček
Člen | 2221
+
0
-

hason napsal(a):

Projekty jako Doctrine2 a Symfony2 navzájem využívají své kódy a nevymýšlejí je znovu.

Já jsem si třeba tu část Doctrine2 která využívá Symfony2 přepsal tak aby využívala Nette. Takže Symfony2 v Nette projektu vůbec nemám (i přes to že mám Doctrine 2)… :-)

Editoval vrtak-cz (17. 9. 2010 9:19)

hason
Člen | 23
+
0
-

David Grudl napsal:
Jinými slovy proč programovat Nette, když je tu Symfony, že? Vůbec nerozumím, co ti vadí.

Nic takového jsem neřekl. Já jsem jen poukázal na to, že už existuje stejná knihovna pro procházení adresářové struktury šířená pod licencí MIT.

Díval jsem se na ten kód v Symfony, musí být alespoň 10× pomalejší než Finder v Nette

Nemohu soudit. Nikde jsem neviděl srovnání. Prozatím to je jen tvrzení o univerzální Nette konstantě ;)

omezení hloubky procházení funguje tak, že projde kompletní strukturu a vše, co je pod limitem, jen zahazuje.

Nějak nemohu najít rozdíl mezi implementací v nette a Symfony2:

Nette: $iterator->setMaxDepth($this->maxDepth);

Symfony2: $iterator->setMaxDepth(INF === $maxDepth ? -1 : $maxDepth);

Víc to studovat nechci, ale nevyhovuje to standardu kvality Nette. Takže mám přebírat nekvalitní kód?

Tvrdit na základě minimálního studia kódu, že je nekvalitní je odvážné tvrzení. O přebírání kódu jsem také nic nepsal. Napsal jsem, že Doctrine2 a Symfony2 využívají navzájem své kódy, ale nepřebírají (chápu jako zkopírování kódu). Proč by se to také dělalo? Vždyť tyto knihovny mohou být nainstalovány přes PEAR a vesele využívány. Stejně jako PHPUnit apod.

No a na závěr ještě ukázka, jaké výhody přináší využívání již existující kódu (Symfony2 Finder a Zend Framework Service):

use Symfony\Component\Finder\Finder;

$s3 = new \Zend_Service_Amazon_S3($key, $secret);
$s3->registerStreamWrapper("s3");

$finder = new Finder();
$finder->name('photos*')->size('< 100K')->date('since 1 hour ago');
foreach ($finder->in('s3://bucket-name') as $file) {
    // do something

    print $file->getFilename()."\n";
}

Editoval hason (17. 9. 2010 9:18)

Šaman
Člen | 2634
+
0
-

hason napsal(a):

Nechci dělat Davidovi advokáta (zvlášť když ho nepotřebuje), ale

  1. srovnáváš syntax a nevidíš rozdíl v implementaci? Samozřejmě, rozdíl v implementaci je na úrovni zdrojových kódů. A jestli po letmém seznámení se se zdrojáky David zjistil, že Symfony vyhledává neefektivně, tak mu můžeš buď věřit, nebo to nastudovat pořádně a pak polemizovat.
  2. jestliže existují volně použitelné knihovny na stejné činnosti které podporuje Nette, tak super, můžeš je vesele používat. Ale vůbec nevidím důvod kritizovat, že se v Nette vynalézá kolo – ono každé to kolo je trošku jiné a to Netťácké by mělo do zbytku frameworku pasovat jak prdýlka na nočník. A jedna z výhod Nette je, že všechny kódy vychází z jedné hlavy. (Např. na Zendu se mi nelíbilo, že to byl takový slepenec všech všemožných knihoven z různých zdrojů. Chyběla tam jednotná štábní kultura už na úrovni syntaxe.)
David Grudl
Nette Core | 8107
+
0
-

S tou hloubkou máte pravdu, omlouvám se (platí to ale o ignorování adresářů, což mimochodem ani nefunguje na Windows). Rychlost jsem neměřil, jde o to, že, naskládání mnoha iterátorů na sebe zpomaluje. A to propojení s S3 předpokládám bude na Nette fungovat úplně stejně, nebo je důvod, proč by nefungovalo? Ale o hodnocení kódu nejde, že, nechám toho.

Trend využívání existujícího kódu do Nette dorazil, ať už v plánu připojit knihovnu Doctrine 2, použití knihovny pod MIT, nebo sdílení kódu s dibi. Pojmu „využívat ale nepřebírat“ nerozumím.

pesu
Člen | 7
+
0
-

Zdravím, měl bych podnět … callback modifikátory. Příklad: najdu všechny .jpg obrázky v určitém adresáři a chci je všechny zobrazit na webu. Jenže cesta na disku <> url, takže by se mi hodilo předat Finderu modifikátor, který by to pěkně upravil a Finder by vracel hotové url obrázků ;)

hrach
Člen | 1834
+
0
-

Feature request – doprogramovat filtrovani pomoci regexpu.

Honza Marek
Člen | 1664
+
0
-

Ono by možná stačilo přidat funkci toArray(), aby se na to daly použít array_map a array_filter funkce.

Yrwein
Člen | 45
+
0
-

Honza Marek: PHP alternativa k metodě: iterator_to_array

Editoval Yrwein (8. 11. 2010 23:28)

hrach
Člen | 1834
+
0
-

Honza Marek napsal(a):

Ono by možná stačilo přidat funkci toArray(), aby se na to daly použít array_map a array_filter funkce.

jestli to byla reakce na mě – tak samozřejmě to by šlo, nicméně, to už není o tom, jak to udělat, ale jak pěkně to udělat, a pěkné to není

<?php
$files = Finder::findFiles('*')->in(..)->toArray();
$files = array_filter($files, function($val) {
	return preg_match('#^\d{4}_\d{2}_\d{2}.log$#', $val->getFilename());
});

# je trosku prasarna oproti

$files = Finder::findFiles('#^\d{4}_\d{2}_\d{2}.log$#')->in(..);
?>
Filip Procházka
Moderator | 4668
+
0
-

nevidím problém…

$finder = Finder::findFiles('*.log')->filter(function($file) {
        return String::match($file->getFilename(), '#^\d{4}_\d{2}_\d{2}.log$#');
});
pesu
Člen | 7
+
0
-

HosipLan napsal(a):

nevidím problém…

$finder = Finder::findFiles('*.log')->filter(function($file) {
        return String::match($file->getFilename(), '#^\d{4}_\d{2}_\d{2}.log$#');
});

No tohle je easy, ale já bych potřeboval např. něco takového:

$finder = Finder::findFiles('*.jpg', '*.gif', '*.png')->modifier(function($file) {
        return preg_replace("#\.(jpg|gif|png)#", "_small.\\1", $file->getFilename());
});
hrach
Člen | 1834
+
0
-

HosipLan: ja problem vidim, pokud uz se jednou sestavuje filtrovaci rexexp (mrkni do zdrojaku), tak nechapu, proc bych mel pak delat filtrovani jeste jednou. David to tam ma taky v TODO, ja sem svym postem spis chtel podporit udelani teto feature.

Filip Procházka
Moderator | 4668
+
0
-

pesu: co takhle?

$maleobrazky = array_map(function($file) {
        return String::replace($file->getFilename(), "#\.(jpg|gif|png)#", "_small.\\1");
}, iterator_to_array(Finder::findFiles('*.jpg', '*.gif', '*.png')));

dump($maleobrazky);

hrach: tvoje řešení je samozřejmě hezčí, to je bez debaty :)

Editoval HosipLan (9. 11. 2010 11:21)

srigi
Nette Blogger | 558
+
0
-

hrachove riesenie sa mi paci najviac.

Editoval srigi (9. 11. 2010 11:16)

David Grudl
Nette Core | 8107
+
0
-

hrach napsal(a):

Feature request – doprogramovat filtrovani pomoci regexpu.

Maličkost, jen vymysli syntax, rozlišení skutečných regulárů od ořezaných. (v nich lze #^\d{4}_\d{2}_\d{2}.log$#' také zadat, sice ukecaněji [0-9][0-9][0-9][0-9]_[0-9][0-9]_[0-9][0-9].log, ale pro mnohé uživatele srozumitelněji)

pesu
Člen | 7
+
0
-

HosipLan napsal(a):

pesu: co takhle?

$maleobrazky = array_map(function($file) {
        return String::replace($file->getFilename(), "#\.(jpg|gif|png)#", "_small.\\1");
}, iterator_to_array(Finder::findFiles('*.jpg', '*.gif', '*.png')));

dump($maleobrazky);

hrach: tvoje řešení je samozřejmě hezčí, to je bez debaty :)

HosipLan: Ano, takhle se to jistě řešit dá, ale nelíbí se mi to. Drbat se pravou rukou za levým uchem je sice umění, ale rozhodně to není efektivní a už vůbec to není nic, co bych chtěl dělat každý den. Když můžu Finderu předat filtr, proč bych mu nemohl předat i modifier, v principu je to to samé, nebo se mýlím? Co takhle pro Finder definovat interface IFinderFilter a IFinderModifier? Vůbec bychom pak nebyli závislí na tom, co Finder umí nebo neumí, prostě by dostal instance filtr/modifier třídy a ty by provedly svoji práci.

hrach
Člen | 1834
+
0
-

David Grudl napsal(a):
Maličkost, jen vymysli syntax, rozlišení skutečných regulárů od ořezaných

Já vím, že to je právě ten zádrhel, možná by šlo:
Finder::findFilesRegexp(..)

David Grudl
Nette Core | 8107
+
0
-

hrach napsal(a):

Víš, v čem je zádrhel, ale stejně sem napíšeš: „Feature request – doprogramovat filtrovani pomoci regexpu.“ Vnímáš to taky jako buzeraci?

pesu napsal(a):

nepotřebuješ modifikátor, stačí

foreach (Finder.... as $file) {
	$url = String::replace($file->getFilename(), "#\.(jpg|gif|png)#", "_small.\\1");
}

Nemá smysl API Finderu zaplácávat něčím, co má triviální řešení.

pesu
Člen | 7
+
0
-

David Grudl napsal(a):

hrach napsal(a):

Víš, v čem je zádrhel, ale stejně sem napíšeš: „Feature request – doprogramovat filtrovani pomoci regexpu.“ Vnímáš to taky jako buzeraci?

pesu napsal(a):

nepotřebuješ modifikátor, stačí

foreach (Finder.... as $file) {
	$url = String::replace($file->getFilename(), "#\.(jpg|gif|png)#", "_small.\\1");
}

Nemá smysl API Finderu zaplácávat něčím, co má triviální řešení.

No to ano, to jistě triviální řešení má. Akorát je tam o jednu iteraci navíc a pak když to chci dostat do šablony, tak to musím někam ukládat, předávat atd. Asi nějak takto:

foreach (Finder.... as $file) {
	$urls[] = String::replace($file->getFilename(), "#\.(jpg|gif|png)#", "_small.\\1");
}
$this->template->urls = $urls;

nebo

$m = new FinderModifier;
$m->setRewrite(....); //momentální nápad

$this->template->urls = Finder::find("*.jpg")->in($path)->modifier($m);

Modifier pak můžu mít v celém projektu jeden a používat ho můžu vícekrát, může dokonce mít na starosti víc věcí. Jinak mi není moc jasné, proč když jsou ve Finderu filtry by tam nemohly být modifiery … filtrování v extra iteraci je přece úplně stejně triviální a přesto jim to výsostné právo býti ve Finderu upřeno není.

paranoiq
Člen | 392
+
0
-
$this->template->urls = new ModifierIterator(Finder::find(...), .../* rewrite options */);

a takové řešení lze navíc použít na cokoliv. nejen na Finder

hrach
Člen | 1834
+
0
-

David Grudl napsal(a):
Víš, v čem je zádrhel, ale stejně sem napíšeš: „Feature request – doprogramovat filtrovani pomoci regexpu.“ Vnímáš to taky jako buzeraci?

lol :) přemýšlím, kdo se blbě vyspal. nene, přečetl jsem si ve zdrojáku tvé todo, a toto mělo být takové to „+1“, jako že by se to hodilo. nic víc, nic míň. nevím co je v té větě buzeračního.

hrach
Člen | 1834
+
0
-

apropo, vzbudil můj příspěvek aspoň diskuzi nad hledáním správného řešení. Pokud se shodneme, nemám problém jej naprogramovat, ale vzhledem k tomu, že spásnou ideu nemám, bylo by zbytečné teď něco implemenovat, skončí to jako aktuální 4 pull requesty – sic dobré, ale né přímo třeba zapadající do koncepce.

jaký je názor na ten findFilesRegexp()?

David Grudl
Nette Core | 8107
+
0
-

pesu napsal(a):

No to ano, to jistě triviální řešení má. Akorát je tam o jednu iteraci navíc a pak když to chci dostat do šablony, tak to musím někam ukládat, předávat atd. Asi nějak takto:

Modifikuj přímo v šabloně {$file|image}

hrach napsal(a):

jaký je názor na ten findFilesRegexp()?

Tři nové funkce findRegExp, findFilesRegexp a findDirectoriesRegExp by asi fakt skončily jako nepoužitý pull request.

Milo
Nette Core | 1283
+
0
-

David Grudl napsal(a):

hrach napsal(a):

Feature request – doprogramovat filtrovani pomoci regexpu.

Maličkost, jen vymysli syntax, rozlišení skutečných regulárů od ořezaných. (v nich lze #^\d{4}_\d{2}_\d{2}.log$#' také zadat, sice ukecaněji [0-9][0-9][0-9][0-9]_[0-9][0-9]_[0-9][0-9].log, ale pro mnohé uživatele srozumitelněji)

A co třeba takto?

Finder::find( '#regexp#', Finder::REGEXP )->...
Finder::find( Finder::regexp( '#regexp#' ) )->...
Finder::find( '\x00#regexp#' [, Finder::AUTO | Finder::REGEXP | Finder::WILDCHARS ] )->...
Filip Procházka
Moderator | 4668
+
0
-

to vypadá hrozně :)

Co by možná nebylo od věci tak udělat třídu RegExp jako je String, php obdobu k javascriptové verzi.

$files = Finder::find(new RegExp('~^\d{4}_\d{2}_\d{2}.log$~i'))->in(...);
  • dalo by se to rozlišit globálně ve FW
  • vyčlenily by se metody ze String
  • hezčí api
  • možnost udělat zkratku jako mají callbacky
$regexp = new RegExp('~(a+b)~i');
$result = $regexp->matchAll('baaabaaabaaa');

$results = regexp('~(a+b)~i')->matchAll('baaabaaabaaa');

Když teď nad tím tak přemýšlím,
nechce se mi mazat co už jsem napsal, ale uvědomil jsem si jak hrozně to vypadá (především ta zkratka)


Takže pokud něco takového tak spíš asi jenom takový jednoduchý kontejner, prostě typ regulární výraz pomocí třídy

namespace Nette;

use Nette;

class RegExp extends Object
{
	private $regexp;


	public function __construct($regexp)
	{
		// if not string throw new InvalidArgumentException
		// + možnost automatického doplnění delimiterů, ikdyž to je asi moc magické...

		$this->regexp = $regexp;
	}


	public function __toString()
	{
		return $this->regexp;
	}
}
redhead
Člen | 1313
+
0
-

Jsem pro 1. HosipLanovo řešení, velice se mi to líbí! I když trochu ukecané. Ale v zásadě jsem byl vždy pro mít regexpy ve vlastní třídě než ve String. Má to tak i Java a dle mého oprávněně.

Milo
Nette Core | 1283
+
0
-

Tak HosipLanovo řešení je elegantnější :) Také by se mohl vytvořit Interface:

interface IStringFinder
{
    public function __construct( $pattern );
    public function match( $text );
    public function matchAll( $text );
    ...
}

class RegExp    implements IStringFinder...
class WildChars implements IStringFinder...
David Grudl
Nette Core | 8107
+
0
-

paranoiq napsal(a):

$this->template->urls = new ModifierIterator(Finder::find(...), .../* rewrite options */);

Přidal jsem Nette\MapIterator, který tohle dělá.

foreach (new MapIterator(Finder::findFiles('*.jpg', '*.gif', '*.png'), function($file) {
	return String::replace($file->getFilename(), "#\.(jpg|gif|png)#", "_small.\\1");
}) as $url) {
	dump($url);
}
pracj3am
Člen | 14
+
0
-

Potřeboval jsem obohatit API o třídění. Takto jsem se s tím vypořádal:

<?php
class Finder extends \Nette\Finder
{
    private $order;

    /**
     * Sets the order comparison function
     * @param callback $order
     * @return Finder
     */
    public function order($order)
    {
        $this->order = $order;
        return $this;
    }


    public function orderByName()
    {
        $this->order = function($f1, $f2) {
            return \strcasecmp($f1->getFilename(), $f2->getFilename());
        };
        return $this;
    }


    public function orderByMTime()
    {
        $this->order = function($f1, $f2) {
            return $f2->getMTime() - $f2->getMTime();
        };
        return $this;
    }


    /**
     * Returns iterator.
     * @return \Iterator
     */
    public function getIterator()
    {
        $iterator = parent::getIterator();
        if ($this->order === NULL) {
            return $iterator;
        }

        $iterator = new \ArrayIterator(\iterator_to_array($iterator));
        $iterator->uasort($this->order);

        return $iterator;
    }

}
?>
Cifro
Člen | 245
+
0
-

Ako pomocou Findera vyberiem napr. 5 náhodných súborov z adresára? Teda ak sa to vôbec dá…

Editoval Cifro (15. 3. 2011 21:43)

Ondřej Mirtes
Člen | 1536
+
0
-

Asi bych si je náhodně vyfiltroval a pak vzal prvních pět prvků výsledku.

Cifro
Člen | 245
+
0
-

To ma tiež napadlo, len neviem ako realizovať ten filter ->filter(function($file){....}), lebo do neho ide ako parameter iba jeden súbor…

Zatiaľ to mam takto :(

	$images = iterator_to_array(Finder::findFiles("*.jpg")->in($this->imagesFolder)->getIterator());

	shuffle($images);
	$c = count($images);
	$this->images = array_slice($images, 0, $c >= 5 ? 5 : $c);

Editoval Cifro (16. 3. 2011 1:46)

paranoiq
Člen | 392
+
0
-

vybrat všechny a pak použít array_rand()

Cifro
Člen | 245
+
0
-

To nejde, lebo to je pole SplFileInfo objektov a keď použijem array_rand() tak to vracia len kľúče, čiže vrati len pathname bez objektov…

paranoiq
Člen | 392
+
0
-

tak ty klíče použij. od toho jsou

Cifro
Člen | 245
+
0
-

Njn… chcel som byť pohodlný, že či to s Finderom nejak nepojde.