Get folders with PHP glob - unlimited levels deep - php

I have this working function that finds folders and creates an array.
function dua_get_files($path)
{
foreach (glob($path . "/*", GLOB_ONLYDIR) as $filename)
{
$dir_paths[] = $filename;
}
return $dir_paths;
}
This function can only find the directories on the current location. I want to find the directory paths in the child folders and their children and so on.
The array should still be a flat list of directory paths.
An example of how the output array should look like
$dir_path[0] = 'path/folder1';
$dir_path[1] = 'path/folder1/child_folder1';
$dir_path[2] = 'path/folder1/child_folder2';
$dir_path[3] = 'path/folder2';
$dir_path[4] = 'path/folder2/child_folder1';
$dir_path[5] = 'path/folder2/child_folder2';
$dir_path[6] = 'path/folder2/child_folder3';

If you want to recursively work on directories, you should take a look at the RecursiveDirectoryIterator.
$path = realpath('/etc');
$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
foreach($objects as $name => $object){
echo "$name\n";
}

Very strange - everybody advice recursion, but better just cycle:
$dir ='/dir';
while($dirs = glob($dir . '/*', GLOB_ONLYDIR)) {
$dir .= '/*';
if(!$result) {
$result = $dirs;
} else {
$result = array_merge($result, $dirs);
}
}

Try this instead:
function dua_get_files($path)
{
$data = array();
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
foreach ($files as $file)
{
if (is_dir($file) === true)
{
$data[] = strval($file);
}
}
return $data;
}

Use this function :
function dua_get_files($path)
{
$dir_paths = array();
foreach (glob($path . "/*", GLOB_ONLYDIR) as $filename)
{
$dir_paths[] = $filename;
$a = glob("$filename/*", GLOB_ONLYDIR);
if( is_array( $a ) )
{
$b = dua_get_files( "$filename/*" );
foreach( $b as $c )
{
$dir_paths[] = $c;
}
}
}
return $dir_paths;
}

You can use php GLOB function, but you must create a recursive function to scan directories at infinite level depth. Then store results in a global variable.
function dua_get_files($path) {
global $dir_paths; //global variable where to store the result
foreach ($path as $dir) { //loop the input
$dir_paths[] = $dir; //can use also "basename($dir)" or "realpath($dir)"
$subdir = glob($dir . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR); //use DIRECTORY_SEPARATOR to be OS independent
if (!empty($subdir)) { //if subdir is not empty make function recursive
dua_get_files($subdir); //execute the function again with current subdir
}
}
}
//usage:
$path = array('galleries'); //suport absolute or relative path. support one or multiple path
dua_get_files($path);
print('<pre>'.print_r($dir_paths,true).'</pre>'); //debug

For PHP, if you are on a linux/unix, you can also use backticks (shell execution) with the unix find command. Directory searching on the filesystem can take a long time and hit a loop -- the system find command is already built for speed and to handle filesystem loops. In other words, the system exec call is likely to cost far less cpu-time than using PHP itself to search the filesystem tree.
$dirs = `find $path -type d`;
Remember to sanitize the $path input, so other users don't pass in security compromising path names (like from the url or something).
To put it into an array
$dirs = preg_split("/\s*\n+\s*/",`find $path -type d`,-1,PREG_SPLIT_NO_EMPTY);

Related

Rename all subdirectories within a directory recursively

