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 cann开发者_如何学Pythonot 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>';
}
}
}
}
}
精彩评论