27

On my old VPS I was using the following code to copy the files and directories within a directory to a new directory that was created after the user submitted their form.

function copyr($source, $dest)
{
   // Simple copy for a file
   if (is_file($source)) {
      return copy($source, $dest);
   }

   // Make destination directory
   if (!is_dir($dest)) {
      mkdir($dest);
      $company = ($_POST['company']);
   }

   // 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;
}

copyr('Template/MemberPages', "Members/$company")

However now on my new VPS it will only create the main directory, but will not copy any of the files to it. I don't understand what could have changed between the 2 VPS's?

Doug T.
  • 64,223
  • 27
  • 138
  • 202
Jason
  • 271
  • 1
  • 3
  • 3
  • Your code would be more readable if you indented every block. – Doug T. Apr 18 '11 at 19:30
  • May be the PHP version, try this [recursive copy](http://www.php.net/manual/en/function.copy.php#91256) function from php manual. – SIFE Apr 18 '11 at 19:31
  • SIFE - Tried that one and it didn't work either. So not sure what I am doing wrong. But I just copy the code, input my source and destination paths, and make sure chmod is all set, but it still doesn't work – Jason Apr 18 '11 at 19:34
  • @Jasom may be you don't have enough permission in destination directory to copy to it. – SIFE Apr 18 '11 at 23:19
  • wtf are you doing with the $_POST global here? this kills the generic approach completely and makes the func unusable for me - but anyway there are good examples in the answers :) – jebbie May 20 '15 at 14:59

15 Answers15

85

Try something like this:

$source = "dir/dir/dir";
$dest= "dest/dir";

mkdir($dest, 0755);
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());
  }
}

Iterator iterate through all folders and subfolders and make copy of files from $source to $dest

zanderwar
  • 3,440
  • 3
  • 28
  • 46
OzzyCzech
  • 9,713
  • 3
  • 50
  • 34
  • If you're using Zend Guard, be sure when using $iterator->getSubPathName() to call it as an anonymous function. What worked for me was: call_user_func_array (array ($iterator, "getSubPathName"), array ()); – Someone13 Feb 07 '14 at 09:26
  • Thanks OzzyCzech Very Helpfull Script for.. but i also want to backing up my database kindly please tell about that process or write script or give me any link for that – Syed Arif Iqbal Oct 09 '14 at 20:39
  • 2
    might want to consider a 3rd parameter of true on the mkdir to recursively make the destination directory i.e. mkdir($dest, 0755, true); foreach (... – Andrew Feb 27 '17 at 12:46
  • 1
    Does `RecursiveIteratorIterator` implements a `__call()` method that forwards `getSubPathName()` to the instance of the `RecursiveDirectoryIterator` or how does that work? – David Nov 17 '17 at 08:33
16

Could I suggest that (assuming it's a *nix VPS) that you just do a system call to cp -r and let that do the copy for you.

James C
  • 14,047
  • 1
  • 34
  • 43
9

I have changed Joseph's code (below), because it wasn't working for me. This is what works:

function cpy($source, $dest){
    if(is_dir($source)) {
        $dir_handle=opendir($source);
        while($file=readdir($dir_handle)){
            if($file!="." && $file!=".."){
                if(is_dir($source."/".$file)){
                    if(!is_dir($dest."/".$file)){
                        mkdir($dest."/".$file);
                    }
                    cpy($source."/".$file, $dest."/".$file);
                } else {
                    copy($source."/".$file, $dest."/".$file);
                }
            }
        }
        closedir($dir_handle);
    } else {
        copy($source, $dest);
    }
}

[EDIT] added test before creating a directory (line 7)

Armel Larcier
  • 15,747
  • 7
  • 68
  • 89
Belovoj
  • 818
  • 13
  • 22
9

This function copies folder recursivley very solid. I've copied it from the comments section on copy command of php.net

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); 
}
Armin
  • 15,582
  • 10
  • 47
  • 64
  • For people of the future, I would recommend to put the body of this method in `if(is_dir($src)){ ..code.. }else{ copy($src, $dst); }`. This also prevents DoS on your server if you try to copy regular file using this method – vakus Apr 16 '19 at 19:45