I'm currently using the following code to list all of the subdirectories within a specific directory.
$dir = realpath('custom_design_copy/');
$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir), RecursiveIteratorIterator::SELF_FIRST);
foreach($objects as $name => $object){
if(is_dir($object)){
echo "$name<br />";
}
}
This gives me results that look something like this.
C:\Data\Web\custom_design_copy\Product
C:\Data\Web\custom_design_copy\Product\images
C:\Data\Web\custom_design_copy\Product\Scripts
What I want to do is rename all of these subdirectories with strtoupper() in order to normalize all of their names. I'm aware of the rename() function but I fear that if I try the following:
rename($name, strtoupper($name));
I'll wind up modifying one of custom_design_copy's parent directory names, which is what I hope to avoid. How can I avoid this issue? I was considering using substr() and searching for the last occurrence of "\" in order to isolate the directory name at the end, but there has to be an easier solution to this. The end result I'm looking for is this.
C:\Data\Web\custom_design_copy\PRODUCT
C:\Data\Web\custom_design_copy\PRODUCT\IMAGES
C:\Data\Web\custom_design_copy\PRODUCT\SCRIPTS
EDIT: While waiting for advice, I attempted my initial approach and found that it worked.
$dir = realpath('custom_design_copy/');
$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir), RecursiveIteratorIterator::SELF_FIRST);
foreach($objects as $name => $object){
if(is_dir($object)){
$slashPos = strrpos($name, '\\');
$newName = substr($name, 0, $slashPos + 1) . strtoupper(substr($name, $slashPos + 1));
rename($name, $newName);
echo "$name<br />";
}
}
Your example uses the RecursiveIteratorIterator::SELF_FIRST flag, which lists leaves and parents in iteration with parents coming first (from the docs).
This means that with a directory structure like:
foo/
foo/bar/
foo/bar/bat.txt
The iteration order is from parents towards leaves:
foo/
foo/bar/
foo/bat/bat.txt
This can be problematic, as you have noted already.
For your needs, the RecursiveIteratorIterator::CHILD_FIRST flag should be used. This flag instructs the iterator to go in the other direction, from leaves towards parents.
foo/bar/bat.txt
foo/bar/
foo/
You can store all directories in an array and after that you can loop the array in descending order changing the last folder to uppercase.
Here is an example:
$path = 'custom_design_copy' . DIRECTORY_SEPARATOR;
$main_dir = realpath($path);
$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($main_dir), RecursiveIteratorIterator::SELF_FIRST);
foreach ($objects as $name => $object) {
if ($object->isDir() && !in_array($object->getFilename(), array('.', '..'))) {
$directories[] = $name;
}
}
foreach (array_reverse($directories) as $dir) {
rename($dir, last_path_upper($dir));
}
function last_path_upper($str) {
$arr = explode(DIRECTORY_SEPARATOR, $str);
$last = array_pop($arr);
return join(DIRECTORY_SEPARATOR, $arr) . DIRECTORY_SEPARATOR . strtoupper($last);
}

PHP look through every root directory for keyword

So I tried for some days to get this done. But I still don't have a single clue how i could build this code so it works. Maybe someone has an idea.
Goal: Automaticely iterate through the root-directory and any of it's subdirectories. If there's a directory, which matches the keyword it should be stored into an array.
I am only looking for directories, that's why there's a regex to exclude every object with a dot in it's name. Not perfect yet, but that's not a problem.
I ll post the first version of my code. Now it's just scanning the directory your handling to the function when calling it. Because all my other attempts are trash and this one at least works
searchformigration('/');
/* Check for related folders, that could be used for a migration */
function searchformigration($dir)
{
$scanned_elements = scandir($dir);
for($c = 0; $c <= (count($scanned_elements) - 1); $c++)
{
/* Anything but files containing a dot (hidden files, files) */
if(preg_match('/^[^.]+$/', $scanned_elements[$c]))
{
/* Checking for the keyword "Project" */
if($scanned_elements[$c] == '*Project*')
{
echo $scanned_elements[$c];
echo '</br>';
}
else
{
continue;
}
}
else
{
continue;
}
}
}
You can recursively retrieve files and folders with RecursiveDirectoryIterator, this will search in / for directories with 'project' in the foldername.
print_r(get_dirs('/','project'));
function get_dirs($path = '.', $search='') {
$dirs = array();
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path),
RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterator as $file) {
if ($file->isDir())
{
if (strpos($file->getFileName(), $search) !== false)
{
$dirs[] = $file->getRealPath();
}
}
}
return $dirs;
}
I would get all directories recursively and then grep for Project:
function searchformigration($dir) {
$results = glob($dir, GLOB_ONLYDIR);
foreach($results as $subdir) {
$results = array_merge($results, searchformigration($subdir));
}
return $results;
}
$results = preg_grep('/Project/', searchformigration('/'));
You can also do this on Linux:
exec("find / -type d -name 'Project'", $results);

