Iterate through directories recursively in PHP - php

When I upload photos to my web server I try to split up the photos into several folders so that I won't have so many photos in 1 single folder.
for example inside a class:
$filename = hash('crc32b', mt_rand());
$img_dir = UPLOAD_DIR.DS.'img'.DS.$filename[0].$filename[1].DS.$filename[2].$filename[3].DS.$filename[4].$filename[5];
DS is a short version of DIRECTORY_SEPARATOR
This could create directories like these:
public_html\assets\upload\img\25\55\8b
public_html\assets\upload\img\00\8c\2a
if file does not exist and there are no directories like that already
$img_path = $img_dir.DS.$filename.'.jpg';
if (!file_exists($img_path) && !is_dir($img_dir)) {
$mode = 0755;
mkdir($img_dir, $mode, true);
chmod(UPLOAD_DIR, $mode);
chmod(UPLOAD_DIR.DS.'img', $mode);
chmod(UPLOAD_DIR.DS.'img'.DS.$filename[0].$filename[1], $mode);
chmod(UPLOAD_DIR.DS.'img'.DS.$filename[0].$filename[1].DS.$filename[2].$filename[3], $mode);
chmod(UPLOAD_DIR.DS.'img'.DS.$filename[0].$filename[1].DS.$filename[2].$filename[3].DS.$filename[4].$filename[5], $mode);
}
I wan't all folders to be 755, how can do that recursively?
EDIT:
Also why do I get 493 as output when I do echo $mode; or echo 0755; ?

If you have access to the shell, and assuming the OS is Linux, I'd go with:
system('chmod -R ' . escapeshellarg(UPLOAD_DIR));
As for your edit, 0755 is an octal which in decimal is 493.
EDIT: you can also try this function, beware that $path should be the full path to a single file, or the path to a directory without trailing slash
function chmod_recursive($path, $mode) {
if(is_dir($path)) {
foreach(glob("$path/*") as $file) {
chmod_recursive($file, $mode);
}
}
else if(is_file($path)) {
chmod($path, $mode);
}
}
calling it like this:
chmod_recursive(UPLOAD_DIR, 0755);

Iterate over the directories starting at the root:
foreach (new DirectoryIterator(UPLOAD_DIR) as $fileInfo) {
if($fileInfo->isDot()) continue;
chmod($fileInfo->getFilename(), $mode);
}
taken from here

Related

How can I find files and folders whose names start with a dot?

I am trying to write a script to recursively descend into a directory structure to find *.sm and *.ssc files. However, my script skips over folders whose names start with a dot, for example .Folder Name. This is on Windows - the folder is not hidden.
An example of a correct result it finds:
/mydir/Directory Name/File Name.sm
An example of a file it does not find:
/mydir/.Directory Name/.File Name.sm
function findFiles($directory) {
function glob_recursive($directory, &$directories = array()) {
foreach(glob($directory, GLOB_ONLYDIR | GLOB_NOSORT) as $folder) {
$directories[] = $folder;
glob_recursive("{$folder}/*", $directories);
}
}
glob_recursive($directory, $directories);
$files = array ();
foreach($directories as $directory) {
$directory = str_replace(['[',']',"\f[","\f]"], ["\f[","\f]",'[[]','[]]'], $directory);
foreach(glob("{$directory}/*.{sm,ssc}", GLOB_BRACE) as $file) {
$files[] = $file;
}
}
return $files;
}
There is a comment in the source code:
Initial DOT must be matched literally.
So though a folder starts with dot isn't hidden in windows, php still regards it as a special name.
glob('.*');
This returns file or folder start with dot.
So a full list can be
array_merge(glob('.*'), glob('*'));

Creating and chmoding multiple directories with mkdir

