PHP Get all subdirectories of a given directory - php

How can I get all sub-directories of a given directory without files, .(current directory) or ..(parent directory)
and then use each directory in a function?

Option 1:
You can use glob() with the GLOB_ONLYDIR option.
Option 2:
Another option is to use array_filter to filter the list of directories. However, note that the code below will skip valid directories with periods in their name like .config.
$dirs = array_filter(glob('*'), 'is_dir');
print_r($dirs);

Here is how you can retrieve only directories with GLOB:
$directories = glob($somePath . '/*' , GLOB_ONLYDIR);

The Spl DirectoryIterator class provides a simple interface for viewing the contents of filesystem directories.
$dir = new DirectoryIterator($path);
foreach ($dir as $fileinfo) {
if ($fileinfo->isDir() && !$fileinfo->isDot()) {
echo $fileinfo->getFilename().'<br>';
}
}

Almost the same as in your previous question:
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($yourStartingPath),
RecursiveIteratorIterator::SELF_FIRST);
foreach($iterator as $file) {
if($file->isDir()) {
echo strtoupper($file->getRealpath()), PHP_EOL;
}
}
Replace strtoupper with your desired function.

Try this code:
<?php
$path = '/var/www/html/project/somefolder';
$dirs = array();
// directory handle
$dir = dir($path);
while (false !== ($entry = $dir->read())) {
if ($entry != '.' && $entry != '..') {
if (is_dir($path . '/' .$entry)) {
$dirs[] = $entry;
}
}
}
echo "<pre>"; print_r($dirs); exit;

In Array:
function expandDirectoriesMatrix($base_dir, $level = 0) {
$directories = array();
foreach(scandir($base_dir) as $file) {
if($file == '.' || $file == '..') continue;
$dir = $base_dir.DIRECTORY_SEPARATOR.$file;
if(is_dir($dir)) {
$directories[]= array(
'level' => $level
'name' => $file,
'path' => $dir,
'children' => expandDirectoriesMatrix($dir, $level +1)
);
}
}
return $directories;
}
//access:
$dir = '/var/www/';
$directories = expandDirectoriesMatrix($dir);
echo $directories[0]['level'] // 0
echo $directories[0]['name'] // pathA
echo $directories[0]['path'] // /var/www/pathA
echo $directories[0]['children'][0]['name'] // subPathA1
echo $directories[0]['children'][0]['level'] // 1
echo $directories[0]['children'][1]['name'] // subPathA2
echo $directories[0]['children'][1]['level'] // 1
Example to show all:
function showDirectories($list, $parent = array())
{
foreach ($list as $directory){
$parent_name = count($parent) ? " parent: ({$parent['name']}" : '';
$prefix = str_repeat('-', $directory['level']);
echo "$prefix {$directory['name']} $parent_name <br/>"; // <-----------
if(count($directory['children'])){
// list the children directories
showDirectories($directory['children'], $directory);
}
}
}
showDirectories($directories);
// pathA
// - subPathA1 (parent: pathA)
// -- subsubPathA11 (parent: subPathA1)
// - subPathA2
// pathB
// pathC

You can try this function (PHP 7 required)
function getDirectories(string $path) : array
{
$directories = [];
$items = scandir($path);
foreach ($items as $item) {
if($item == '..' || $item == '.')
continue;
if(is_dir($path.'/'.$item))
$directories[] = $item;
}
return $directories;
}

Non-recursively List Only Directories
The only question that direct asked this has been erroneously closed, so I have to put it here.
It also gives the ability to filter directories.
/**
* Copyright © 2020 Theodore R. Smith <https://www.phpexperts.pro/>
* License: MIT
*
* #see https://stackoverflow.com/a/61168906/430062
*
* #param string $path
* #param bool $recursive Default: false
* #param array $filtered Default: [., ..]
* #return array
*/
function getDirs($path, $recursive = false, array $filtered = [])
{
if (!is_dir($path)) {
throw new RuntimeException("$path does not exist.");
}
$filtered += ['.', '..'];
$dirs = [];
$d = dir($path);
while (($entry = $d->read()) !== false) {
if (is_dir("$path/$entry") && !in_array($entry, $filtered)) {
$dirs[] = $entry;
if ($recursive) {
$newDirs = getDirs("$path/$entry");
foreach ($newDirs as $newDir) {
$dirs[] = "$entry/$newDir";
}
}
}
}
return $dirs;
}