Dir-Content to array - exclude files with specific pattern

I would like to collect all files in a specific directory (at the moment I'm using scandir) - but only those, who do not have a special pattern.
Example:
someimage.png
someimage-150x150.png
someimage-233x333.png
someotherimage.png
someotherimage-760x543.png
someotherimage-150x50.png
In this case I would like to get someimage.png and someotherimage.png as result in my array.
How can I solve this?
To get array of filenames consisting of letters only, you can use this:
$array = array();
$handle = opendir($directory);
while ($file = readdir($handle)) {
if(preg_match('/^[A-Za-z]+\.png$/',$file)){
$array[] = $file;
}
}
The OOP way could be to use a DirectoryIterator in combination with a FilterIterator:
class FilenameFilter extends FilterIterator {
protected $filePattern;
public function __construct(Iterator $iterator , $pattern) {
parent::__construct($iterator);
$this->filePattern = $pattern;
}
public function accept() {
$currentFile = $this->current();
return (1 === preg_match($this->filePattern, $currentFile));
}
}
Usage:
$myFilter = new FilenameFilter(new DirectoryIterator('path/to/your/files'), '/^[a-z-_]*\.(png|PNG|jpg|JPG)$/i');
foreach ($myFilter as $filteredFile) {
// Only files which match your specified pattern should appear here
var_dump($filteredFile);
}
It's just an idea and the code is not tested but. Hope that helps;
$files = array(
"someimage.png",
"someimage-150x150.png",
"someimage-233x333.png",
"someotherimage.png",
"someotherimage-760x543.png",
"someotherimage-150x50.png",
);
foreach ( $files as $key => $value ) {
if ( preg_match( '#\-[0-9]+x[0-9]+\.(png|jpe?g|gif)$#', $value ) ) {
unset( $files[$key] );
}
}
echo '<xmp>' . print_r( $files, 1 ) . '</xmp>';
This regex will fill $correctFiles with all png images that don't contain dimensions (42x42 for example) in their names.
<?php
// here you get the files with scandir, or any method you want
$files = array(
'someimage.png',
'someimage-150x150.png',
'someimage-233x333.png',
'someotherimage.png',
'someotherimage-760x543.png',
'someotherimage-150x50.png'
);
$correctFiles = array(); // This will contain the correct file names
foreach ($files as $file)
if (!preg_match('/^.*-\d+x\d+\.png$/', $file)) // If the file doesn't have "NUMBERxNUMBER" in their name
$correctFiles[] = $file;
print_r($correctFiles); // Here you can do what you want with those files
If you don't want to store the names in an array (faster, less memory consumption), you can use the code below.
<?php
// here you get the files with scandir, or any method you want
$files = array(
'someimage.png',
'someimage-150x150.png',
'someimage-233x333.png',
'someotherimage.png',
'someotherimage-760x543.png',
'someotherimage-150x50.png'
);
foreach ($files as $file)
if (!preg_match('/^.*-\d+x\d+\.png$/', $file)) // If the file doesn't have "NUMBERxNUMBER" in their name
{
print_r($file); // Here you can do what you want with this file
}

Recursive function scanning a folder and storing it in an array

I am trying to make a recursive function to go through all of the folder path that I have given it in the parameters.
What I am trying to do is to store the folder tree into an array for example I have Folder1 and this folder contains 4 text files and another folder and I want the structure to be a multidimensional array like the following
Array 1 = Folder one
Array 1 = text.text.....So on so forth
I have the following function that I build but its not working as I want it too. I know that I need to check whether it is in the root directory or not but when it becomes recursive it becoems harder
function displayAllFolders($root)
{
$foldersArray = array();
$listFolderFile = scandir($root);
foreach($listFolderFile as $row)
{
if($row == "." || $row == "..")
{
continue;
}
elseif(is_dir("$root/$row") == true)
{
$foldersArray["$root/$row"] = "$row";
$folder = "$root/$row";
#$foldersArray[] = displayAllFolders("$root/$row");
}
else
{
$foldersArray[]= array("$root/$row") ;
}
}
var_dump($foldersArray);
}
Using RecursiveDirectoryIterator with RecursiveIteratorIterator this becomes rather easy, e.g.:
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(
// root dir
'.',
// ignore dots
RecursiveDirectoryIterator::SKIP_DOTS
),
// include directories
RecursiveIteratorIterator::SELF_FIRST
// default is:
// RecursiveIteratorIterator::LEAVES_ONLY
//
// which would only list files
);
foreach ($it as $entry) {
/* #var $entry \SplFileInfo */
echo $entry->getPathname(), "\n";
}
Your approach isn't recursive at all.
It would be recursive if you called the same function again in case of a directory. You only make one sweep.
Have a look here:
http://php.net/manual/en/function.scandir.php
A few solutions are posted. I would advise you to start with the usercomment by mmda dot nl.
(function is named dirToArray, exactly what you are tryting to do.)
In case it will be removed, I pasted it here:
function dirToArray($dir) {
$result = array();
$cdir = scandir($dir);
foreach ($cdir as $key => $value) {
if (!in_array($value,array(".",".."))) {
if (is_dir($dir . DIRECTORY_SEPARATOR . $value)) {
$result[$value] = dirToArray($dir . DIRECTORY_SEPARATOR . $value);
}
else {
$result[] = $value;
}
}
}
return $result;
}
Why not using PHP itself? Just have a look at the RecursiveDirectoryIterator of the standard php library (SPL).
$folders = [];
$iterator = new RecursiveDirectoryIterator(new RecursiveDirectoryIterator($directory));
iterator_apply($iterator, 'scanFolders', array($iterator, $folders));
function scanFolders($iterator, $folders) {
while ($iterator->valid()) {
if ($iterator->hasChildren()) {
scanFolders($iterator->getChildren(), $folders);
} else {
$folders[] = $iterator->current();
}
$iterator->next();
}
}