I'm using mkdir function to create and chmod directories $dirX $dirY.
The following code block creates $dirY only and upload the desired file there. What's going wrong here? Why the other directory isn't being created along with the uploaded file?
$dirA = 'mydir1/';
$dirB = '../mydir2/';
$directory = array('$dirA','$dirB');
foreach ($directory as $dir);
if (!is_dir($dir)){
mkdir($dir, 0777)
};
for($f=0; $f<count($_FILES['newsimage_upload']['name']); $f++) {
$nume_f = $_FILES['newsimage_upload']['name'][$f];
$thefile = $dir . '/'. $nume_f; //It doesn't set the directories of array's strings
if (!move_uploaded_file ($_FILES['newsimage_upload']['tmp_name'][$f], $thefile)) {
$uploadresult[$f] = 'The file '. $nume_f. 'could not be copied, try again';
}
Your code should be something similar to:
$dirA = 'mydir1/';
$dirB = '../mydir2/';
$directory = array('$dirA','$dirB');
foreach ($directory as $dir){
if (!is_dir($dir)) mkdir($dir, 0777);
for($f=0; $f<count($_FILES['newsimage_upload']['name']); $f++) {
$nume_f = $_FILES['newsimage_upload']['name'][$f];
$thefile = $dir . '/'. $nume_f; //It doesn't set the directories of array's strings
if (!move_uploaded_file ($_FILES['newsimage_upload']['tmp_name'][$f], $thefile)) {
$uploadresult[$f] = 'The file '. $nume_f. 'could not be copied, try again';
}
//some more code
} //closing the for
//some more code
} //closing the foreach
Note that in your original code example there is a missing closing curly brace for the for loop, so I assume you closed it in your original code before the final foreach closing brace.
This is the problem:
foreach ($directory as $dir);
Make that
foreach ($directory as $dir) {
...
}
And you should be OK.
PHP's mkdir function has this functionality baked in already. Just specify the recursive option as true.
You also need to use realpath to resolve paths with dots in them.
Also, as stated by another answerer -- you need brackets around the inner-code block of your initial foreach.
$dirA = 'mydir1/';
$dirB = '../mydir2/';
$directory = array('$dirA','$dirB');
foreach ($directory as $dir){
// Note the next two lines which I have modified:
$realPath = realpath($dir);
if (!is_dir($realPath)) mkdir($realPath, 0777, $recursive=true);
for($f=0; $f<count($_FILES['newsimage_upload']['name']); $f++) {
$nume_f = $_FILES['newsimage_upload']['name'][$f];
$thefile = $dir . '/'. $nume_f; //It doesn't set the directories of array's strings
if (!move_uploaded_file ($_FILES['newsimage_upload']['tmp_name'][$f], $thefile)) {
$uploadresult[$f] = 'The file '. $nume_f. 'could not be copied, try again';
}
//some more code
} //closing the for
//some more code
} //closing the foreach
the return code from mkdir() should be checked to assure the directory was actually created.
the value of umask() may stop the created directory(s) from having the desired permissions,
so umask() should be saved/set to an appropriate value, then restored after the directory(s) are created.
There may be a problem with the line:
$directory = array('$dirA','$dirB');
as single quotes do not expand variables into their contents.
I suggest using double quotes on that line.

Create file and folders recursively

I got an array containing path names and file names
['css/demo/main.css', 'home.css', 'admin/main.css','account']
I want to create those files and folders if they are not existed yet. Overwrite them if they are already existed.
For each of this paths you'll have to specific whether it is a file or a directory. Or you could make your script assume, that the path is pointing to a file when the basename (the last part of the path) contains a dot.
To create a directory recursively is simple:
mkdir(dirname($path), 0755, true); // $path is a file
mkdir($path, 0755, true); // $path is a directory
0755 is the file permission expression, you can read about it here: http://ch.php.net/manual/en/function.chmod.php
<?php
function mkpath($path)
{
if(#mkdir($path) or file_exists($path)) return true;
return (mkpath(dirname($path)) and mkdir($path));
}
?>
This makes paths recursively.
I have just used a simple way to explode the string and rebuild and check if is a file or a directory
public function mkdirRecursive($path) {
$str = explode(DIRECTORY_SEPARATOR, $path);
$dir = '';
foreach ($str as $part) {
$dir .= DIRECTORY_SEPARATOR. $part ;
if (!is_dir($dir) && strlen($dir) > 0 && strpos($dir, ".") == false) {
mkdir($dir , 655);
}elseif(!file_exists($dir) && strpos($dir, ".") !== false){
touch($dir);
}
}
}
You can use is_dir
if (!is_dir($path))
{
#mkdir($path, 0777, true);
}

Deleting all files from a folder using PHP?

For example I had a folder called `Temp' and I wanted to delete or flush all files from this folder using PHP. Could I do this?
$files = glob('path/to/temp/*'); // get all file names
foreach($files as $file){ // iterate files
if(is_file($file)) {
unlink($file); // delete file
}
}
If you want to remove 'hidden' files like .htaccess, you have to use
$files = glob('path/to/temp/{,.}*', GLOB_BRACE);
If you want to delete everything from folder (including subfolders) use this combination of array_map, unlink and glob:
array_map( 'unlink', array_filter((array) glob("path/to/temp/*") ) );
This call can also handle empty directories ( thanks for the tip, #mojuba!)
Here is a more modern approach using the Standard PHP Library (SPL).
$dir = "path/to/directory";
if(file_exists($dir)){
$di = new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS);
$ri = new RecursiveIteratorIterator($di, RecursiveIteratorIterator::CHILD_FIRST);
foreach ( $ri as $file ) {
$file->isDir() ? rmdir($file) : unlink($file);
}
}
foreach (new DirectoryIterator('/path/to/directory') as $fileInfo) {
if(!$fileInfo->isDot()) {
unlink($fileInfo->getPathname());
}
}
This code from http://php.net/unlink:
/**
* Delete a file or recursively delete a directory
*
* #param string $str Path to file or directory
*/
function recursiveDelete($str) {
if (is_file($str)) {
return #unlink($str);
}
elseif (is_dir($str)) {
$scan = glob(rtrim($str,'/').'/*');
foreach($scan as $index=>$path) {
recursiveDelete($path);
}
return #rmdir($str);
}
}
$dir = 'your/directory/';
foreach(glob($dir.'*.*') as $v){
unlink($v);
}
Assuming you have a folder with A LOT of files reading them all and then deleting in two steps is not that performing.
I believe the most performing way to delete files is to just use a system command.
For example on linux I use :
exec('rm -f '. $absolutePathToFolder .'*');
Or this if you want recursive deletion without the need to write a recursive function
exec('rm -f -r '. $absolutePathToFolder .'*');
the same exact commands exists for any OS supported by PHP.
Keep in mind this is a PERFORMING way of deleting files. $absolutePathToFolder MUST be checked and secured before running this code and permissions must be granted.
See readdir and unlink.
<?php
if ($handle = opendir('/path/to/files'))
{
echo "Directory handle: $handle\n";
echo "Files:\n";
while (false !== ($file = readdir($handle)))
{
if( is_file($file) )
{
unlink($file);
}
}
closedir($handle);
}
?>
The simple and best way to delete all files from a folder in PHP
$files = glob('my_folder/*'); //get all file names
foreach($files as $file){
if(is_file($file))
unlink($file); //delete file
}
Got this source code from here - http://www.codexworld.com/delete-all-files-from-folder-using-php/
unlinkr function recursively deletes all the folders and files in given path by making sure it doesn't delete the script itself.
function unlinkr($dir, $pattern = "*") {
// find all files and folders matching pattern
$files = glob($dir . "/$pattern");
//interate thorugh the files and folders
foreach($files as $file){
//if it is a directory then re-call unlinkr function to delete files inside this directory
if (is_dir($file) and !in_array($file, array('..', '.'))) {
echo "<p>opening directory $file </p>";
unlinkr($file, $pattern);
//remove the directory itself
echo "<p> deleting directory $file </p>";
rmdir($file);
} else if(is_file($file) and ($file != __FILE__)) {
// make sure you don't delete the current script
echo "<p>deleting file $file </p>";
unlink($file);
}
}
}
if you want to delete all files and folders where you place this script then call it as following
//get current working directory
$dir = getcwd();
unlinkr($dir);
if you want to just delete just php files then call it as following
unlinkr($dir, "*.php");
you can use any other path to delete the files as well
unlinkr("/home/user/temp");
This will delete all files in home/user/temp directory.
Another solution:
This Class delete all files, subdirectories and files in the sub directories.
class Your_Class_Name {
/**
* #see http://php.net/manual/de/function.array-map.php
* #see http://www.php.net/manual/en/function.rmdir.php
* #see http://www.php.net/manual/en/function.glob.php
* #see http://php.net/manual/de/function.unlink.php
* #param string $path
*/
public function delete($path) {
if (is_dir($path)) {
array_map(function($value) {
$this->delete($value);
rmdir($value);
},glob($path . '/*', GLOB_ONLYDIR));
array_map('unlink', glob($path."/*"));
}
}
}
Posted a general purpose file and folder handling class for copy, move, delete, calculate size, etc., that can handle a single file or a set of folders.
https://gist.github.com/4689551
To use:
To copy (or move) a single file or a set of folders/files:
$files = new Files();
$results = $files->copyOrMove('source/folder/optional-file', 'target/path', 'target-file-name-for-single-file.only', 'copy');
Delete a single file or all files and folders in a path:
$files = new Files();
$results = $files->delete('source/folder/optional-file.name');
Calculate the size of a single file or a set of files in a set of folders:
$files = new Files();
$results = $files->calculateSize('source/folder/optional-file.name');
<?
//delete all files from folder & sub folders
function listFolderFiles($dir)
{
$ffs = scandir($dir);
echo '<ol>';
foreach ($ffs as $ff) {
if ($ff != '.' && $ff != '..') {
if (file_exists("$dir/$ff")) {
unlink("$dir/$ff");
}
echo '<li>' . $ff;
if (is_dir($dir . '/' . $ff)) {
listFolderFiles($dir . '/' . $ff);
}
echo '</li>';
}
}
echo '</ol>';
}
$arr = array(
"folder1",
"folder2"
);
for ($x = 0; $x < count($arr); $x++) {
$mm = $arr[$x];
listFolderFiles($mm);
}
//end
?>
For me, the solution with readdir was best and worked like a charm. With glob, the function was failing with some scenarios.
// Remove a directory recursively
function removeDirectory($dirPath) {
if (! is_dir($dirPath)) {
return false;
}
if (substr($dirPath, strlen($dirPath) - 1, 1) != '/') {
$dirPath .= '/';
}
if ($handle = opendir($dirPath)) {
while (false !== ($sub = readdir($handle))) {
if ($sub != "." && $sub != ".." && $sub != "Thumb.db") {
$file = $dirPath . $sub;
if (is_dir($file)) {
removeDirectory($file);
} else {
unlink($file);
}
}
}
closedir($handle);
}
rmdir($dirPath);
}
public static function recursiveDelete($dir)
{
foreach (new \DirectoryIterator($dir) as $fileInfo) {
if (!$fileInfo->isDot()) {
if ($fileInfo->isDir()) {
recursiveDelete($fileInfo->getPathname());
} else {
unlink($fileInfo->getPathname());
}
}
}
rmdir($dir);
}
I've built a really simple package called "Pusheh". Using it, you can clear a directory or remove a directory completely (Github link). It's available on Packagist, also.
For instance, if you want to clear Temp directory, you can do:
Pusheh::clearDir("Temp");
// Or you can remove the directory completely
Pusheh::removeDirRecursively("Temp");
If you're interested, see the wiki.
I updated the answer of #Stichoza to remove files through subfolders.
function glob_recursive($pattern, $flags = 0) {
$fileList = glob($pattern, $flags);
foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir) {
$subPattern = $dir.'/'.basename($pattern);
$subFileList = glob_recursive($subPattern, $flags);
$fileList = array_merge($fileList, $subFileList);
}
return $fileList;
}
function glob_recursive_unlink($pattern, $flags = 0) {
array_map('unlink', glob_recursive($pattern, $flags));
}
This is a simple way and good solution. try this code.
array_map('unlink', array_filter((array) array_merge(glob("folder_name/*"))));

I need to find a file in directory and copy it to a different directory

I merely have the file name, without extension (.txt, .eps, etc.)
The directory has several subfolders. So, the file could be anywhere.
How can I seek the filename, without the extension, and copy it to different directory?
http://www.pgregg.com/projects/php/preg_find/preg_find.php.txt seems to be exactly what you need, to find the file. then just use the normal php copy() command http://php.net/manual/en/function.copy.php to copy it.
https://stackoverflow.com/search?q=php+recursive+file
have a look at this http://php.net/manual/en/function.copy.php
as for seeking filenames, could use a database to log where the files are? and use that log to find your files
I found that scandir() is the fastest method for such operations:
function findRecursive($folder, $file) {
foreach (scandir($folder) as $filename) {
$path = $folder . '/' . $filename;
# $filename starts with desired string
if (strpos($filename, $file) === 0) {
return $path;
}
# search sub-directories
if (is_dir($path)) {
$result = findRecursive($path);
if ($result !== NULL) {
return $result;
}
}
}
}
For copying the file, you can use copy():
copy(findRecursive($folder, $partOfFilename), $targetFile);

Categories