Článek vytištěný ze serveru WebTip

Název článku: PHP v praxi, 19. díl - Výpis všech adresářů a souborů
Datum publikování: 07.02. 2002
URL článku: http://www.webtip.cz/art/wt_tech_php/jave_php_19.html (kliknětě pro návrat)

Všechna práva vyhrazena (c) 2000 Grafika Publishing s.r.o.
Doslovné ani částečně přebírání tohoto materiálu není povoleno bez předchozího písemného svolení vydavatele - společnosti Grafika Publishing s.r.o.


PHP v praxi, 19. díl - Výpis všech adresářů a souborů

V 17. díle jsme si ukázali jednoduché řešení pro výpis jediného adresáře. Jsou ale případy, kdy potřebujeme mít přehled o kompletní adresářové struktuře. Tedy nejen obsah jednoho konkrétního adresáře, ale i všech dalších adresářů v jeho podadresářích až k tomu poslednímu.

Ukážeme si nejdříve cyklus, na kterém skript postavíme.

$i=0;
$adr=opendir($pozice);
while ($file = readdir($adr))
{
if ($file!="."&&$file!="..")
{
$polozka[$i]['name']=$file;
$polozka[$i]['pozi']=$pozice;
$polozka[$i]['size']=filesize($pozice.$file);
$polozka[$i]['time']=date("H:i:s d.m.Y",filemtime($pozice.$file));
if (is_dir($pozice.$file))
{
$polozka[$i]['type']="dir";
$dir2handle[count($dir2handle)]=$pozice.$polozka[$i]['name']."/";
}
else
{
$polozka[$i]['type']="file";
}
$i++;
}
}

Hned na začátku operujeme s pomocnou proměnnou $i - ta se na konci cyklu vždy zvýší o jedna ($i++) a později se stává identifikujícím prvkem pro pole $polozka. Funkcí opendir() otevřeme pro zpracování adresář, k němuž je cesta uložena v proměnné $pozice. Nyní while() při každém cyklu přiřazuje proměnné $file jednu položku z tohoto adresáře. Podmínka if() je jasná - stejně jako při prohlížení jednoho adresáře v 17. díle i zde odstraňujeme adresářové cesty na současný adresář a adresář o jeden výše.

Následuje přiřazování různých vlastností vícerozměrnému poli (multidimensional array) $polozka[][]. S jednoduchým polem jsme se již setkali. Je to vlastně proměnná obsahující více informací najednou a přistupujeme k ní přes identifikátor mezi hranatými závorkami. Vícerozměrná znamená, že se další informace skrývají pod dalšími identifikátory v dalších hranatých závorkách. Pro názornost: pole $polozka[0][name] je jméno souboru, který se načetl jako první (začíname jako obvykle od nuly), $polozka[0][size] je velikost tohoto souboru. $polozka[1][pozi] však už patří k souboru (či adresáři) dalšímu. Tímto způsobem získáme všechna data dobře přístupná v jednom poli, protože $i se zvyšuje vždy po ukončení jednoho celého cyklu (jinými slovy: až se dokončí všechny operace s jedním souborem či adresářem).

Do $polozka[$i]['name'] se ukládá jméno dotyčného souboru (či adresáře), $polozka[$i]['pozi'] obsahuje jméno nadřazeného adresáře (to je důležité pro zjištění struktury a jednotlivých vazeb souborů a adresářů mezi sebou), v $polozka[$i]['size'] se pomocí funkce filesize() zjistí velikost souboru (u adresáře to bude vždy 0, cesta k souboru je udána $pozice.$file, což je skutečná a úplná cesta k dotyčnému souboru). Do $polozka[$i]['time'] se zapíše pomocí již známé funkce date() datum poslední změny souboru (funkce filemtime()). Následuje podmínka, jež má za úkol zjistit, zda-li je aktuálně zpracovávaná položka soubor či adresář. Podle výsledku se poté poli $polozka[$i]['type'] přiřadí buď řetězec "file" nebo "dir". Jestliže se jedná o adresář, uloží se kompletní cesta k němu do pole $dir2handle[]. Jeho identifikátor vytvoříme tak, že sečteme počet jeho identifikátorů (0,1,2,3,4 - počet 5) - pokud neobsahuje nic, začneme číslem 0. Tím velice jednoduše získáme identifikátor vždy o 1 větší než je ten poslední.