<?php
/*this will do what you asked for, it only returns the subdirectory names in a given
path, and you can make hyperlinks and use them:
*/
$yourStartingPath = "photos\\";
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($yourStartingPath),
RecursiveIteratorIterator::SELF_FIRST);
foreach($iterator as $file) {
if($file->isDir()) {
$path = strtoupper($file->getRealpath()) ;
$path2 = PHP_EOL;
$path3 = $path.$path2;
$result = end(explode('/', $path3));
echo "<br />". basename($result );
}
}
/* best regards,
Sanaan Barzinji
Erbil
*/
?>

This is the one liner code:
$sub_directories = array_map('basename', glob($directory_path . '/*', GLOB_ONLYDIR));

Proper way
/**
* Get all of the directories within a given directory.
*
* #param string $directory
* #return array
*/
function directories($directory)
{
$glob = glob($directory . '/*');
if($glob === false)
{
return array();
}
return array_filter($glob, function($dir) {
return is_dir($dir);
});
}
Inspired by Laravel

The following recursive function returns an array with the full list of sub directories
function getSubDirectories($dir)
{
$subDir = array();
$directories = array_filter(glob($dir), 'is_dir');
$subDir = array_merge($subDir, $directories);
foreach ($directories as $directory) $subDir = array_merge($subDir, getSubDirectories($directory.'/*'));
return $subDir;
}
Source: https://www.lucidar.me/en/web-dev/how-to-get-subdirectories-in-php/

You can use the glob() function to do this.
Here is some documentation on it:
http://php.net/manual/en/function.glob.php

Find all PHP files recursively. The logic should be simple enough to tweak and it aims to be fast(er) by avoiding function calls.
function get_all_php_files($directory) {
$directory_stack = array($directory);
$ignored_filename = array(
'.git' => true,
'.svn' => true,
'.hg' => true,
'index.php' => true,
);
$file_list = array();
while ($directory_stack) {
$current_directory = array_shift($directory_stack);
$files = scandir($current_directory);
foreach ($files as $filename) {
// Skip all files/directories with:
// - A starting '.'
// - A starting '_'
// - Ignore 'index.php' files
$pathname = $current_directory . DIRECTORY_SEPARATOR . $filename;
if (isset($filename[0]) && (
$filename[0] === '.' ||
$filename[0] === '_' ||
isset($ignored_filename[$filename])
))
{
continue;
}
else if (is_dir($pathname) === TRUE) {
$directory_stack[] = $pathname;
} else if (pathinfo($pathname, PATHINFO_EXTENSION) === 'php') {
$file_list[] = $pathname;
}
}
}
return $file_list;
}

If you're looking for a recursive directory listing solutions. Use below code I hope it should help you.
<?php
/**
* Function for recursive directory file list search as an array.
*
* #param mixed $dir Main Directory Path.
*
* #return array
*/
function listFolderFiles($dir)
{
$fileInfo = scandir($dir);
$allFileLists = [];
foreach ($fileInfo as $folder) {
if ($folder !== '.' && $folder !== '..') {
if (is_dir($dir . DIRECTORY_SEPARATOR . $folder) === true) {
$allFileLists[$folder . '/'] = listFolderFiles($dir . DIRECTORY_SEPARATOR . $folder);
} else {
$allFileLists[$folder] = $folder;
}
}
}
return $allFileLists;
}//end listFolderFiles()
$dir = listFolderFiles('your searching directory path ex:-F:\xampp\htdocs\abc');
echo '<pre>';
print_r($dir);
echo '</pre>'
?>

