Log in

View Full Version : Resolved Help with rename() function



Frankie House
03-20-2009, 05:59 AM
Hi,

I was wondering if someone can help me with this, I'm trying to make a website duplication application, where i store a website on my server and when someone fills in a form, it will copy the website and move it to a different folder on the server. I used the rename () function like this.


<?php
$source = "original";
$destination = "new";

if (rename($source, $destination)) {
echo "Your Website Was Created.", "\n";
} else {
echo "Sorry, Your Website Was Not Created. Please try again.", "\n";
}
?>


and that works fine except i only end up with the folder "new", is there anyway to keep the original folder as well?

Thanks In advance:)

JasonDFR
03-20-2009, 12:28 PM
Unfortunately I don't think it is as simple as calling rename on a whole directory. You need to copy each file one at a time and create new directories as needed.

I had some permission problems while working this function out on my local machine. You probably won't have any on your webhost's server, but locally you'll need to make sure the orig directory and the directory that is going to receive the copy has read/write access.

I think there is some room for improvement in this function and I hope Daniel or Twey will comment on it. The elseif before the recursive call doesn't seem right to me for some reason, but I am not sure.

Daniel, Twey, Nile, TechieTim, please comment. Making this work is a learning experience for me too.


function copy_dir($dir, $copyto) {

rtrim($dir, '/');
rtrim($copyto, '/');

// If original doesn't exist, or destination does exist
// trigger errors
if ( !is_dir($dir) || is_dir($copyto) ) {
trigger_error("Original doesn't exist or the directory you wish to create does exist",
E_USER_ERROR);
exit;
}

if ( !mkdir($copyto) ) {
trigger_error('Cannot create new directory: ' . $copyto, E_USER_ERROR);
exit;
}

$contents = scandir($dir);

for ( $x = 0; $x < count($contents); $x++) {

if ( $contents[$x] != '.' && $contents[$x] != '..' ) {

if ( is_file("$dir/$contents[$x]") ) {

copy("$dir/$contents[$x]", "$copyto/$contents[$x]");

// Something about testing for the $copyto directory here doesn't seem right
// But if you don't, you'll create an endless loop and continue to copy files
// FOREVER!
} elseif ( is_dir("$dir/$contents[$x]") && "$dir/$contents[$x]" != $copyto ) {

// Recursive call here
copy_dir("$dir/$contents[$x]", "$copyto/$contents[$x]");

}

}

}

}

copy_dir('/path_to_orig', '/path_to_new');

exit;

techietim
03-20-2009, 01:03 PM
Daniel, Twey, Nile, TechieTim, please comment.

The only thing I would change is I'd replace the for statement with a foreach statement. I'd then, before the foreach, unset $contents array item's 0 and 1, allowing me to remove the if statement that checks to see if it's a . or ..

I also didn't see the need for the endless loop protection, so I removed it.



function copy_dir($dir, $copyto) {
rtrim($dir, '/');
rtrim($copyto, '/');
if ( !is_dir($dir) || is_dir($copyto) ) {
trigger_error("Original doesn't exist or the directory you wish to create does exist",
E_USER_ERROR);
exit;
}
if ( !mkdir($copyto) ) {
trigger_error('Cannot create new directory: ' . $copyto, E_USER_ERROR);
exit;
}
$contents = scandir($dir);
unset($contents[0], $contents[1]);
foreach ($contents as $item) {
if ( is_file("$dir/$item") ) {
copy("$dir/$item", "$copyto/$item");
} elseif ( is_dir("$dir/$item") ) {
copy_dir("$dir/$item", "$copyto/$item");
}
}
}

JasonDFR
03-20-2009, 01:24 PM
Cool. I like the unset the first two indexes. They will always be the first two?

And it actually did start out as a foreach, but I changed it for some reason before I was finished when I started having trouble with the endless loop.

Your version works great. I am going to have to look at mine closer and figure out exactly where I was running into trouble. You see anything?

Thanks.

Frankie House
03-20-2009, 01:35 PM
Thanks alot guys that seemed to work great, I get an error

Fatal error: Original doesn't exist or the directory you wish to create does exist in /home/gudic/public_html/copy.php on line 7

but it seems to of copied the folder fine, thanks for your help