Tento cyklus je však funkční pozue pro jeden adresář. Abychom jej mohli použít úplně pro všechny adresáře, musí se stále měnit $pozice a to do té doby, než vyčerpáme všechny adresáře určené ke zpracování. Provedeme tedy následující:

function loop_dir($pozice)
{
GLOBAL $dir2handle, $polozka;

$i=count($polozka);
$adr=opendir($pozice);
while ($file = readdir($adr))
{
if ($file!="."&&$file!="..")
{
$polozka[$i]['name']=$file;
$polozka[$i]['pozi']=$pozice;
$polozka[$i]['size']=filesize($pozice.$file);
$polozka[$i]['time']=date("H:i:s d.m.Y",filemtime($pozice.$file));
if (is_dir($pozice.$file))
{
$polozka[$i]['type']="dir";
$dir2handle[count($dir2handle)]=$pozice.$polozka[$i]['name']."/";
}
else
{
$polozka[$i]['type']="file";
}
$i++;
}
}
}
$dir2handle[0]='./';
for($j=0;$j<(count($dir2handle));$j++)
{
loop_dir($dir2handle[$j);
}

Cyklus jsme včlenili do námi definované funkce loop_dir(). Voláme ji v dalším cyklu, for(). První cyklus probíhá následovně: pole $dir2handle[0] obsahuje './', tedy současný adresář. Cyklus for probíhá od 0 až do čísla menšího než počet položek v poli $dir2handle[]. Tam máme nyní jednu položku, takže cyklus by měl probehnout pouze jednou - pro nulu. Zavolá se funkce loop_dir(), která předá proměnné $pozice obsah pole $dir2handle[0], tedy to './'. Jak už jsme si říkali, z funkcí je omezen výstup proměnných, proto nastavujeme pomocí GLOBAL tzv. globální proměnné, tedy ty, jež budeme moci využívat i mimo naši funkci. Pomocné $i se poprvé skutečně přiřadí 0 (pole $polozka[] je zatím prázdné). Dále se zpracuje současný adresář přesně tak, jak jsme si ukázali výše. Výstupem jsou tedy pole $dir2handle[] a $polozka[][]. $dir2handle[] obsahuje všechny adresáře, jež máme za úkol dále zpracovat a v $polozka[][] jsou úplně všechny dosud získané informace o souborech a adresářích.

Tím jsme ovšem neskončili! Na konci cyklu for() se k pomocné proměnné $j přičetla jednička ($j++). Kdyby se nic nezměnilo, cyklus by se ukončil, jenže pokud námi zvolený adresář obsahoval další adresáře, jsou přeci v poli $dir2handle[], které tedy změnilo počet svých položek a z toho důvodu se o ně navýšilo omezení tohoto cyklu. Takže se funkci loop_dir() předá další adresář ke zpracování a tak to bude pokračovat až do doby, kdy již nebudou žádné další adresáře k dispozici. Vícerozměrné pole $polozka[][] se mezitím zaplní všemi adresáři a soubory, jež těmito cykli prošly.

Půlka celého problému je za námi, nyní přejdeme k řešení problému, jak ta data dostat v nějaké rozumné podobě na obrazovku.

sort ($dir2handle);
for($i=0;$i<(count($dir2handle));$i++)
{
echo '<b><a name="'.$dir2handle[$i].'">';
echo $dir2handle[$i]."</a></b><br><blockquote>";
for($j=0;$j<(count($polozka));$j++)
{
if($polozka[$j]['pozi']==$dir2handle[$i]&&$polozka[$j]['type']=="dir")
{
echo '<a href="#'.$polozka[$j]['pozi'];
echo $polozka[$j]['name'].'/">';
echo $polozka[$j]['name']."</a> (dir)<br>";
}
}
for($j=0;$j<(count($polozka));$j++)
{
if($polozka[$j]['pozi']==$dir2handle[$i]&&$polozka[$j]['type']=="file")
{
echo '<a href="'.$polozka[$j]['pozi'];
echo $polozka[$j]['name'].'">';
echo $polozka[$j]['name']."</a> (";
echo $polozka[$j]['size']." B, ";
echo $polozka[$j]['time'].")<br>";
}
}
echo "</blockquote>";
}

Nejprve se pomocí funkce sort() abecedně seřadí pole $dir2handle. Dále následují dva do sebe vnořené cykly, jež mají za úkol porovnat nadřazené adresáře všech položek s obsahem $dir2handle[] a podle toho je vypsat či nevypsat. Aby se od sebe logicky oddělily adesáře a soubory, jsou v cyklu cykly dva, první pro adresáře, druhý pro soubory. Vypsání jednotlivých části pole $polozka[][] a html tagy pro vytvoření odkazů je už předpokládám jasné. Toto rozhodně není jediné možné řešení problému, jistě vymyslíte lepší, nám ale zatím stačí.

Nyní si ukážeme celý zdrojový kód včetně zadávání cesty přes formulář a kontroly správnosti zadání:

<html>
<body>
<form>
<input type="text" name="cesta" value="<?= $cesta ?>">
<input type="submit">
</form>
<?
if (isset($cesta))
{
if(is_dir($cesta))
{
if(substr($cesta,-1)!="/")
{
$dir2handle[0]=$cesta."/";
}
else
{
$dir2handle[0]=$cesta;
}
}
else
{
echo "Zadaná cesta <b>$cesta</b> není platným adresářem<br>";
$dir2handle[0]='./';
}
}
else
{
$dir2handle[0]='./';
}
function loop_dir($pozice)
{
GLOBAL $dir2handle, $polozka;
$i=count($polozka);
$adr=opendir($pozice);
while ($file = readdir($adr))
{
if ($file!="."&&$file!="..")
{
$polozka[$i]['name']=$file;
$polozka[$i]['pozi']=$pozice;
$polozka[$i]['size']=filesize($pozice.$file);
$polozka[$i]['time']=date("H:i:s d.m.Y",filemtime($pozice.$file));
if (is_dir($pozice.$file))
{
$polozka[$i]['type']="dir";
$dir2handle[count($dir2handle)]=$pozice.$polozka[$i]['name']."/";
}
else
{
$polozka[$i]['type']="file";
}
$i++;
}
}
}
for($i=0;$i<(count($dir2handle));$i++)
{
loop_dir($dir2handle[$i]);
}
sort ($dir2handle);
for($i=0;$i<(count($dir2handle));$i++)
{
echo '<b><a name="'.$dir2handle[$i].'">';
echo $dir2handle[$i]."</a></b><br><blockquote>";
for($j=0;$j<(count($polozka));$j++)
{
if($polozka[$j]['pozi']==$dir2handle[$i]&&$polozka[$j]['type']=="dir")
{
echo '<a href="#'.$polozka[$j]['pozi'];
echo $polozka[$j]['name'].'/">';
echo $polozka[$j]['name']."</a> (dir)<br>";
}
}
for($j=0;$j<(count($polozka));$j++)
{
if($polozka[$j]['pozi']==$dir2handle[$i]&&$polozka[$j]['type']=="file")
{
echo '<a href="'.$polozka[$j]['pozi'];
echo $polozka[$j]['name'].'">';
echo $polozka[$j]['name']."</a> (";
echo $polozka[$j]['size']." B, ";
echo $polozka[$j]['time'].")<br>";
}
}
echo "</blockquote>";
}
?>
</body>
</html>

Do budoucna bych chtěl napojit tento výpis adresářové struktury s nečím podobným SiteMapperu ze 16. dílu. Tím bychom dostali kromě informací o slepých odkazech také zprávu o tom, na které soubory pro změnu žádný odkaz nevede.


Autor: Veselý Jan
E-mail: jave@yo.cz