Find all subfolders under a specified directory.
<?php
function scanDirAndSubdir($dir, &$fullDir = array()){
$currentDir = scandir($dir);
foreach ($currentDir as $key => $filename) {
$realpath = realpath($dir . DIRECTORY_SEPARATOR . $filename);
if (!is_dir($realpath) && $filename != "." && $filename != "..") {
scanDirAndSubdir($realpath, $fullDir);
} else {
$fullDir[] = $realpath;
}
}
return $fullDir;
}
var_dump(scanDirAndSubdir('C:/web2.0/'));
Sample :
array (size=4)
0 => string 'C:/web2.0/config/' (length=17)
1 => string 'C:/web2.0/js/' (length=13)
2 => string 'C:/web2.0/mydir/' (length=16)
3 => string 'C:/web2.0/myfile/' (length=17)

Related

Recursive tree folder function php not going into subfolders

I am currently working on a php file indexer and I need to create a recursive function to create an array that will contains the files and subfolders list of the parent folder, with the subfolders also being arrays containing their files and their subfolders (etc...). As it is a school project, I cannot use DirectoryRecursiveIterator and its siblings RecursiveIterator and DirectoryIterator.
My issue is that it scans the parent folder and finds the subfolders and files but does not go in subfolders to find files and subfolders.
The code
<?php
class H5AI
{
// Properties
private $_tree;
private $_path;
// Construct
public function __construct($_path)
{
$_tree = [];
$parent = $_tree;
print_r($this->getFiles($_path, $parent));
}
// Methods
public function getPath()
{
return $this->_path;
}
public function getTree()
{
return $this->_tree;
}
public function getFiles($path, $parent)
{
//Opening the directory
$dirHandle = opendir($path);
while (false !== $entry = readdir($dirHandle)) {
//If file found
if (!is_dir($path . DIRECTORY_SEPARATOR . $entry)) {
array_push($parent, $entry);
}
// When subdirs found (ignore . & ..)
else if (is_dir($path . DIRECTORY_SEPARATOR . $entry) && $entry !== "." && $entry !== "..") {
$newPath = $path . DIRECTORY_SEPARATOR . $entry;
$parent[$entry] = [];
$this->getFiles($newPath, $parent[$entry]);
}
}
return $parent;
}
}
// Calling function
$h5a1 = new H5AI($argv[1]);
// Command I use in the terminal
php index.php "./test_dir"
//Output
Array
(
[sub_test_dir] => Array
(
)
[0] => test.css
[sub_test_dir2] => Array
(
)
[1] => test.js
[2] => test.html
)
class H5AI {
public function __construct(string $path) {
print_r($this->getFiles($path));
}
public function getFiles(string $directory): array {
$handle = opendir($directory);
$entries = [];
while (true) {
$entry = readdir($handle);
if ($entry === false) {
break;
}
$path = $directory . DIRECTORY_SEPARATOR . $entry;
if (is_file($path) && !str_starts_with($entry, '.')) {
$entries[] = $entry;
} elseif (is_dir($path) && !in_array($entry, [ '.', '..', '$RECYCLE.BIN' /* add other dir names to exclude here */ ])) {
$entries[$entry] = $this->getFiles($path);
}
}
closedir($handle);
return $entries;
}
}
You are creating a separate array, which contains what you want, but is not inserted into your parent array. You have everything right, you just need one little fix:
//...
else if (is_dir($path . DIRECTORY_SEPARATOR . $entry) && $entry !== "." && $entry !== "..") {
$newPath = $path . DIRECTORY_SEPARATOR . $entry;
$parent[$entry] = [];
$parent[$entry] = $this->getFiles($newPath, $parent[$entry]); // <-- fix is on this line
}

get full directory in zf2

hi all I have made a function
function getDirContents($dir, &$results = array()){
$files = scandir($dir);
foreach($files as $key => $value){
$path = realpath($dir.DIRECTORY_SEPARATOR.$value);
if(!is_dir($path)) {
$results[] = $path;
} else if(is_dir($path) && $value != "." && $value != "..") {
getDirContents($path, $results);
$results[] = $path;
}
}
return $results;
}
and I am calling it in like
$value = $this->getDirContents("/var/www/staging/public/files/rgerger");
way from my controller by I am uable to get the full details of the folder I mean the directories and the subdirectories with all contents ..
scandir is on only giving the folder under the target folder which is only scaned but this is not giving me the content of the child folder upto the end.
You need to assign the result of the recursive method call back to the caller.
My (untested) example
function getDirectoryContents($dir)
{
$files = [];
if (is_dir($dir)) {
foreach(scandir($dir) as $key => $fileName) {
$filePath = realpath($dir . DIRECTORY_SEPARATOR . $fileName);
if ($fileName == '.' || $fileName == '..') {
continue;
}
if (is_dir($filePath)) {
$files[] = getDirectoryContents($filePath);
} else {
$files[] = $fileName;
}
}
}
return $files;
}
As per my comment, you can also use the SPL RecursiveDirectoryIterator which is PHP's inbuilt OOP solution for iterating the file system.