7

The Symfony's FileSystem Component offers a good error handling as well as recursive remove and other useful stuffs. Using @OzzyCzech's great answer, we can do a robust recursive copy this way:

use Symfony\Component\Filesystem\Filesystem;

// ...

$fileSystem = new FileSystem();

if (file_exists($target))
{
    $this->fileSystem->remove($target);
}

$this->fileSystem->mkdir($target);

$directoryIterator = new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS);
$iterator = new \RecursiveIteratorIterator($directoryIterator, \RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterator as $item)
{
    if ($item->isDir())
    {
        $fileSystem->mkdir($target . DIRECTORY_SEPARATOR . $iterator->getSubPathName());
    }
    else
    {
        $fileSystem->copy($item, $target . DIRECTORY_SEPARATOR . $iterator->getSubPathName());
    }
}

Note: you can use this component as well as all other Symfony2 components standalone.

Alain Tiemblo
  • 36,099
  • 17
  • 121
  • 153
  • 4
    The Symfony Filesystem component has a [mirror](https://symfony.com/doc/current/components/filesystem.html#mirror) method : `$filesystem->mirror('/path/to/source', '/path/to/target');` – Silviu G Jan 10 '20 at 09:50
6

Here's what we use at our company:

static public function copyr($source, $dest)
{
    // recursive function to copy
    // all subdirectories and contents:
    if(is_dir($source)) {
        $dir_handle=opendir($source);
        $sourcefolder = basename($source);
        mkdir($dest."/".$sourcefolder);
        while($file=readdir($dir_handle)){
            if($file!="." && $file!=".."){
                if(is_dir($source."/".$file)){
                    self::copyr($source."/".$file, $dest."/".$sourcefolder);
                } else {
                    copy($source."/".$file, $dest."/".$file);
                }
            }
        }
        closedir($dir_handle);
    } else {
        // can also handle simple copy commands
        copy($source, $dest);
    }
}
Joseph Beeson
  • 69
  • 1
  • 4
  • It needs to add if (!file_exists($dest."/".$sourcefolder)) check before mkdir($dest."/".$sourcefolder); in cases when folders are exists – Pavel Nazarov May 26 '16 at 19:42
  • almost worked for me. but replaced like this. 「copy($source."/".$file, $dest."/".$file);」 ⇒ 「 copy($source."/".$file, $dest."/".$sourcefolder."/".$file);」 – harufumi.abe Feb 14 '20 at 07:35
2
function recurse_copy($source, $dest)
{
    // 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);
    }

    // Loop through the folder
    $dir = dir($source);
    while (false !== $entry = $dir->read()) {
        // Skip pointers
        if ($entry == '.' || $entry == '..') {
            continue;
        }

        // Deep copy directories
        recurse_copy("$source/$entry", "$dest/$entry");
    }

    // Clean up
    $dir->close();
    return true;
}
Jens A. Koch
  • 39,862
  • 13
  • 113
  • 141
exiang
  • 559
  • 2
  • 14
2

Why not just ask the OS to take care of this?

system("cp -r olddir newdir");

Done.

Paulo Freitas
  • 13,194
  • 14
  • 74
  • 96
William
  • 69
  • 1
1

OzzyCheck's is elegant and original, but he forgot the initial mkdir($dest); See below. No copy command is ever provided with contents only. It must fulfill its entire role.

$source = "dir/dir/dir";
$dest= "dest/dir";

mkdir($dest, 0755);
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());
  }
}
tropicalm
  • 51
  • 1
  • 3
1

hmm. as that's complicated ))

function mkdir_recursive( $dir ){
  $prev = dirname($dir);
  if( ! file_exists($prev))
  {
    mkdir_recursive($prev);
  }
  if( ! file_exists($dir))
  {
     mkdir($dir);
  }
}

...
$srcDir = "/tmp/from"
$dstDir = "/tmp/to"
mkdir_recursive($dstDir);
foreach( $files as $file){
  copy( $srcDir."/".$file, $dstDir."/".$file);
}

$file - somthing like that file.txt

1

Here's a simple recursive function to copy entire directories

