php recursive folder readdir vs find performance - php

i came across few articles about performance and readdir
here is the php script:
function getDirectory( $path = '.', $level = 0 ) {
$ignore = array( 'cgi-bin', '.', '..' );
$dh = #opendir( $path );
while( false !== ( $file = readdir( $dh ) ) ){
if( !in_array( $file, $ignore ) ){
$spaces = str_repeat( ' ', ( $level * 4 ) );
if( is_dir( "$path/$file" ) ){
echo "$spaces $file\n";
getDirectory( "$path/$file", ($level+1) );
} else {
echo "$spaces $file\n";
}
}
}
closedir( $dh );
}
getDirectory( "." );
this echo the files/ folders correctly.
now i found this:
$t = system('find');
print_r($t);
which also find all the folders and files then i can create an array like the first code.
i think the system('find'); is faster than the readdir but i want to know if it'S a good practice?
thank you very much

Here's my benchmark using a simple for loop with 10 iteration on my server:
$path = '/home/clad/benchmark/';
// this folder has 10 main directories and each folder as 220 files in each from 1kn to 1mb
// glob no_sort = 0.004 seconds but NO recursion
$files = glob($path . '/*', GLOB_NOSORT);
// 1.8 seconds - not recommended
exec('find ' . $path, $t);
unset($t);
// 0.003 seconds
if ($handle = opendir('.')) {
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {
// action
}
}
closedir($handle);
}
// 1.1 seconds to execute
$path = realpath($path);
$objects = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
foreach($objects as $name => $object) {
// action
}
}
Clearly the readdir is faster to use specially if you have lots of traffic on your site.

'find' is not portable, it's a unix/linux command. readdir() is portable and will work on Windows or any other OS. Moreover, 'find' without any parameters is recursive, so if you're in a dir with lots of subdirs and files, you will get to see all of them, rather than only the contents of that $path.

Related

PHP delete all subdirectories in a directory having expired date?

