Rekurzivní procházení adresářů s filtrem souborů
Ohledně SPL (Standard PHP Library Functions) rozšíření v PHP 5 se nikde moc nepíše. Funkce používám zejména k vypsání struktury adresářů, souborů nebo filtrovanému výpisu kompletního stromu souborů.
Přejdu přímo ke kontrétnímu příkladu. Detailní popis Standard PHP Library Functions můžete nastudovat v manuálu.
Za úkol jsem si stanovil vytvořit třídy, jejichž cílem je procházet soubory a adresáře na základě filtru definovaného regulárním výrazem.
Nejprve začneme představením třídy DirectoryIterator.
- <?
- $it=new DirectoryIterator(dirname(__FILE__));
- foreach ($it as $itFile) {
- if (!$it->isDot()) {
- if (!$it->isDir()) {
- echo $itFile->getFileName()."<br/>";
- }
- }
- }
- ?>
V konstruktoru DirectoryIterator nastavíme objektu cestu k procházenému adresáři, použil jsem dirname(__FILE__), což je aktuální adresář, kde se skript nachází. Pomocí cyklu pak už jen procházíme jednotlivé položky. Prvně testujeme funkcí isDot() zda nejde o položku typu "." nebo ".." (adresářové odkazy na nadřízený prvek), pokud ano, pokračujeme na další položku cyklu. Další testovací podmínkou je isDir(), pokud ano pokračujeme na další položku. Pokud jsme se dostali na soubor, potom volám funkci getFileName(), která vrací název souboru. Kromě funkce getFileName máme možnost použít spousty dalších funkcí, například na zjištění cesty souboru, času vytvoření souboru apod.
Jednotlivé kroky budou stejné jako u ostatních tutoriálů na tomto webu. Prvně stanovením cíle a cílené programátorské techniky.
Seznam dílčích úkolů:
- Vypsat soubory a adresáře vyhovující filtru
- Vypsat jen soubory vyhovující filtru
- Vypsat soubory a adresáře vyhovující filtru včetně všech podadresářů
- Vypsat jen soubory vyhovující filtru včetně souborů ve všech podadresářích
1. Úkol č.1 - Jak vypsat soubory a adresáře vyhovující filtru
Chci vypsat adresáře a soubory, které začínají názvem clanek, čemuž odpovídá regulární výraz "^clanek". Nejdříve si stanovím kód, se kterým chci pracovat. Třídu, bych chtěl používat nějak takto:
- <?
- $it=new DirectoryIteratorFilter(dirname(__FILE__), "^clanek");
- foreach ($it as $itFile) {
- echo $itFile->getPath()." - ".$itFile->getFileName()."<br/>";
- }
- ?>
Ještě před testováním třídy jsem si vytvořil testovací data. Soubor testovaci-data.php vytvoří v adresáři, kde je uložen, 10 různých adresářů, v každém z nich dalších 5 adresářů a v každém adresáři 5 různých souborů. Dokáže docela zaneřádit počítač tak dávejte pozor jak to používáte.
Adresáře a soubory jsou vytvořeny a teď se pustíme do psaní kódu:
- class DirectoryIteratorFilter extends FilterIterator {
- private $_rx;
- public function __construct($path, $regex=null) {
- parent::__construct(new DirectoryIterator($path));
- $this->_rx= $regex;
- }
- public function accept() {
- $inner=$this->getInnerIterator();
- if ($inner->isDot()) return false;
- if ($this->_rx) return ereg($this->_rx, $inner->getFileName());
- return true;
- }
- public function key() {
- return $this->getInnerIterator()->getPathname();
- }
- }
- $it=new DirectoryIteratorFilter(dirname(__FILE__), "^clanek");
- foreach ($it as $itFile) {
- echo $itFile->getPath()." - ".$itFile->getFileName()."<br/>";
- }
K filtrování souborů jsem využil vestavěnou SPL třídu FilterIterator. V konstruktoru parametrem vkládám cestu k procházenému adresáři a volám rodiče tedy FilterIterator s vytvořeným objektem DirectoryIterator. Dále je předáván regulární výraz pro vyhledávání, není povinný.
Funkce accept vrací true pro objekty, které projdou filtrem. Omezení máme dvojí, na prvním řádku se vyřadí objekty typu "." a ".." pomocí porovnání funkce isDot(), a na druhém řádku se používá k porovnání název souboru funkcí ereg s regulárním výrazem. Druhý řádek je testován jen v případě, že byl zadán regulární výraz. Ukázka.
2. Úkol č.2 - Jak vypsat jen soubory vyhovující filtru
Úkol je obdobný původnímu, jen rozšíříme filtr a podmínku, která nesmí nechat projít adresáře. Protože PHP vrací stejné objekty pro adresáře i pro soubory, nevidím důvod proč k stavbě třídy FileIteratorFilter nerozšířit původní třídu DirectoryIteratorFilter.
Tedy hlavní změna bude ve funkci accept, kde doplníme další podmínku. Původní třídu DirectoryIteratorFile jsem rozšířil takto:
- <?
- class DirectoryIteratorFilter extends FilterIterator {
- private $_rx, $_onlyFiles;
- public function __construct($path, $regex=null, $onlyFiles=0) {
- parent::__construct(new DirectoryIterator($path));
- $this->_rx=$regex;
- $this->_onlyFiles=$onlyFiles;
- }
- public function accept() {
- $inner=$this->getInnerIterator();
- if ($inner->isDot()) return false;
- if ($this->_onlyFiles) if ($inner->isDir()) return false;
- if ($this->_rx) return ereg($this->_rx, $inner->getFileName());
- return true;
- }
- public function key() {
- return $this->getInnerIterator()->getPathname();
- }
- }
- ?>
Nyní už třída DirectoryIteratorFilter filtruje i adresáře, pokud nastavíme promennou $onlyFiles v konstruktoru na hodnotu 1.
Použití FileIteratorFilter bude vypadat takto:
- <?
- class FileIteratorFilter extends DirectoryIteratorFilter {
- public function __construct($path, $regex=null) {
- parent::__construct($path, $regex, 1);
- }
- }
- ?>
Její použití bude opět obdobné (ukázka):
- <?
- include("DirectoryIteratorFilter.php");
- include("FileIteratorFilter.php");
- $it=new FileIteratorFilter(dirname(__FILE__), "^clanek");
- foreach ($it as $itFile) {
- echo $itFile->getPath()." - ".$itFile->getFileName()."<br/>";
- }
- ?>
Úkol č.3 - Jak vypsat soubory a adresáře vyhovující filtru včetně všech podadresářů
I v tomto úkolu budu rozšiřovat to co už známe. Budou nutné menší úpravy a budeme používat jiný iterátor, ale změny budou minimální. A protože budu muset předávat jiný iterátor, rozšířil jsem původní třídu DirectoryIteratorFilter o dva řáky v konstruktoru. Jejich úkolem je rozeznat, zda proměnná $path je objekt, v tom případě předpokládáme, že se jedná o iterátor a předáme ho rodiči. Pokud není iterátor vytvoříme ho.
- <?
- class DirectoryIteratorFilter extends FilterIterator {
- private $_rx, $_onlyFiles;
- public function __construct($path, $regex=null, $onlyFiles=0) {
- if (is_object($path)) parent::__construct($path);
else parent::__construct(new DirectoryIterator($path)); - $this->_rx=$regex;
- $this->_onlyFiles=$onlyFiles;
- }
- public function accept() {
- $inner=$this->getInnerIterator();
- if ($inner->isDot()) return false;
- if ($this->_onlyFiles) if ($inner->isDir()) return false;
- if ($this->_rx) return ereg($this->_rx, $inner->getFileName());
- return true;
- }
- public function key() {
- return $this->getInnerIterator()->getPathname();
- }
- }
- ?>
Třídu opět používáme obdobně (ukázka).
- <?
- include("DirectoryIteratorFilter.php");
- include("FileIteratorFilter.php");
- include("RecursiveDirectoryIteratorFilter.php");
- $it=new RecursiveDirectoryIteratorFilter(dirname(__FILE__), "^clanek");
- foreach ($it as $itFile) {
- echo $itFile->getPath()." - ".$itFile->getFileName()."<br/>";
- }
- ?>
Úkol č.4 - Jak vypsat jen soubory vyhovující filtru včetně všech podadresářů
V tomto úkolu už je snad vše jasné, tentokrát budeme rozšiřovat třídu RecursiveDirectoryIteratorFilter.
A to následovně:
- <?
- class RecursiveFileIteratorFilter extends RecursiveDirectoryIteratorFilter {
- public function __construct($path, $regex=null) {
- parent::__construct($path, $regex, 1);
- }
- }
- ?>
Ukázka použití:
- <?
- include("DirectoryIteratorFilter.php");
- include("FileIteratorFilter.php");
- include("RecursiveDirectoryIteratorFilter.php");
- include("RecursiveFileIteratorFilter.php");
- $it=new RecursiveFileIteratorFilter(dirname(__FILE__), "^clanek");
- foreach ($it as $itFile) {
- echo $itFile->getPath()." - ".$itFile->getFileName()."<br/>";
- }
- ?>
Doufám, že to bylo srozumitelné a bude Vám to užitečné, pokud jsem se někde provinil proti principům OOP, kritiku vítám. Zároveň se těším na feedback ohledně používání tohoto kódu, není příliš testován.
podobné články
| 09.03.2008 | Reference a odkazování na objekty v PHP5 | (20%) |
komentáře
RSS Komentáře


