Skip to content

Instantly share code, notes, and snippets.

@jonataswalker
Last active April 2, 2024 18:40
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save jonataswalker/3c0c6b26eabb2e36bc90 to your computer and use it in GitHub Desktop.
Save jonataswalker/3c0c6b26eabb2e36bc90 to your computer and use it in GitHub Desktop.
Build a tree (array or json) from any directory.
<?php
**
* Public Domain.
* @author Jonatas Walker
* Based in: http://kvz.io/blog/2007/10/03/convert-anything-to-tree-structures-in-php/
* Usage:
* $root = '/home/www/';
* $allow_ext = array('jpg','gif');
* $exclude_dir = array('tilemill');
* $prefix = 'small';
*
* $tree = new buildTreeFromDirectory($root);
* $tree->setFilter($allow_ext, $prefix);
* $tree->saveJSON('/path/to/save', 'json-tree.json');
*
*/
class buildTreeFromDirectory {
public $root_dir,
$file_prefix,
$allowed_extensions,
$ignore_hidden,
$exclude_dir;
private $filter;
public function __construct($root_dir){
$root_dir = realpath($root_dir);
if(!$root_dir)
die("This directory doesn't exist!");
$this->root_dir = $root_dir;
$this->setFilter();
date_default_timezone_set(@date_default_timezone_get());
}
public function setFilter(
$allowed_extensions = array('*'),
$file_prefix = '',
$exclude_dir = array(),
$ignore_hidden = true
){
$this->allowed_extensions = $allowed_extensions;
$this->file_prefix = $file_prefix;
$this->ignore_hidden = $ignore_hidden;
$this->exclude_dir = $exclude_dir;
}
public function buildTree(){
$dir = new RecursiveDirectoryIterator(
$this->root_dir, FilesystemIterator::SKIP_DOTS);
$this->filter($dir);
$it = new RecursiveIteratorIterator(
$this->filter,
RecursiveIteratorIterator::SELF_FIRST,
RecursiveIteratorIterator::CATCH_GET_CHILD
);
$tree = array();
foreach($it as $fileinfo) {
$name = $fileinfo->getFilename();
$sub_path_name = $it->getSubPathName();
$parts = explode(DIRECTORY_SEPARATOR, $sub_path_name);
array_pop($parts);
$parentArr = &$tree;
//go deep in the file|dir path
foreach ($parts as $part) {
$parentArr = &$parentArr['dirs'][$part];
}
if ($fileinfo->isDir()) {
// Add the final part to the structure
$parentArr['dirs'][$name] = array('folder' => $name);
} else {
// Add some file info to the structure
if($fileinfo->isLink()){
$realpath = $fileinfo->getRealPath();
$filesize = filesize($realpath);
$filemtime = filemtime($realpath);
} else {
$filesize = $fileinfo->getSize();
$filemtime = $fileinfo->getMTime();
}
$parentArr['files'][] = array(
'filename' => $name,
'filesize' => $this->fileSizeConvert($filesize),
'date' => date("d-m-Y H:i", $filemtime),
'relative_path' => $it->getSubPath()
);
}
}
unset($parentArr);
$this->sortArray($tree);
return $tree;
}
private function sortArray(&$tree){
foreach ($tree as &$value) {
if (is_array($value))
$this->sortArray($value);
}
return ksort($tree);
}
public function saveJSON($path, $filename){
$tree = $this->buildTree();
$path = realpath($path);
if($path && is_writable($path)){
$full = $path .DIRECTORY_SEPARATOR. $filename;
$write = file_put_contents($full, json_encode($tree));
if($write)
echo 'Saved: ' . $full . "\n";
else
echo 'Error trying to save: ' . $full . "\n";
}
}
public function getJSON(){
header('Content-Type: application/json');
echo json_encode(array(
'tree' => $this->buildTree()
));
}
private function filter($dir){
$this->filter = new RecursiveCallbackFilterIterator($dir,
function($current, $key, $iterator){
$filename = $current->getFilename();
//ignore all hidden files/directories
if($this->ignore_hidden){
if(substr($filename, 0, 1) == '.')
return false;
}
// Allow recursion
if($iterator->hasChildren() &&
!in_array($filename, $this->exclude_dir)) {
return true;
}
if($current->isReadable() === false)
return false;
//filter by file extension
$path = $current->getPathname();
$file_ext = pathinfo($path, PATHINFO_EXTENSION);
if($this->allowed_extensions[0] == '*'){ //no extension filter
$ext_allowed = true;
} else {
$ext_allowed = in_array($file_ext, $this->allowed_extensions);
}
if(!empty($this->file_prefix)){
//filter by prefix and extension
if(strpos($filename, $this->file_prefix) === 0 && $ext_allowed)
return true;
else
return false;
}
//filter by extension
return $ext_allowed;
}
);
}
public function fileSizeConvert($bytes){
$label = array('B', 'KB', 'MB', 'GB', 'TB', 'PB');
for($i = 0; $bytes >= 1024 && $i < (count($label) -1); $bytes /= 1024, $i++);
return round($bytes, 2) . " " . $label[$i];
}
}
//Usage:
$root = '/home/www/tests';
$allow_ext = array('jpg','gif');
$exclude_dir = array('tilemill');
$prefix = 'small';
$tree = new buildTreeFromDirectory($root);
$tree->setFilter($allow_ext, $prefix, $exclude_dir);
//$tree->getJSON(); //for ajax purpose
$tree_class->saveJSON('/path/to/save', 'json-tree.json');
/** This is an example of the JSON structure
* you can see a beautified tree of this at http://codebeautify.org/jsonviewer/067c13
{"tree":{"dirs":{"dir1":{"dirs":{"subdir1":{"dirs":{"sub-subdir1":{"folder":"sub-subdir1"}},"files":[{"date":"31-07-2015 12:53","filename":"small55ba1674dc59b.jpg","filesize":"15.25 KB","relative_path":"dir1/subdir1"}],"folder":"subdir1"}},"files":[{"date":"31-07-2015 12:46","filename":"small55ba1674dc59b.jpg","filesize":"15.25 KB","relative_path":"dir1"}],"folder":"dir1"},"dir2":{"dirs":{"subdir2.1":{"dirs":{"sub-subdir2.1":{"folder":"sub-subdir2.1"}},"folder":"subdir2.1"},"subdir2.2":{"dirs":{"sub-subdir2.2":{"folder":"sub-subdir2.2"}},"folder":"subdir2.2"},"subdir2.3":{"dirs":{"sub-subdir2.3":{"folder":"sub-subdir2.3"}},"folder":"subdir2.3"}},"files":[{"date":"31-07-2015 12:46","filename":"small55ba1674dc59b.jpg","filesize":"15.25 KB","relative_path":"dir2"}],"folder":"dir2"},"dir3":{"folder":"dir3"},"dir4":{"folder":"dir4"}},"files":[{"date":"30-07-2015 09:19","filename":"small55ba166fb2fe2.jpg","filesize":"36.91 KB","relative_path":""},{"date":"30-07-2015 09:20","filename":"small55ba1673846e8.jpg","filesize":"28.25 KB","relative_path":""},{"date":"30-07-2015 09:20","filename":"small55ba1674dc59b.jpg","filesize":"15.25 KB","relative_path":""},{"date":"30-07-2015 09:20","filename":"small55ba1671bd37b.jpg","filesize":"21.38 KB","relative_path":""},{"date":"30-07-2015 09:19","filename":"small55ba166e8b119.jpg","filesize":"31.82 KB","relative_path":""}]}}
*/
/**And this is the Array structure - basically two entries for each directory:
* one for sub folders and other for files
Array
(
[dirs] => Array
(
[dir1] => Array
(
[dirs] => Array
(
[subdir1] => Array
(
[dirs] => Array
(
[sub-subdir1] => Array
(
[folder] => sub-subdir1
)
)
[files] => Array
(
[0] => Array
(
[date] => 31-07-2015 12:53
[filename] => small55ba1674dc59b.jpg
[filesize] => 15.25 KB
[relative_path] => dir1/subdir1
)
)
[folder] => subdir1
)
)
[files] => Array
(
[0] => Array
(
[date] => 31-07-2015 12:46
[filename] => small55ba1674dc59b.jpg
[filesize] => 15.25 KB
[relative_path] => dir1
)
)
[folder] => dir1
)
[dir2] => Array
(
[dirs] => Array
(
[subdir2.1] => Array
(
[dirs] => Array
(
[sub-subdir2.1] => Array
(
[folder] => sub-subdir2.1
)
)
[folder] => subdir2.1
)
)
[files] => Array
(
[0] => Array
(
[date] => 31-07-2015 12:46
[filename] => small55ba1674dc59b.jpg
[filesize] => 15.25 KB
[relative_path] => dir2
)
)
[folder] => dir2
)
)
[files] => Array
(
[0] => Array
(
[date] => 30-07-2015 09:19
[filename] => small55ba166fb2fe2.jpg
[filesize] => 36.91 KB
[relative_path] =>
)
)
)
*/
?>
@KalenAnson
Copy link

FYI: you must supply an absolute path in the buildTreeFromDirectory($root) function as the $root variable. If you pass a relative path the function will not build an accurate directory structure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment