Recursive Copy of Directory
Asked Answered
P

16

28

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?

Potvaliant answered 18/4, 2011 at 19:25 Comment(5)
Your code would be more readable if you indented every block.Fulgurant
May be the PHP version, try this recursive copy function from php manual.Ridgeway
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 workPotvaliant
@Jasom may be you don't have enough permission in destination directory to copy to it.Ridgeway
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 :)Insurer
B
87

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

Barb answered 15/10, 2011 at 5:22 Comment(4)
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 ());Tavarez
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 thatOblate
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 (...Continent
Does RecursiveIteratorIterator implements a __call() method that forwards getSubPathName() to the instance of the RecursiveDirectoryIterator or how does that work?Gynecic
J
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.

Jampack answered 18/4, 2011 at 19:27 Comment(0)
B
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)

Beowulf answered 14/8, 2012 at 7:49 Comment(0)
M
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); 
}
Manifestative answered 25/4, 2013 at 8:21 Comment(1)
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 methodStirk
F
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.

Fiche answered 4/12, 2014 at 9:35 Comment(1)
The Symfony Filesystem component has a mirror method : $filesystem->mirror('/path/to/source', '/path/to/target');Idioblast
D
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);
    }
}
Denton answered 13/1, 2012 at 13:36 Comment(2)
It needs to add if (!file_exists($dest."/".$sourcefolder)) check before mkdir($dest."/".$sourcefolder); in cases when folders are existsSwane
almost worked for me. but replaced like this. 「copy($source."/".$file, $dest."/".$file);」 ⇒ 「 copy($source."/".$file, $dest."/".$sourcefolder."/".$file);」Crosby
J
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;
}
Johnson answered 14/10, 2013 at 8:38 Comment(0)
M
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());
  }
}
Monogamist answered 20/11, 2013 at 9:38 Comment(1)
You should check if the dir exists before creating it.Hierarch
R
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

Romero answered 5/5, 2015 at 4:8 Comment(0)
D
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); 
} 
?>
Disembody answered 13/11, 2015 at 15:29 Comment(0)
K
1

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

system("cp -r olddir newdir");

Done.

Ketchup answered 1/12, 2015 at 17:28 Comment(2)
Not just OS dependent, but "system" call is usually disabled in production.Correll
Not a good idea,never use shell in php unless you have to.Meander
P
1
<?php

/**
 * code by Nk ([email protected])
 */

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'); ?>
Potheen answered 22/2, 2018 at 19:19 Comment(0)
S
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.

Spano answered 18/4, 2011 at 19:29 Comment(0)
T
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);
}
Temperamental answered 27/11, 2017 at 19:12 Comment(0)
T
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);

}

Torrential answered 22/7, 2022 at 20:39 Comment(0)
F
-1
function copyDirectory( $source, $destination ) {
    foreach (
        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS),
            \RecursiveIteratorIterator::SELF_FIRST) as $item
    ) {
        $dir_destination = str_replace( '//', '/', $destination . DIRECTORY_SEPARATOR . $iterator->getSubPath() );
        if(!file_exists($dir_destination)) mkdir( $dir_destination, 0777, true );
    }
    foreach (
        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator( $source, FilesystemIterator::SKIP_DOTS ),
            \RecursiveIteratorIterator::CHILD_FIRST ) as $item
    ) {
        if($item->isFile()) {
            $file_destination = str_replace('//', '/', $destination . DIRECTORY_SEPARATOR . str_replace( $source, '', $item ) );
            copy( $item,  $file_destination );
        }
    }
    return true;
}
Filmdom answered 1/7 at 10:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.