JasonDFR
03-20-2009, 01:38 PM
I realized why I was having trouble. Although the endless loop does not occur, you do end up with an extra folder when you copy into a folder inside the same directory you are copying from. I was initially doing this so I only had to change permissions on one folder.

Try copy_dir('/home/user/directory', '/home/user/directory/copy'); (Tim's version) See if you experience the creation of an extra "copyto" folder.

Also, will this function work as is on a non *nix machine? Thinking about the directory separators...

techietim
03-20-2009, 01:51 PM
I made mine on up on Windows and it worked fine.

As far as the . and .. being the first array item, they usually are, but it sorts the files by what their ASCII value is, so a file named - would be before the periods.

Also, if you're copying a folder like /, it wouldn't have a parent directory, so .. wouldn't come up.

A quick fix would be just to add back in the checking for . and .., remove the unset, but still keep the foreach loop.

JasonDFR
03-20-2009, 01:59 PM
What about using your version to create a copy of the folder inside the same directory you are copying from?

Give this a shot with your version Tim and tell me what you experience:

copy_dir('/home/user/directory', '/home/user/directory/copy');

I'm going to work on this again later. I want to try using opendir() .

Frankie House
03-20-2009, 02:04 PM
I was actually just calling the function like this

copy_dir('original', 'copy');

exit;

as the folders are being created on the same level, would you say to do it with the full path like that
('/home/user/directory/original', '/home/user/directory/copy')?

techietim
03-20-2009, 02:34 PM
@jason

I'm not sure about *nix, but on Windows it gives an error if you try to copy a (1)folder to a (2)folder that is inside of the (1)folder.

EDIT:
Not doing this inside of PHP, but using Windows explorer.

Frankie House
03-21-2009, 07:15 AM
Thanks alot both of you got it working perfectly

JasonDFR
03-21-2009, 07:18 AM
Cool.

When you can, post the code you got working well.

JasonDFR
03-21-2009, 10:39 AM
I did this for practice. I really would appreciate some feedback. Thanks.


<?php
// DirectoryCopier.php

/**
* DirectoryCopier
* Makes a copy of an existing directory and it's contents.
*
* @author jasondfr
* @param string $dir Existing Directory
* @param string $copyto Directory to copy to
*
* @notes Bad things happen when copying a direcotry into itself ->
* It would be nice to allow this. strpos() is used in the setCopyto method
* to check for this. However, I'm not sure this is the best way.
*
*/
class DirectoryCopier extends DirectoryIterator {

protected $_dir;
protected $_copyto = null;
protected $_result = false;

public function __construct($dir, $copyto = null) {
$this->_dir = realpath($dir);
if (!is_null($copyto)) {
$this->setCopyTo($copyto);
}
parent::__construct($this->_dir);
}

public function setCopyTo($copyto) {
$this->_copyto = (string) rtrim($copyto, '/');
if (strpos($this->_copyto, $this->_dir) !== false) {
throw new Exception('Cannot copy into new directory within
directory being copied'
);
}
if ( is_dir($this->_copyto) ) {
$this->_copyto = realpath($this->_copyto);
}
}

public function getCopyTo() {
return $this->_copyto;
}

public function copy() {
if (is_null($this->_copyto)) {
throw new Exception('You must specify a directory to copy to.'
);
}
if (!is_dir($this->_copyto)) {
if (!mkdir($this->_copyto)) {
throw new Exception('Cannot create directory to copy into.
Check permissions and syntax.'
);
}
}
foreach ( $this as $file ) {
if (!$file->isdot()) {
if ($file->isFile()) {
$this->_result = copy($file->getPathName(),
$this->_copyto . '/' . $file->getFilename()
);
if ($this->_result !==true) {
break;
}
} elseif ($file->isDir()) {
$dir = new DirectoryCopier($file->getPathName(),
$this->_copyto . '/' . $file);
$dir->copy();
} else {
continue;
}
}
}
return $this->_result;
}
}

Usage:


<?php
require_once('DirectoryCopier.php');

$dir = '/home/user/dir';
$copyto = '/home/directory';

$dircopier = new DirectoryCopier($dir, $copyto);


try {
if ($dircopier->copy())
echo 'Successful';
else
echo 'Not Successful';
} catch (Exception $e) {
print_r($e);
}

exit;