[2010–09–15] Třída Nette\Finder pro procházení adresářovou strukturou
- David Grudl
- Nette Core | 8212
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.
- Patrik Votoček
- Člen | 2221
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
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.
- David pise, ze je mozne pouzit vlastny filter (vid priklady)
- 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
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 | 8212
Tohle skutečně s nástrojem pro iterování nad adresářovou strukturou nesouvisí…
- David Grudl
- Nette Core | 8212
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
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)
- iguana007
- Člen | 970
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 | 8212
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 | 8212
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.
- Patrik Votoček
- Člen | 2221
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
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 | 2658
hason napsal(a):
Nechci dělat Davidovi advokáta (zvlášť když ho nepotřebuje), ale
- 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.
- 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 | 8212
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
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ů ;)
- Honza Marek
- Člen | 1664
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
Honza Marek: PHP alternativa k metodě: iterator_to_array
Editoval Yrwein (8. 11. 2010 23:28)
- hrach
- Člen | 1837
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
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
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());
});
- Filip Procházka
- Moderator | 4668
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)
- David Grudl
- Nette Core | 8212
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
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.
- David Grudl
- Nette Core | 8212
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
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í.
- hrach
- Člen | 1837
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 | 1837
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 | 8212
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
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
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;
}
}
- Milo
- Nette Core | 1283
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 | 8212
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
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
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)