Funkci glob mám v oblibě, ale chybí mi u ní modifikátor pro rekurzivní procházení adresářů. Toho by se nejspíš dalo nějak dosáhnout zkombinováním tříd RecursiveDirectoryIterator, RecursiveIteratorIterator a GlobIterator, ale přišlo mi to tak krkolomné, že jsem si raději napsal funkci použitelnou se stejnou lehkostí jako původní glob:
<?php /** Nalezení souborů vyhovujících dané masce ve všech podadresářích * @param string se stejnými zástupnými znaky jako glob() * @param int stejné jako glob() * @return array nalezené soubory * @copyright Jakub Vrána, https://php.vrana.cz/ */ function rglob($pattern, $flags = 0) { $return = glob($pattern, $flags); $dirs = "*"; $separator = DIRECTORY_SEPARATOR; $files = $pattern; if (preg_match('~(.*)([/\\\\])(.+)~', $pattern, $match)) { $dirs = "$match[1]$match[2]*"; $separator = $match[2]; $files = $match[3]; } foreach (glob($dirs, ($flags & ~GLOB_MARK) | GLOB_ONLYDIR) as $subdir) { $return = array_merge($return, rglob("$subdir$separator$files", $flags)); } return $return; } ?>
Funkce si poradí se všemi obvyklými vstupy:
*.xml
dir/*.xml
dir*/*.xml
Dbá také na to, aby byl výstup konzistentní: pokud jí předáte dir/*.xml
, tak na Windows nevrátí dir/subdir\a.xml
. Stejně tak pro *.xml
nevrátí ./a.xml
. Kvůli tomu je trochu složitější, než by na první pohled mělo být potřeba.
Zásadní nevýhodou této funkce je, že nejprve získá seznam všech souborů, uloží ho do paměti a pak je teprve můžeme procházet. Vzhledem k mému nejobvyklejšímu využití této funkce (jednorázové skripty spouštěné z příkazové řádky) mi to ale nevadí. Vyřešit by se to dalo přepsáním na iterátor. S podporou operátoru yield (v HipHopu pro PHP nebo v PHP 5.5) by to vypadalo skoro stejně:
<?php
/** Nalezení souborů vyhovujících dané masce ve všech podadresářích
* @param string se stejnými zástupnými znaky jako glob()
* @param int stejné jako glob()
* @return array nalezené soubory
* @copyright Jakub Vrána, https://php.vrana.cz/
*/
function rglob_yield($pattern, $flags = 0) {
foreach (glob($pattern, $flags) as $filename) {
yield $filename;
}
$dirs = "*";
$separator = DIRECTORY_SEPARATOR;
$files = $pattern;
if (preg_match('~(.*)([/\\\\])(.+)~', $pattern, $match)) {
$dirs = "$match[1]$match[2]*";
$separator = $match[2];
$files = $match[3];
}
foreach (glob($dirs, ($flags & ~GLOB_MARK) | GLOB_ONLYDIR) as $subdir) {
foreach (rglob_yield("$subdir$separator$files", $flags) as $filename) {
yield $filename;
}
}
}
foreach (rglob_yield('www/api/*.php') as $filename) {
echo "$filename\n";
}
?>
Diskuse je zrušena z důvodu spamu.