List content of directory (php)

I have a folder. I want to put every file in this folder into an array and afterwards I want to echo them all in an foreach loop.
What's the best way to do this?
Thanks!
Scandir is what you're looking for
http://php.net/manual/en/function.scandir.php
<?php
$dir = '/tmp';
$files1 = scandir($dir);
print_r($files1);
?>
Or use combination of opendir and readdir
http://php.net/manual/en/function.readdir.php
Doesn't get much easier than this
http://ca3.php.net/manual/en/function.scandir.php
Don't forget to filter out hidden and parent directories (they start with a dot) on linux.
An Alternative:
define('PATH', 'files/');
$filesArray = array();
$filesArray = glob(PATH . '*', GLOB_ONLYDIR);
This method allow you to specify/filter a by file type. E.G.,
define('PATH', 'files/');
define('FILE_TYPE', '.jpg');
$filesArray = array();
$filesArray = glob(PATH . '*' . FILE_TYPE, GLOB_ONLYDIR);
You can also get the FULL path name to the file by removing the parameter 'GLOB_ONLYDIR'
This works for files and folders in subfolders too. Return list of folders and list of files with their path.
$dir = __DIR__; //work only for this current dir
function listFolderContent($dir,$path=''){
$r = array();
$list = scandir($dir);
foreach ($list as $item) {
if($item!='.' && $item!='..'){
if(is_file($path.$item)){
$r['files'][] = $path.$item;
}elseif(is_dir($path.$item)){
$r['folders'][] = $path.$item;
$sub = listFolderContent($path.$item,$path.$item.'/');
if(isset($sub['files']) && count($sub['files'])>0)
$r['files'] = isset ($r['files'])?array_merge ($r['files'], $sub['files']):$sub['files'];
if(isset($sub['folders']) && count($sub['folders'])>0)
$r['folders'] = array_merge ($r['folders'], $sub['folders']);
}
}
}
return $r;
}
$list = listFolderContent($dir);
var_dump($list['files']);
var_dump($list['folders']);
Edit: dwich answer is better. I will leave this just for information.
readdir.
<?php
if ($handle = opendir('/path/to/dir')) {
echo "Directory handle: $handle\n";
echo "Files:\n";
while (false !== ($file = readdir($handle))) {
echo "$file\n";
}
closedir($handle);
}
?>
Hope this helps.
—Alberto

Categories