source: http://php.net/manual/de/function.copy.php

<?php 
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); 
} 
?>
dazzafact
  • 2,570
  • 3
  • 30
  • 49
1
<?php

/**
 * code by Nk (nk.have.a@gmail.com)
 */

class filesystem
{
    public static function normalizePath($path)
    {
        return $path.(is_dir($path) && !preg_match('@/$@', $path) ? '/' : '');      
    }

    public static function rscandir($dir, $sort = SCANDIR_SORT_ASCENDING)
    {
        $results = array();

        if(!is_dir($dir))
        return $results;

        $dir = self::normalizePath($dir);

        $objects = scandir($dir, $sort);

        foreach($objects as $object)
        if($object != '.' && $object != '..')
        {
            if(is_dir($dir.$object))
            $results = array_merge($results, self::rscandir($dir.$object, $sort));
            else
            array_push($results, $dir.$object);
        }

        array_push($results, $dir);

        return $results;
    }

    public static function rcopy($source, $dest, $destmode = null)
    {
        $files = self::rscandir($source);

        if(empty($files))
        return;

        if(!file_exists($dest))
        mkdir($dest, is_int($destmode) ? $destmode : fileperms($source), true);

        $source = self::normalizePath(realpath($source));
        $dest = self::normalizePath(realpath($dest));

        foreach($files as $file)
        {
            $file_dest = str_replace($source, $dest, $file);

            if(is_dir($file))
            {
                if(!file_exists($file_dest))
                mkdir($file_dest, is_int($destmode) ? $destmode : fileperms($file), true);
            }
            else
            copy($file, $file_dest);
        }
    }
}

?>

/var/www/websiteA/backup.php :

<?php /* include.. */ filesystem::rcopy('/var/www/websiteA/', '../websiteB'); ?>
Nkc
  • 11
  • 2
0

There were some issues with the functions that I tested in the thread and here is a powerful function that covers everything. Highlights:

  1. No need to have an initial or intermediate source directories. All of the directories up to the source directory and to the copied directories will be handled.

  2. Ability to skip directory or files from an array. (optional) With the global $skip; skipping of files even under sub level directories are handled.

  3. Full recursive support, all the files and directories in multiple depth are supported.

$from = "/path/to/source_dir";
$to = "/path/to/destination_dir";
$skip = array('some_file.php', 'somedir');

copy_r($from, $to, $skip);

function copy_r($from, $to, $skip=false) {
    global $skip;
    $dir = opendir($from);
    if (!file_exists($to)) {mkdir ($to, 0775, true);}
    while (false !== ($file = readdir($dir))) {
        if ($file == '.' OR $file == '..' OR in_array($file, $skip)) {continue;}
        
        if (is_dir($from . DIRECTORY_SEPARATOR . $file)) {
            copy_r($from . DIRECTORY_SEPARATOR . $file, $to . DIRECTORY_SEPARATOR . $file);
        }
        else {
            copy($from . DIRECTORY_SEPARATOR . $file, $to . DIRECTORY_SEPARATOR . $file);
        }
    }
    closedir($dir);
}
Community
  • 1
  • 1
Tarik
  • 4,270
  • 38
  • 35
0

I guess you should check user(group)rights. You should consider chmod for example, depending on how you run (su?)PHP. You could possibly also choose to modify your php configuration.

Robert de W
  • 316
  • 8
  • 24
0

Recursively copies files from source to target, with choice of overwriting existing files on the target.

function copyRecursive($sourcePath, $targetPath, $overwriteExisting) {
$dir = opendir($sourcePath);
while (($file = readdir($dir)) !== false) {
    if ($file == "." || $file == "..") continue; // ignore these.

    $source = $sourcePath . "/" . $file;
    $target = $targetPath. "/" . $file;

    if (is_dir($source) == true) {
        // create the target directory, if it does not exist.
        if (file_exists($target) == false) @mkdir($target);
        copyRecursive($source, $target, $overwriteExisting);
    } else {
        if ((file_exists($target) == false) || ($overwriteExisting == true)) {
            copy($source, $target);
        }
    }
}
closedir($dir);

}

Manish
  • 87
  • 7