Directory Tree Script with PHP

The old saying goes “The world is but a file and I, a lonely man, shall list it in the cosmos!” Ok, I made up the new old saying but haven’t you ever wanted to add a basic file directory listing script to use with your own file manager? Maybe something that can simply list the contents of a directory (its subfolders and files) and even make them expandable/collapsible like you see in windows explorer? No? Well read the damn blog anyways! Just kidding…. but we are going to cover creating a basic directory tree listing script in PHP right here on the Programming Underground!

There are a lot of scripts out there that take advantage of listing a directory’s contents. Many windows, and even some Linux, users know all about the famous file tree. You see it in windows explorer where you have several folders that can easily be expanded to reveal sub folders and files. It is a basic task for things like control panels, file managers, or even web applications that need to let people navigate directories and files.

PHP can do a lot of this with functions like readdir, is_dir, and basename. Of course it needs some help with a few arrays and some JavaScript to provide us the animation.

Now this script isn’t super fancy for a reason. I wanted to provide a very basic framework for which programmers, like yourself, can take and add stuff to it, customize its look and even alter its mechanics if you desired. Consider this a skeleton which you can then add things like icons or make the files links themselves which will launch into your favorite “What you see is what you get” (WYSIWYG) editor.

You may have seen this type of tool as part of file managers that Internet Service Providers (ISPs) use when providing you a website control panel. Typically you click an icon, go to a page which lists your files, it offers you some place to upload files and lists the current directory as well… maybe letting you edit or delete files as well. So if you have ever wanted to do such a tool, this can certainly be at the heart of it.

Before we get to the code, I wanted to also say that at a few points in the code I make comments as to where you might find useful places to customize the script. Where you could add your icons or your fancy wav files for sounding the creaking of a wooden door as you open a folder. Ok, don’t do that one. That would just be annoying!

<html>
<head>
<title>Current Directory Listing</title>
<script type="text/javascript">
	// Find the appropriate folder id and determine its state.
	// If it is not showing, show it and if it is showing hide it.
	function showSubs(topicid) {
		var subs = document.getElementById("folder" + topicid);
		
		// In the if statement below you can also add statements to change icons for each element
		// Just remember to give the icon a unique id that matches the folder's id.
		if (subs.style.display == "none") {
			subs.style.display = "block";
		}
		else {
			subs.style.display = "none";
		}
	}

</script>
</head>

<body>

<?php

// Counter assigns a unique sequential number to each folder it encounters
$listDirCount = 0;

// This function will take the path to build a list of folders and files in that folder
// Then display those folders and files according to the way the operating system shows them. 

function listDir($path = ".") {
	global $listDirCount;
	
	$folders = array();
	$files = array();
	
	// Open the given path
	if ($handle = opendir($path)) {
		// Loop through its contents adding folder paths or files to separate arrays
		// Make sure not to include "." or ".." in the listing.
		
		while (false !== ($file = readdir($handle))) {
			if ($file != "." && $file != "..") {
				if (is_dir($path . "/" . $file)) {	
					$folders[] = $path . "/" . $file;
				}
				else { $files[] = $file; }
			}
		}
		
		// Once we build the folder array, get a new number, create a clickable link for the folder, 
		// and then construct a div tag which will contain the next list of folders/files.
		// The link will trigger our javascript above to toggle the div's display on and off.
		
		for ($i = 0; $i < count($folders); $i++) {
			$listDirCount++;
			
			// Here is the folder name, so you can add icons and such to this line
			echo "<a href=\"java script:void(0)\" onclick=\"showSubs($listDirCount)\">" . basename($folders[$i]) . "</a><br/>\n";
			
			echo '<div id="folder' . $listDirCount . '" style="margin-left: 15px; margin-right: 10px; display: none;">';
			listDir($folders[$i]);
			echo '</div>';
		}
		
		// Here we just loop and print the file names. Add icons here for files if you like.
		for ($i = 0; $i < count($files); $i++) {
			echo "{$files[$i]}<br/>\n";
		}
		
		// Finally close the directory.
		closedir($handle);
	}
}

// Kick off the listing with default parameters
// If you want specific folder that is under the folder that this dir is in, use paths like "./nameofdir" or simply "nameofdir"
listDir();
?>

</body>
</html>

You can copy this php example above into your favorite editor, save it as a php file and place it in a given directory on your web host. It will then give you all the directories and files from that point downwards. It will simply list the directories at the top and then followed by the files. You will notice that the directory names are also links. By clicking each one you are calling the JavaScript function showSubs() which will toggle the display of hidden <div> tags created by the script. What this effect should do is then provide you with the ability to expand and collapse directories.

We created this tree structure using a single function called listDir which takes the directory to list as a parameter. Now I made this a function because I wanted to setup a recursive structure in which when I run into a sub directory I then can call listDir again and pass it this sub directory to then list. This will allow me to go into sub directories of sub directories of sub directories. As deep as it goes. Each time indenting it a little further.

The process first opens the directory using readdir. It then loops through all sub items of that directory. For each item it runs into (we determine this using is_dir which returns true or false) we either add it to an array called “$folders” or an array called “$files”. Since this function is recursive, these arrays are unique to each call of the function. So they only will contain the sub directory names and file names of a single directory.

We then get a unique number identifier for each directory, list the directory name as a link to our JavaScript, and create a <div> tag underneath it to list the sub contents. This <div> is going to start out as hidden. However, when we click its link it will call the JavaScript and pass it the number we assigned to the <div> tag (like folder1 or folder12). The JavaScript will get that <div> tag and either toggle its display property to make it visible or toggle it to make it hidden. Lastly we loop through the files of the directory.

Now you may be asking yourself why didn’t we just list the directory or file in our initial while loop? Why not just do it there and do away with these for loops? The reason is pretty simple. If we had done it in the while loop we would have had all folders and files mixed together in the list. By creating these arrays and then controlling which prints first, we can float the directories to the top and then list the files. Just like you see in good old windows explorer.

The last thing I wanted to point out is that $listDirCount is defined outside the function and then referenced in the function using the keyword “global”. The reason this is setup this way is that we need the value assigned to each directory to be the next logical number from the last call to listDir(). If we had made it part of the function we would have had a fresh instance of $listDirCount each time the function was called. It would never have known about previous calls to listDir() made earlier. By making it global here we are essentially making it static like you may have seen in other languages. PHP 5 does have static variables that can be tied to classes so if you wanted to make this a class instead of a function it is up to you. I wanted to keep it short and sweet.

Read through the in code comments and follow along. Don’t let the recursion trip you up. Just remember that each time it is called it is being passed a new directory to explore. Hope this helps you knock out a file manager for that upcoming CMS you all have been building. Right? Good luck guys. 🙂

About The Author

Martyr2 is the founder of the Coders Lexicon and author of the new ebooks "The Programmers Idea Book" and "Diagnosing the Problem" . He has been a programmer for over 25 years. He works for a hot application development company in Vancouver Canada which service some of the biggest tech companies in the world. He has won numerous awards for his mentoring in software development and contributes regularly to several communities around the web. He is an expert in numerous languages including .NET, PHP, C/C++, Java and more.