There is a directory /home/example/public_html/users/files/. Within the directory there are subdirectories with random names like 2378232828923_1298295497.
How do I completely delete the subdirectories which have creation date > 1 month?
There is a good script that I use to delete files, but it don't work with dirs:
$seconds_old = 2629743; //1 month old
$directory = "/home/example/public_html/users/files/";
if( !$dirhandle = #opendir($directory) )
return;
while( false !== ($filename = readdir($dirhandle)) ) {
if( $filename != "." && $filename != ".." ) {
$filename = $directory. "/". $filename;
if( #filectime($filename) < (time()-$seconds_old) )
#unlink($filename); //rmdir maybe?
}
}
you need a recursive function for this.
function remove_dir($dir)
{
chdir($dir);
if( !$dirhandle = #opendir('.') )
return;
while( false !== ($filename = readdir($dirhandle)) ) {
if( $filename == "." || $filename = ".." )
continue;
if( #filectime($filename) < (time()-$seconds_old) ) {
if (is_dir($filename)
remove_dir($filename);
else
#unlink($filename);
}
}
chdir("..");
rmdir($dir);
}
<?php
$dirs = array();
$index = array();
$onemonthback = strtotime('-1 month');
$handle = opendir('relative/path/to/dir');
while($file = readdir($handle){
if(is_dir($file) && $file != '.' && $file != '..'){
$dirs[] = $file;
$index[] = filemtime( 'relative/path/to/dir/'.$file );
}
}
closedir($handle);
asort( $index );
foreach($index as $i => $t) {
if($t < $onemonthback) {
#unlink('relative/path/to/dir/'.$dirs[$i]);
}
}
?>
If PHP runs on a Linux server, you could use a shell command, to improve performance (a recursive PHP function can be inefficient in very large directories):
shell_exec('rm -rf '.$directory);

PHP - Code to traverse a directory and get all the files(images)

i want to write a page that will traverse a specified directory.... and get all the files in that directory...
in my case the directory will only contain images and display the images with their links...
something like this
How to Do it
p.s. the directory will not be user input.. it will be same directory always...
if ($handle = opendir('.')) {
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {
echo "$file\n";
}
}
closedir($handle);
}
use readdir
<?php
//define directory
$dir = "images/";
//open directory
if ($opendir = opendir($dir)){
//read directory
while(($file = readdir($opendir))!= FALSE ){
if($file!="." && $file!= ".."){
echo "<img src='$dir/$file' width='80' height='90'><br />";
}
}
}
?>
source: phpacademy.org
You'll want to use the scandir function to walk the list of files in the directory.
Hi you can use DirectoryIterator
try {
$dir = './';
/* #var $Item DirectoryIterator */
foreach (new DirectoryIterator($dir) as $Item) {
if($Item->isFile()) {
echo $Item->getFilename() . "\n";
}
}
} catch (Exception $e) {
echo 'No files Found!<br />';
}
If you want to pass directories recursively:
http://php.net/manual/en/class.recursivedirectoryiterator.php
/**
* function get files
* #param $path string = path to fine files in
* #param $accept array = array of extensions to accept
* #param currentLevel = 0, stopLevel = 0
* #return array of madmanFile objects, but you can modify it to
* return whatever suits your needs.
*/
public static function getFiles( $path = '.', $accept, $currentLevel = 0, $stopLevel = 0){
$path = trim($path); //trim whitespcae if any
if(substr($path,-1)=='/'){$path = substr($path,0,-1);} //cutoff the last "/" on path if provided
$selectedFiles = array();
try{
//ignore these files/folders
$ignoreRegexp = "/\.(T|t)rash/";
$ignore = array( 'cgi-bin', '.', '..', '.svn');
$dh = #opendir( $path );
//Loop through the directory
while( false !== ( $file = readdir( $dh ) ) ){
// Check that this file is not to be ignored
if( !in_array( $file, $ignore ) and !preg_match($ignoreRegexp,$file)){
$spaces = str_repeat( ' ', ( $currentLevel * 4 ) );
// Its a directory, so we need to keep reading down...
if( is_dir( "$path/$file" ) ){
//merge current selectFiles array with recursion return which is
//another array of selectedFiles
$selectedFiles = array_merge($selectedFiles,MadmanFileManager::getFiles( "$path/$file", $accept, ($currentLe$
} else{
$info = pathinfo($file);
if(in_array($info['extension'], $accept)){
$selectedFiles[] = new MadmanFile($info['filename'], $info['extension'], MadmanFileManager::getSize($
}//end if in array
}//end if/else is_dir
}
}//end while
closedir( $dh );
// Close the directory handle
}catch (Exception $e){
echo 'Caught exception: ', $e->getMessage(), "\n";
}
return $selectedFiles;
}
You could as others have suggested check every file in the dir, or you could use glob to identify files based on extension.
I use something along the lines of:
if ($dir = dir('images'))
{
while(false !== ($file = $dir->read()))
{
if (!is_dir($file) && $file !== '.' && $file !== '..' && (substr($file, -3) === 'jpg' || substr($file, -3) === 'png' || substr($file, -3) === 'gif'))
{
// do stuff with the images
}
}
}
else { echo "Could not open directory"; }
You could also try the glob function:
$path = '/your/path/';
$pattern = '*.{gif,jpg,jpeg,png}';
$images = glob($path . $pattern, GLOB_BRACE);
print_r($images);
$dir = "/etc/php5/";
// Open a known directory, and proceed to read its contents
if (is_dir($dir)) {
if ($dh = opendir($dir)) {
while (($file = readdir($dh)) !== false) {
echo "filename: $file : filetype: " . filetype($dir . $file) . "\n";
}
closedir($dh);
}
}
For further reference :http://php.net/manual/en/function.opendir.php
I would start off by creating a recursive function:
function recurseDir ($dir) {
// open the provided directory
if ($handle = opendir($_SERVER['DOCUMENT_ROOT'].$dir)) {
// we dont want the directory we are in or the parent directory
if ($entry !== "." && $entry !== "..") {
// recursively call the function, if we find a directory
if (is_dir($_SERVER['DOCUMENT_ROOT'].$dir.$entry)) {
recurseDir($dir.$entry);
}
else {
// else we dont find a directory, in which case we have a file
// now we can output anything we want here for each file
// in your case we want to output all the images with the path under it
echo "<img src='".$dir.$entry."'>";
echo "<div><a href='".$dir.$entry."'>".$dir.$entry."</a></div>";
}
}
}
}
The $dir param needs to be in the following format:
"/path/" or "/path/to/files/"
Basically, just don't include the server root, because i have already done that below using $_SERVER['DOCUMENT_ROOT'].
So, in the end just call the recurseDir function we just made in your code once, and it will traverse any sub folders and output the image with the link under it.

Copy entire contents of a directory to another using php

I tried to copy the entire contents of the directory to another location using
copy ("old_location/*.*","new_location/");
but it says it cannot find stream, true *.* is not found.
Any other way
Thanks
Dave
that worked for a one level directory. for a folder with multi-level directories I used this:
function recurseCopy(
string $sourceDirectory,
string $destinationDirectory,
string $childFolder = ''
): void {
$directory = opendir($sourceDirectory);
if (is_dir($destinationDirectory) === false) {
mkdir($destinationDirectory);
}
if ($childFolder !== '') {
if (is_dir("$destinationDirectory/$childFolder") === false) {
mkdir("$destinationDirectory/$childFolder");
}
while (($file = readdir($directory)) !== false) {
if ($file === '.' || $file === '..') {
continue;
}
if (is_dir("$sourceDirectory/$file") === true) {
recurseCopy("$sourceDirectory/$file", "$destinationDirectory/$childFolder/$file");
} else {
copy("$sourceDirectory/$file", "$destinationDirectory/$childFolder/$file");
}
}
closedir($directory);
return;
}
while (($file = readdir($directory)) !== false) {
if ($file === '.' || $file === '..') {
continue;
}
if (is_dir("$sourceDirectory/$file") === true) {
recurseCopy("$sourceDirectory/$file", "$destinationDirectory/$file");
}
else {
copy("$sourceDirectory/$file", "$destinationDirectory/$file");
}
}
closedir($directory);
}
As described here, this is another approach that takes care of symlinks too:
/**
* Copy a file, or recursively copy a folder and its contents
* #author Aidan Lister <aidan#php.net>
* #version 1.0.1
* #link http://aidanlister.com/2004/04/recursively-copying-directories-in-php/
* #param string $source Source path
* #param string $dest Destination path
* #param int $permissions New folder creation permissions
* #return bool Returns true on success, false on failure
*/
function xcopy($source, $dest, $permissions = 0755)
{
$sourceHash = hashDirectory($source);
// Check for symlinks
if (is_link($source)) {
return symlink(readlink($source), $dest);
}
// Simple copy for a file
if (is_file($source)) {
return copy($source, $dest);
}
// Make destination directory
if (!is_dir($dest)) {
mkdir($dest, $permissions);
}
// Loop through the folder
$dir = dir($source);
while (false !== $entry = $dir->read()) {
// Skip pointers
if ($entry == '.' || $entry == '..') {
continue;
}
// Deep copy directories
if($sourceHash != hashDirectory($source."/".$entry)){
xcopy("$source/$entry", "$dest/$entry", $permissions);
}
}
// Clean up
$dir->close();
return true;
}
// In case of coping a directory inside itself, there is a need to hash check the directory otherwise and infinite loop of coping is generated
function hashDirectory($directory){
if (! is_dir($directory)){ return false; }
$files = array();
$dir = dir($directory);
while (false !== ($file = $dir->read())){
if ($file != '.' and $file != '..') {
if (is_dir($directory . '/' . $file)) { $files[] = hashDirectory($directory . '/' . $file); }
else { $files[] = md5_file($directory . '/' . $file); }
}
}
$dir->close();
return md5(implode('', $files));
}
copy() only works with files.
Both the DOS copy and Unix cp commands will copy recursively - so the quickest solution is just to shell out and use these. e.g.
`cp -r $src $dest`;
Otherwise you'll need to use the opendir/readdir or scandir to read the contents of the directory, iterate through the results and if is_dir returns true for each one, recurse into it.
e.g.
function xcopy($src, $dest) {
foreach (scandir($src) as $file) {
if (!is_readable($src . '/' . $file)) continue;
if (is_dir($src .'/' . $file) && ($file != '.') && ($file != '..') ) {
mkdir($dest . '/' . $file);
xcopy($src . '/' . $file, $dest . '/' . $file);
} else {
copy($src . '/' . $file, $dest . '/' . $file);
}
}
}
The best solution is!
<?php
$src = "/home/www/domain-name.com/source/folders/123456";
$dest = "/home/www/domain-name.com/test/123456";
shell_exec("cp -r $src $dest");
echo "<H3>Copy Paste completed!</H3>"; //output when done
?>
With Symfony this is very easy to accomplish:
$fileSystem = new Symfony\Component\Filesystem\Filesystem();
$fileSystem->mirror($from, $to);
See https://symfony.com/doc/current/components/filesystem.html
function full_copy( $source, $target ) {
if ( is_dir( $source ) ) {
#mkdir( $target );
$d = dir( $source );
while ( FALSE !== ( $entry = $d->read() ) ) {
if ( $entry == '.' || $entry == '..' ) {
continue;
}
$Entry = $source . '/' . $entry;
if ( is_dir( $Entry ) ) {
full_copy( $Entry, $target . '/' . $entry );
continue;
}
copy( $Entry, $target . '/' . $entry );
}
$d->close();
}else {
copy( $source, $target );
}
}
Like said elsewhere, copy only works with a single file for source and not a pattern. If you want to copy by pattern, use glob to determine the files, then run copy. This will not copy subdirectories though, nor will it create the destination directory.
function copyToDir($pattern, $dir)
{
foreach (glob($pattern) as $file) {
if(!is_dir($file) && is_readable($file)) {
$dest = realpath($dir . DIRECTORY_SEPARATOR) . basename($file);
copy($file, $dest);
}
}
}
copyToDir('./test/foo/*.txt', './test/bar'); // copies all txt files
<?php
function copy_directory( $source, $destination ) {
if ( is_dir( $source ) ) {
#mkdir( $destination );
$directory = dir( $source );
while ( FALSE !== ( $readdirectory = $directory->read() ) ) {
if ( $readdirectory == '.' || $readdirectory == '..' ) {
continue;
}
$PathDir = $source . '/' . $readdirectory;
if ( is_dir( $PathDir ) ) {
copy_directory( $PathDir, $destination . '/' . $readdirectory );
continue;
}
copy( $PathDir, $destination . '/' . $readdirectory );
}
$directory->close();
}else {
copy( $source, $destination );
}
}
?>
from the last 4th line , make this
$source = 'wordpress';//i.e. your source path
and
$destination ='b';
Full thanks must go to Felix Kling for his excellent answer which I have gratefully used in my code. I offer a small enhancement of a boolean return value to report success or failure:
function recurse_copy($src, $dst) {
$dir = opendir($src);
$result = ($dir === false ? false : true);
if ($result !== false) {
$result = #mkdir($dst);
if ($result === true) {
while(false !== ( $file = readdir($dir)) ) {
if (( $file != '.' ) && ( $file != '..' ) && $result) {
if ( is_dir($src . '/' . $file) ) {
$result = recurse_copy($src . '/' . $file,$dst . '/' . $file);
} else {
$result = copy($src . '/' . $file,$dst . '/' . $file);
}
}
}
closedir($dir);
}
}
return $result;
}
My pruned version of #Kzoty answer.
Thank you Kzoty.
Usage
Helper::copy($sourcePath, $targetPath);
class Helper {
static function copy($source, $target) {
if (!is_dir($source)) {//it is a file, do a normal copy
copy($source, $target);
return;
}
//it is a folder, copy its files & sub-folders
#mkdir($target);
$d = dir($source);
$navFolders = array('.', '..');
while (false !== ($fileEntry=$d->read() )) {//copy one by one
//skip if it is navigation folder . or ..
if (in_array($fileEntry, $navFolders) ) {
continue;
}
//do copy
$s = "$source/$fileEntry";
$t = "$target/$fileEntry";
self::copy($s, $t);
}
$d->close();
}
}
I clone entire directory by SPL Directory Iterator.
function recursiveCopy($source, $destination)
{
if (!file_exists($destination)) {
mkdir($destination);
}
$splFileInfoArr = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);
foreach ($splFileInfoArr as $fullPath => $splFileinfo) {
//skip . ..
if (in_array($splFileinfo->getBasename(), [".", ".."])) {
continue;
}
//get relative path of source file or folder
$path = str_replace($source, "", $splFileinfo->getPathname());
if ($splFileinfo->isDir()) {
mkdir($destination . "/" . $path);
} else {
copy($fullPath, $destination . "/" . $path);
}
}
}
#calling the function
recursiveCopy(__DIR__ . "/source", __DIR__ . "/destination");
For Linux servers you just need one line of code to copy recursively while preserving permission:
exec('cp -a '.$source.' '.$dest);
Another way of doing it is:
mkdir($dest);
foreach ($iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST) as $item)
{
if ($item->isDir())
mkdir($dest.DIRECTORY_SEPARATOR.$iterator->getSubPathName());
else
copy($item, $dest.DIRECTORY_SEPARATOR.$iterator->getSubPathName());
}
but it's slower and does not preserve permissions.
I had a similar situation where I needed to copy from one domain to another on the same server, Here is exactly what worked in my case, you can as well adjust to suit yours:
foreach(glob('../folder/*.php') as $file) {
$adjust = substr($file,3);
copy($file, '/home/user/abcde.com/'.$adjust);
Notice the use of "substr()", without it, the destination becomes '/home/user/abcde.com/../folder/', which might be something you don't want. So, I used substr() to eliminate the first 3 characters(../) in order to get the desired destination which is '/home/user/abcde.com/folder/'. So, you can adjust the substr() function and also the glob() function until it fits your personal needs. Hope this helps.
Long-winded, commented example with return logging, based on parts of most of the answers here:
It is presented as a static class method, but could work as a simple function also:
/**
* Recursive copy directories and content
*
* #link https://stackoverflow.com/a/2050909/591486
* #since 4.7.2
*/
public static function copy_recursive( $source = null, $destination = null, &$log = [] ) {
// is directory ##
if ( is_dir( $source ) ) {
$log[] = 'is_dir: '.$source;
// log results of mkdir call ##
$log[] = '#mkdir( "'.$destination.'" ): '.#mkdir( $destination );
// get source directory contents ##
$source_directory = dir( $source );
// loop over items in source directory ##
while ( FALSE !== ( $entry = $source_directory->read() ) ) {
// skip hidden ##
if ( $entry == '.' || $entry == '..' ) {
$log[] = 'skip hidden entry: '.$entry;
continue;
}
// get full source "entry" path ##
$source_entry = $source . '/' . $entry;
// recurse for directories ##
if ( is_dir( $source_entry ) ) {
$log[] = 'is_dir: '.$source_entry;
// return to self, with new arguments ##
self::copy_recursive( $source_entry, $destination.'/'.$entry, $log );
// break out of loop, to stop processing ##
continue;
}
$log[] = 'copy: "'.$source_entry.'" --> "'.$destination.'/'.$entry.'"';
// copy single files ##
copy( $source_entry, $destination.'/'.$entry );
}
// close connection ##
$source_directory->close();
} else {
$log[] = 'copy: "'.$source.'" --> "'.$destination.'"';
// plain copy, as $destination is a file ##
copy( $source, $destination );
}
// clean up log ##
$log = array_unique( $log );
// kick back log for debugging ##
return $log;
}
Call like:
// call method ##
$log = \namespace\to\method::copy_recursive( $source, $destination );
// write log to error file - you can also just dump it on the screen ##
error_log( var_export( $log, true ) );
I find this to be way simpler, more easily customizable, and to not require any dependency:
foreach(glob("old_location/*") as $file) {
copy($file, "new_location/" . basename($file));
}
// using exec
function rCopy($directory, $destination)
{
$command = sprintf('cp -r %s/* %s', $directory, $destination);
exec($command);
}
For copy entire contents from a directory to another, first you should sure about transfer files that they were transfer correctly. for this reason, we use copy files one by one! in correct directories. we copy a file and check it if true go to next file and continue...
1- I check the safe process of transferring each file with this function:
function checksum($src,$dest)
{
if(file_exists($src) and file_exists($dest)){
return md5_file($src) == md5_file($dest) ? true : false;
}else{
return false;
}
}
2- Now i copy files one by one from src into dest, check it and then continue. (For separate the folders that i don't want to copy them, use exclude array)
$src = __DIR__ . '/src';
$dest = __DIR__ . '/dest';
$exclude = ['.', '..'];
function copyDir($src, $dest, $exclude)
{
!is_dir($dest) ? mkdir($dest) : '';
foreach (scandir($src) as $item) {
$srcPath = $src . '/' . $item;
$destPath = $dest . '/' . $item;
if (!in_array($item, $exclude)) {
if (is_dir($srcPath)) {
copyDir($srcPath, $destPath, $exclude);
} else {
copy($srcPath, $destPath);
if (checksum($srcPath, $destPath)) {
echo 'Success transfer for:' . $srcPath . '<br>';
}else{
echo 'Failed transfer for:' . $srcPath . '<br>';
}
}
}
}
}

Copy all files and folder from one directory to another directory PHP

I have directory called "mysourcedir" it has sonme files and folders. so i want to copy all content from this directory to some other "destinationfolder" on Linux server using PHP.
function full_copy( $source, $target ) {
if ( is_dir( $source ) ) {
#mkdir( $target );
$d = dir( $source );
while ( FALSE !== ( $entry = $d->read() ) ) {
if ( $entry == '.' || $entry == '..' ) {
continue;
}
$Entry = $source . '/' . $entry;
if ( is_dir( $Entry ) ) {
$this->full_copy( $Entry, $target . '/' . $entry );
continue;
}
copy( $Entry, $target . '/' . $entry );
}
$d->close();
}else {
copy( $source, $target );
}
}
I am trying this code, but it does some problem, it creates directory "mysourcedir" at destination location. I am expecting to just copy all files and folders at destination,. Please suggest
Hello there little php developers this not a question but an answer to a question on how to copy files from one folder to another. I have come across over the internet that some developers use rename()instead of copy() to move files from one directory to another.
Here is simple working code. Tested and work like charm.
<===========================The Magic============================>
<?php
$dir = "path/to/targetFiles/";
$dirNew="path/to/newFilesFolder/";
// Open a known directory, and proceed to read its contents
if (is_dir($dir)) {
if ($dh = opendir($dir)) {
while (($file = readdir($dh)) !== false) {
//exclude unwanted
if ($file==".") continue;
if ($file=="..")continue;
//if ($file=="index.php") continue; for example if you have index.php in the folder
if (copy("$dir/$file","$dirNew/$file"))
{
echo "Files Copyed Successfully";
//echo "<img src=$dirNew/$file />";
//if files you are moving are images you can print it from
//new folder to be sure they are there
}
else {echo "File Not Copy";}
}
closedir($dh);
}
}
?>
class FolderCopy {
public static function copyFolder($src, $dest) {
$path = realpath($src);
$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
/** SplFileInfo $object*/
foreach($objects as $name => $object)
{
$startsAt = substr(dirname($name), strlen($src));
self::mkDir($dest.$startsAt);
if(is_writable($dest.$startsAt) and $object->isFile())
{
copy((string)$name, $dest.$startsAt.DIRECTORY_SEPARATOR.basename($name));
}
}
}
private static function mkDir($folder, $perm=0777) {
if(!is_dir($folder)) {
mkdir($folder, $perm);
}
}
}
FolderCopy::copyFolder(dirname(dirname(FILE))."/images", dirname(FILE)."/test");
This is my suggestion.
<?php
/**
* Copy a file, or recursively copy a folder and its contents
*
* #author Aidan Lister <aidan#php.net>
* #version 1.0.1
* #param string $source Source path
* #param string $dest Destination path
* #return bool Returns TRUE on success, FALSE on failure
*/
function copyr($source, $dest)
{
// Simple copy for a file
if (is_file($source)) {
chmod($dest, 777);
return copy($source, $dest);
}
// Make destination directory
if (!is_dir($dest)) {
mkdir($dest);
}
chmod($dest, 777);
// Loop through the folder
$dir = dir($source);
while (false !== $entry = $dir->read()) {
// Skip pointers
if ($entry == '.' || $entry == '..') {
continue;
}
// Deep copy directories
if ($dest !== "$source/$entry") {
copyr("$source/$entry", "$dest/$entry");
}
}
// Clean up
$dir->close();
return true;
}
?>
You probably just want to move that line down, like this:
function full_copy( $source, $target ) {
if ( is_dir( $source ) ) {
$d = dir( $source );
while ( FALSE !== ( $entry = $d->read() ) ) {
if ( $entry == '.' || $entry == '..' ) {
continue;
}
$Entry = $source . '/' . $entry;
if ( is_dir( $Entry ) ) {
#mkdir( $Entry );
$this->full_copy( $Entry, $target . '/' . $entry );
continue;
}
copy( $Entry, $target . '/' . $entry );
}
$d->close();
}else {
copy( $source, $target );
}
Although personally I would avoid using 2 variables with the same name with only captilisation to differentiate them. Check the user comments on http://www.php.net/copy for other possibilities.
I think taht the $d->read() will return also the name of the parent and hat is why you are creating it again in the target directory.
try running cp -a. That takes care of preserving mod times and permissions, and all that. cp -al will make a hardlink farm.
for windows server:
shell_exec('xcopy \\old\\folder \\new\\folder /s /e /y /i');
for linux server:
shell_exec('cp -R /old/folder /new/folder');
I have tried all the examples but no one is copying sub folders and its data. Finally I got the answer:
<?php
$dir = "/var/www/html/json/";
$dirNew = "/var/www/html/json1/";
// Open a known directory, and proceed to read its contents
recurse_copy($dir, $dirNew);
function recurse_copy($src, $dst) {
$dir = opendir($src);
#mkdir($dst);
while (false !== ( $file = readdir($dir))) {
if (( $file != '.' ) && ( $file != '..' )) {
if (is_dir($src . '/' . $file)) {
recurse_copy($src . '/' . $file, $dst . '/' . $file);
} else {
copy($src . '/' . $file, $dst . '/' . $file);
}
}
}
closedir($dir);
}

Recursively counting files with PHP

Simple question for a newb and my Google-Fu is failing me. Using PHP, how can you count the number of files in a given directory, including any sub-directories (and any sub-directories they might have, etc.)? e.g. if directory structure looks like this:
/Dir_A/
/Dir_A/File1.blah
/Dir_A/Dir_B/
/Dir_A/Dir_B/File2.blah
/Dir_A/Dir_B/File3.blah
/Dir_A/Dir_B/Dir_C/
/Dir_A/Dir_B/Dir_C/File4.blah
/Dir_A/Dir_D/
/Dir_A/Dir_D/File5.blah
The script should return with '5' for "./Dir_A".
I've cobbled together the following but it's not quite returning the correct answer, and I'm not sure why:
function getFilecount( $path = '.', $filecount = 0, $total = 0 ){
$ignore = array( 'cgi-bin', '.', '..', '.DS_Store' );
$dh = #opendir( $path );
while( false !== ( $file = readdir( $dh ) ) ){
if( !in_array( $file, $ignore ) ){
if( is_dir( "$path/$file" ) ){
$filecount = count(glob( "$path/$file/" . "*"));
$total += $filecount;
echo $filecount; /* debugging */
echo " $total"; /* debugging */
echo " $path/$file"; /* debugging */
getFilecount( "$path/$file", $filecount, $total);
}
}
}
return $total;
}
I'd greatly appreciate any help.
This should do the trick:
function getFileCount($path) {
$size = 0;
$ignore = array('.','..','cgi-bin','.DS_Store');
$files = scandir($path);
foreach($files as $t) {
if(in_array($t, $ignore)) continue;
if (is_dir(rtrim($path, '/') . '/' . $t)) {
$size += getFileCount(rtrim($path, '/') . '/' . $t);
} else {
$size++;
}
}
return $size;
}
Use the SPL, then see if you still get an error.
RecursiveDirectoryIterator
Usage example:
<?php
$path = realpath('/etc');
$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
foreach($objects as $name => $object){
echo "$name\n";
}
?>
This prints a list of all files and directories under $path (including $path ifself). If you want to omit directories, remove the RecursiveIteratorIterator::SELF_FIRST part.
Then just use isDir()
based on Andrew's answer...
$path = realpath('my-big/directory');
$objects = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path),
RecursiveIteratorIterator::SELF_FIRST
);
$count=iterator_count($objects);
echo number_format($count); //680,642 wooohaah!
like that i'm able to count (not listing) thousands & thousands files. 680,642 files in less than 4.6 seconds actually ;)
Paolo Bergantino was almost with his code, but the function will still count .DS_Store files since he misspelled it. Correct Code below
function getFileCount($path) {
$size = 0;
$ignore = array('.','..','cgi-bin','.DS_Store');
$files = scandir($path);
foreach($files as $t) {
if(in_array($t, $ignore)) continue;
if (is_dir(rtrim($path, '/') . '/' . $t)) {
$size += getFileCount(rtrim($path, '/') . '/' . $t);
} else {
$size++;
}
}
return $size;
}
Why are you passing $filecount? The [passed-in] value is not being used; the only usage is at "$total += $filecount" and you're overriding $filecount just before that.
You're missing the case when the function encounters a regular (non-dir) file.
Edit: I just noticed the call to glob(). It's not necessary. Your function is recursively touching every file in the whole directory tree, anyway. See #Paolo Bergantino's answer.
Check the PHP manual on glob() function: http://php.net/glob
It has examples in comments as to how to make it recursive.

Categories