php function to read subdir content

I would like to ask what I have to add to make this function to show not only the files on top dir but also the files in subdirs..
private function _populateFileList()
{
$dir_handle = opendir($this->_files_dir);
if (! $dir_handle)
{
return false;
}
while (($file = readdir($dir_handle)) !== false)
{
if (in_array($file, $this->_hidden_files))
{
continue;
}
if (filetype($this->_files_dir . '/' . $file) == 'file')
{
$this->_file_list[] = $file;
}
}
closedir($dir_handle);
return true;
}
Thank you in advance!
You could implement the recursion yourself, or you could use the existing iterator classes to handle the recursion and filesystem traversal for you:
$dirIterator = new RecursiveDirectoryIterator('.', FilesystemIterator::SKIP_DOTS);
$recursiveIterator = new RecursiveIteratorIterator($dirIterator);
$filterIterator = new CallbackFilterIterator($recursiveIterator, function ($file) {
// adjust as needed
static $badFiles = ['foo', 'bar', 'baz'];
return !in_array($file, $badFiles);
});
$files = iterator_to_array($filterIterator);
var_dump($files);
By this you can get all subdir content
customerdel('FolderPath');
function customerdel($dirname=null){
if($dirname!=null){
if (is_dir($dirname))
$dir_handle = opendir($dirname);
if (!$dir_handle)
return false;
while($file = readdir($dir_handle)) {
if ($file != "." && $file != "..") {
if (!is_dir($dirname."/".$file))
echo $dirname."/".$file.'<br>';
else{
echo $dirname.'/'.$file.'<br> ';
customerdel($dirname.'/'.$file);
}
}
}
closedir($dir_handle);
}
}
Here is how you can get a recursive array of all files in a directory and its subdirectories.
The returned array is like: array( [fileName] => [filePath] )
EDIT: I've included a small check if there are filenames in the subdirectories with the same name. If so, an underscore and counter is added to the key-name in the returned array:
array( [fileName]_[COUNTER] => [filePath] )
private function getFileList($directory) {
$fileList = array();
$handle = opendir($directory);
if ($handle) {
while ($entry = readdir($handle)) {
if ($entry !== '.' and $entry !== '..') {
if (is_dir($directory . $entry)) {
$fileList = array_merge($fileList, $this->getFileList($directory . $entry . '/'));
} else {
$i = 0;
$_entry = $entry;
// Check if filename is allready in use
while (array_key_exists($_entry, $fileList)) {
$i++;
$_entry = $entry . "_$i";
}
$fileList[$_entry] = $directory . $entry;
}
}
}
closedir($handle);
}
return $fileList;
}

PHP: sort folder first and then files

