A friend just asked me to write a
PHP function to list all the contents of a directory and its sub-directories.
Nothing special here... just a simple example piece of code and boredom...
function ListContents($DirName)
{
print '<ul>';
$dir=opendir($DirName);
while($file=readdir($dir))
if($file!='.' && $file!='..')
{
$FilePath="$DirName/$file";
$IsDir=is_dir($FilePath);
print "<li>$file [".($IsDir ? 'D' : number_format(filesize($FilePath), 0, '.', ',')).']';
if($IsDir)
ListContents($FilePath);
print '</li>';
}
closedir($dir);
print '</ul>';
}
It wouldn’t be a bad idea to turn off
PHP’s “output buffering” and on “implicit flush” when running something like this for larger directories.
Example output for “ListContents('c:\\temp');”:
- A.BMP [230]
- Dir1 [D]
- codeblocks-1.0rc2_mingw.exe [13,597,181]
- Dir1a [D]
- Dir2 [D]
- Dir3 [D]
- HW.C [12,009]
- INIFILE.C [9,436]
- NTDETECT.COM [47,564]
I decided to make it a little nicer afterwards by
bolding the directories, adding their total size, and changing sizes to a human readable format. This function is a lot more memory intensive because it holds data in strings instead of immediately outputting.
function HumanReadableSize($Size)
{
$MetricSizes=Array('Bytes', 'KB', 'MB', 'GB', 'TB');
for($SizeOn=0;$Size>=1024 && $SizeOn<count($MetricSizes)-1;$SizeOn++) //Loops until Size is < a binary thousand (1,024) or we have run out of listed Metric Sizes
$Size/=1024;
return preg_replace('/\\.?0+$/', '', number_format($Size, 2, '.', ',')).' '.$MetricSizes[$SizeOn]; //Forces to a maximum of 2 decimal places, adds comma at thousands place, appends metric size
}
function ListContents2($DirName, &$RetSize)
{
$Output='<ul>';
$dir=opendir($DirName);
$TotalSize=0;
while($file=readdir($dir))
if($file!='.' && $file!='..')
{
$FilePath="$DirName/$file";
if(is_dir($FilePath)) //Is directory
{
$DirContents=ListContents2($FilePath, $DirSize);
$Output.="<li><b>$file</b> [".HumanReadableSize($DirSize)."]$DirContents</li>";
$TotalSize+=$DirSize;
}
else //Is file
{
$FileSize=filesize($FilePath);
$Output.="<li>$file [".HumanReadableSize($FileSize).']</li>';
$TotalSize+=$FileSize;
}
}
closedir($dir);
$RetSize=$TotalSize;
$Output.='</ul>';
return $Output;
}
Example output for “print ListContents2('c:\\temp', $Dummy);”:
- A.BMP [230 Bytes]
- Dir1 [12.99 MB]
- codeblocks-1.0rc2_mingw.exe [12.97 MB]
- Dir1a [24.95 KB]
- Dir2 [0 Bytes]
- Dir3 [20.94 KB]
- HW.C [11.73 KB]
- INIFILE.C [9.21 KB]
- NTDETECT.COM [46.45 KB]
The memory problem can be rectified through a little extra IO by calculating the size of a directory before its contents is listed, thereby not needing to keep everything in a string.
function CalcDirSize($DirName)
{
$dir=opendir($DirName);
$TotalSize=0;
while($file=readdir($dir))
if($file!='.' && $file!='..')
$TotalSize+=(is_dir($FilePath="$DirName/$file") ? CalcDirSize($FilePath) : filesize($FilePath));
closedir($dir);
return $TotalSize;
}
function ListContents3($DirName)
{
print '<ul>';
$dir=opendir($DirName);
$TotalSize=0;
while($file=readdir($dir))
if($file!='.' && $file!='..')
{
$FilePath="$DirName/$file";
$IsDir=is_dir($FilePath);
$FileSize=($IsDir ? CalcDirSize($FilePath) : filesize($FilePath));
$TotalSize+=$FileSize;
print '<li>'.($IsDir ? '<b>' : '').$file.($IsDir ? '</b>' : '').' ['.HumanReadableSize($FileSize).']';
if($IsDir) //Is directory
$TotalSize+=ListContents3($FilePath);
print '</li>';
}
closedir($dir);
print '</ul>';
}
Example output: for “ListContents3('c:\\temp');”:
- A.BMP [230 Bytes]
- Dir1 [12.99 MB]
- codeblocks-1.0rc2_mingw.exe [12.97 MB]
- Dir1a [24.95 KB]
- Dir2 [0 Bytes]
- Dir3 [20.94 KB]
- HW.C [11.73 KB]
- INIFILE.C [9.21 KB]
- NTDETECT.COM [46.45 KB]
Of course, after all this, my friend took the original advice I gave him before writing any of this code, which was that using
bash commands might get him to his original goal much easier.