$dir = '/master/files';
$files = scandir($dir);
foreach($files as $file){
if(($file != '.') && ($file != '..')){
if(is_dir($dir.'/'.$file)){
echo '<li class="folder">'.$file.'</li>';
}else{
echo '<li class="file">'.$file.'</li>';
}
}
}
From the script above, I get result:
images (folder)
index.html
javascript (folder)
style.css
How to sort the folder first and then files?
Try this :
$dir = '/master/files';
$directories = array();
$files_list = array();
$files = scandir($dir);
foreach($files as $file){
if(($file != '.') && ($file != '..')){
if(is_dir($dir.'/'.$file)){
$directories[] = $file;
}else{
$files_list[] = $file;
}
}
}
foreach($directories as $directory){
echo '<li class="folder">'.$directory.'</li>';
}
foreach($files_list as $file_list){
echo '<li class="file">'.$file_list.'</li>';
}
You don't need to make 2 loops, you can do the job with this piece of code:
<?php
function scandirSorted($path) {
$sortedData = array();
foreach(scandir($path) as $file) {
// Skip the . and .. folder
if($file == '.' || $file == '..')
continue;
if(is_file($path . $file)) {
// Add entry at the end of the array
array_push($sortedData, '<li class="folder">' . $file . '</li>');
} else {
// Add entry at the begin of the array
array_unshift($sortedData, '<li class="file">' . $file . '</li>');
}
}
return $sortedData;
}
?>
This function will return the list of entries of your path, folders first, then files.
Modifying your code as little as possible:
$folder_list = "";
$file_list = "";
$dir = '/master/files';
$files = scandir($dir);
foreach($files as $file){
if(($file != '.') && ($file != '..')){
if(is_dir($dir.'/'.$file)){
$folder_list .= '<li class="folder">'.$file.'</li>';
}else{
$file_list .= '<li class="file">'.$file.'</li>';
}
}
}
print $folder_list;
print $file_list;
This only loops through everything once, rather than requiring multiple passes.
I have a solution for N deep recursive directory scan:
function scanRecursively($dir = "/") {
$scan = array_diff(scandir($dir), array('.', '..'));
$tree = array();
$queue = array();
foreach ( $scan as $item )
if ( is_file($item) ) $queue[] = $item;
else $tree[] = scanRecursively($dir . '/' . $item);
return array_merge($tree, $queue);
}
Store the output in 2 arrays, then iterate through the arrays to output them in the right order.
$dir = '/master/files';
$contents = scandir($dir);
// create blank arrays to store folders and files
$folders = $files = array();
foreach ($contents as $file) {
if (($file != '.') && ($file != '..')) {
if (is_dir($dir.'/'.$file)) {
// add to folders array
$folders[] = $file;
} else {
// add to files array
$files[] = $file;
}
}
}
// output folders
foreach ($folders as $folder) {
echo '<li class="folder">' . $folder . '</li>';
}
// output files
foreach ($files as $file) {
echo '<li class="file">' . $file . '</li>';
}
Being a CodeIgniter lover, I have in fact modified the core directory_helper from that to include the ability to have certain files exempt from the scanning in addition to setting the depth and choosing if hidden files should be included.
All credit goes to the original authors of CI. I simply added to it
with the exempt array and building in the sorting.
It uses ksort to order the folders, as the folder name is set as the key and natsort to order the files inside each folder.
The only thing you may need to do is define what the DIRECTORY_SEPARATOR is for your environment but I don't think you will need to modify much else.
function directory_map($source_dir, $directory_depth = 0, $hidden = FALSE, $exempt = array())
{
if ($fp = #opendir($source_dir))
{
$folddata = array();
$filedata = array();
$new_depth = $directory_depth - 1;
$source_dir = rtrim($source_dir, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;
while (FALSE !== ($file = readdir($fp)))
{
// Remove '.', '..', and hidden files [optional]
if ($file === '.' OR $file === '..' OR ($hidden === FALSE && $file[0] === '.'))
{
continue;
}
is_dir($source_dir.$file) && $file .= DIRECTORY_SEPARATOR;
if (($directory_depth < 1 OR $new_depth > 0) && is_dir($source_dir.$file))
{
$folddata[$file] = directory_map($source_dir.$file, $new_depth, $hidden, $exempt);
}
elseif(empty($exempt) || !empty($exempt) && !in_array($file, $exempt))
{
$filedata[] = $file;
}
}
!empty($folddata) ? ksort($folddata) : false;
!empty($filedata) ? natsort($filedata) : false;
closedir($fp);
return array_merge($folddata, $filedata);
}
return FALSE;
}
Usage example would be:
$filelist = directory_map('full_server_path');
As mentioned above, it will set the folder name as the key for the child array, so you can expect something along the lines of the following:
Array(
[documents/] => Array(
[0] => 'document_a.pdf',
[1] => 'document_b.pdf'
),
[images/] => Array(
[tn/] = Array(
[0] => 'picture_a.jpg',
[1] => 'picture_b.jpg'
),
[0] => 'picture_a.jpg',
[1] => 'picture_b.jpg'
),
[0] => 'file_a.jpg',
[1] => 'file_b.jpg'
);
Just keep in mind that the exempt will be applied to all folders. This is handy if you want to skip out a index.html file or other file that is used in the directories you don't want included.
Try
<?php
$path = $_SERVER['DOCUMENT_ROOT'];
chdir ($path);
$dir = array_diff (scandir ('.'), array ('.', '..', '.DS_Store', 'Thumbs.db'));
usort ($dir, create_function ('$a,$b', '
return is_dir ($a)
? (is_dir ($b) ? strnatcasecmp ($a, $b) : -1)
: (is_dir ($b) ? 1 : (
strcasecmp (pathinfo ($a, PATHINFO_EXTENSION), pathinfo ($b, PATHINFO_EXTENSION)) == 0
? strnatcasecmp ($a, $b)
: strcasecmp (pathinfo ($a, PATHINFO_EXTENSION), pathinfo ($b, PATHINFO_EXTENSION))
))
;
'));
header ('content-type: text/plain');
print_r ($dir);
?>
public function sortedScanDir($dir) {
// scan the current folder
$content = scandir($dir);
// create arrays
$folders = [];
$files = [];
// loop through
foreach ($content as $file) {
$fileName = $dir . '/' . $file;
if (is_dir($fileName)) {
$folders[] = $file;
} else {
$files[] = $file;
}
}
// combine
return array_merge($folders, $files);
}

something missing in object by scanning directory

i coded the following object in php. it was working, but after changing something in my structur the directory crawler only save the last found directory in the array.
<?php
include_once('root.php');
class crawler
{
private $pages = array();
/**
* #param string $dir
* #return array current_dir
*/
function found_dir($dir)
{
// echo "Found (dir): {$dir}\n";
// add $dir to $pages
$parts = explode("/", $dir);
$current = &$this->pages;
// e.g. ./content/page/sub_page/
// [0] => '.'
// [1] => 'content'
// [-1] => ''
for ($i = 2; $i < count($parts) - 1; $i++) {
if (!in_array('sub', $current))
$current['sub'] = array();
if (!in_array($parts[$i], $current['sub']))
$current['sub'][$parts[$i]] = array('name' => $parts[$i]);
$current = &$current['sub'][$parts[$i]];
}
return $current;
}
/**
* #param string $file
*/
function found_file($file)
{
// echo "Found (file): {$file}\n";
// move to current $dir
$current = &$this->found_dir($file);
// add $file to $pages
$parts = explode(".", substr($file, strrpos($file, "/") + 1));
if (count($parts) == 1) {
$current[$parts[0]] = file_get_contents($file);
} else {
if (strlen($parts[0]) == 0)
$current[$parts[1]] = true;
else if (!in_array($parts[0], $current))
$current[$parts[0]] = array($parts[1] => file_get_contents($file));
else
$current[$parts[0]][$parts[1]] = file_get_contents($file);
}
}
/**
* #param string $root
*/
function read_all_files($root = '.')
{
// vars for handling
$directories = array();
$last_letter = $root[strlen($root) - 1];
$root = ($last_letter == '\\' || $last_letter == '/') ? $root : $root . DIRECTORY_SEPARATOR;
$directories[] = $root;
while (sizeof($directories)) {
$dir = array_pop($directories);
if ($handle = opendir($dir)) {
while (false !== ($file = readdir($handle))) {
if ($file == '.' || $file == '..') {
continue;
}
$file = $dir . $file;
if (is_dir($file)) {
$directory_path = $file . DIRECTORY_SEPARATOR;
array_push($directories, $directory_path);
} elseif (is_file($file))
$this->found_file($file);
}
closedir($handle);
}
}
}
/**
* #return root
*/
public function generatePages()
{
// get available files, create temp array
$this->read_all_files('./content/');
// print
print_r($this->pages);
// loop array
return new root($this->pages);
}
}
i can't find the error. perhaps somebody of you see the error. thank you very much for help!
beste regards
You're using in_array where you should be using isset():
$pages = array('sub' => array())
in_array('sub', $pages) => false
isset($pages['sub']) => true

Categories