Recursive tree folder function php not going into subfolders - php

I am currently working on a php file indexer and I need to create a recursive function to create an array that will contains the files and subfolders list of the parent folder, with the subfolders also being arrays containing their files and their subfolders (etc...). As it is a school project, I cannot use DirectoryRecursiveIterator and its siblings RecursiveIterator and DirectoryIterator.
My issue is that it scans the parent folder and finds the subfolders and files but does not go in subfolders to find files and subfolders.
The code
<?php
class H5AI
{
// Properties
private $_tree;
private $_path;
// Construct
public function __construct($_path)
{
$_tree = [];
$parent = $_tree;
print_r($this->getFiles($_path, $parent));
}
// Methods
public function getPath()
{
return $this->_path;
}
public function getTree()
{
return $this->_tree;
}
public function getFiles($path, $parent)
{
//Opening the directory
$dirHandle = opendir($path);
while (false !== $entry = readdir($dirHandle)) {
//If file found
if (!is_dir($path . DIRECTORY_SEPARATOR . $entry)) {
array_push($parent, $entry);
}
// When subdirs found (ignore . & ..)
else if (is_dir($path . DIRECTORY_SEPARATOR . $entry) && $entry !== "." && $entry !== "..") {
$newPath = $path . DIRECTORY_SEPARATOR . $entry;
$parent[$entry] = [];
$this->getFiles($newPath, $parent[$entry]);
}
}
return $parent;
}
}
// Calling function
$h5a1 = new H5AI($argv[1]);
// Command I use in the terminal
php index.php "./test_dir"
//Output
Array
(
[sub_test_dir] => Array
(
)
[0] => test.css
[sub_test_dir2] => Array
(
)
[1] => test.js
[2] => test.html
)

class H5AI {
public function __construct(string $path) {
print_r($this->getFiles($path));
}
public function getFiles(string $directory): array {
$handle = opendir($directory);
$entries = [];
while (true) {
$entry = readdir($handle);
if ($entry === false) {
break;
}
$path = $directory . DIRECTORY_SEPARATOR . $entry;
if (is_file($path) && !str_starts_with($entry, '.')) {
$entries[] = $entry;
} elseif (is_dir($path) && !in_array($entry, [ '.', '..', '$RECYCLE.BIN' /* add other dir names to exclude here */ ])) {
$entries[$entry] = $this->getFiles($path);
}
}
closedir($handle);
return $entries;
}
}

You are creating a separate array, which contains what you want, but is not inserted into your parent array. You have everything right, you just need one little fix:
//...
else if (is_dir($path . DIRECTORY_SEPARATOR . $entry) && $entry !== "." && $entry !== "..") {
$newPath = $path . DIRECTORY_SEPARATOR . $entry;
$parent[$entry] = [];
$parent[$entry] = $this->getFiles($newPath, $parent[$entry]); // <-- fix is on this line
}

Related

get full directory in zf2

hi all I have made a function
function getDirContents($dir, &$results = array()){
$files = scandir($dir);
foreach($files as $key => $value){
$path = realpath($dir.DIRECTORY_SEPARATOR.$value);
if(!is_dir($path)) {
$results[] = $path;
} else if(is_dir($path) && $value != "." && $value != "..") {
getDirContents($path, $results);
$results[] = $path;
}
}
return $results;
}
and I am calling it in like
$value = $this->getDirContents("/var/www/staging/public/files/rgerger");
way from my controller by I am uable to get the full details of the folder I mean the directories and the subdirectories with all contents ..
scandir is on only giving the folder under the target folder which is only scaned but this is not giving me the content of the child folder upto the end.
You need to assign the result of the recursive method call back to the caller.
My (untested) example
function getDirectoryContents($dir)
{
$files = [];
if (is_dir($dir)) {
foreach(scandir($dir) as $key => $fileName) {
$filePath = realpath($dir . DIRECTORY_SEPARATOR . $fileName);
if ($fileName == '.' || $fileName == '..') {
continue;
}
if (is_dir($filePath)) {
$files[] = getDirectoryContents($filePath);
} else {
$files[] = $fileName;
}
}
}
return $files;
}
As per my comment, you can also use the SPL RecursiveDirectoryIterator which is PHP's inbuilt OOP solution for iterating the file system.

Recursivly get all files in a directory, and sub directories by extension

I was looking at RecursiveDirectoryIterator and glob to say
"return me a list of files (in an array) based on the extension (for example) .less. Oh and look in all child, grandchild and so on and so forth, excluding . and .. until you find all files matching."
But I am not sure the best approach to create a recursive function that keeps going well beyond the grand child.
What I have is a mess, its worked for two years - but now I need to refactor and change it up:
public function get_directory_of_files($path, $filename, $extension) {
if (!is_dir($path)) {
throw new AisisCore_FileHandling_FileException("Could not find said path: " . $path);
}
if (file_exists($filename)) {
$handler = opendir($path);
while ($file = readdir($handler)) {
if ($file != "." && $file != "..") {
$this->package_files [] = $file;
$count = count($this->package_files);
for ($i = 0; $i < $count; $i++) {
if (substr(strrchr($this->package_files [$i], '.'), 1) == $extension) {
if ($this->package_files [$i] == $filename) {
$this->files_got_back = $this->package_files [$i];
}
}
}
}
}
}
return $this->_files_got_back;
}
This requires a file name to be passed in and thats not really my thing to do any more. So how can I re-write this function to do the above "pseudo code"
This function recursively finds files with a matching ending string
function getDirectoryContents($directory, $extension)
{
$extension = strtolower($extension);
$files = array();
$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory));
while($it->valid())
{
if (!$it->isDot() && endsWith(strtolower($it->key()), $extension))
{
array_push($files, $it->key());
}
$it->next();
}
return $files;
}
function endsWith($haystack, $needle)
{
return $needle === "" || substr($haystack, -strlen($needle)) === $needle;
}
Used like so
print_r(getDirectoryContents('folder/', '.php'));
It converts the extension to lowercase to compare against
Take a look at this code:
<?php
class ex{
private function get_files_array($path,$ext, &$results){ //&to ensure it's a reference... but in php obj are passed by ref.
if (!is_dir($path)) {
//throw new AisisCore_FileHandling_FileException("Could not find said path: " . $path);
}
if ($dir = opendir($path)) {
$extLength = strlen($ext);
while (false !== ($file = readdir($dir))) {
if ($file != '.' && $file != '..'){
if (is_file($path.'/'.$file) && substr($file,-$extLength) == $ext){
$results[] = $path . '/' . $file; //it's a file and the correct extension
}
elseif (is_dir($path . '/'. $file)){
$this->get_files_array($path.'/'.$file, $ext, $results); //it's a dir
}
}
}
}else{
//unable to open dir
}
}
public function get_files_deep($path,$ext){
$results = array();
$this->get_files_array($path,$ext,$results);
return $results;
}
}
$ex = new ex();
var_dump($ex->get_files_deep('_some_path','.less'));
?>
It will retrieve all the files with the matching extension in the path and it's sub directories.
I hope it's what you need.

php function to read subdir content

I would like to ask what I have to add to make this function to show not only the files on top dir but also the files in subdirs..
private function _populateFileList()
{
$dir_handle = opendir($this->_files_dir);
if (! $dir_handle)
{
return false;
}
while (($file = readdir($dir_handle)) !== false)
{
if (in_array($file, $this->_hidden_files))
{
continue;
}
if (filetype($this->_files_dir . '/' . $file) == 'file')
{
$this->_file_list[] = $file;
}
}
closedir($dir_handle);
return true;
}
Thank you in advance!
You could implement the recursion yourself, or you could use the existing iterator classes to handle the recursion and filesystem traversal for you:
$dirIterator = new RecursiveDirectoryIterator('.', FilesystemIterator::SKIP_DOTS);
$recursiveIterator = new RecursiveIteratorIterator($dirIterator);
$filterIterator = new CallbackFilterIterator($recursiveIterator, function ($file) {
// adjust as needed
static $badFiles = ['foo', 'bar', 'baz'];
return !in_array($file, $badFiles);
});
$files = iterator_to_array($filterIterator);
var_dump($files);
By this you can get all subdir content
customerdel('FolderPath');
function customerdel($dirname=null){
if($dirname!=null){
if (is_dir($dirname))
$dir_handle = opendir($dirname);
if (!$dir_handle)
return false;
while($file = readdir($dir_handle)) {
if ($file != "." && $file != "..") {
if (!is_dir($dirname."/".$file))
echo $dirname."/".$file.'<br>';
else{
echo $dirname.'/'.$file.'<br> ';
customerdel($dirname.'/'.$file);
}
}
}
closedir($dir_handle);
}
}
Here is how you can get a recursive array of all files in a directory and its subdirectories.
The returned array is like: array( [fileName] => [filePath] )
EDIT: I've included a small check if there are filenames in the subdirectories with the same name. If so, an underscore and counter is added to the key-name in the returned array:
array( [fileName]_[COUNTER] => [filePath] )
private function getFileList($directory) {
$fileList = array();
$handle = opendir($directory);
if ($handle) {
while ($entry = readdir($handle)) {
if ($entry !== '.' and $entry !== '..') {
if (is_dir($directory . $entry)) {
$fileList = array_merge($fileList, $this->getFileList($directory . $entry . '/'));
} else {
$i = 0;
$_entry = $entry;
// Check if filename is allready in use
while (array_key_exists($_entry, $fileList)) {
$i++;
$_entry = $entry . "_$i";
}
$fileList[$_entry] = $directory . $entry;
}
}
}
closedir($handle);
}
return $fileList;
}

PHP Get all subdirectories of a given directory

How can I get all sub-directories of a given directory without files, .(current directory) or ..(parent directory)
and then use each directory in a function?
Option 1:
You can use glob() with the GLOB_ONLYDIR option.
Option 2:
Another option is to use array_filter to filter the list of directories. However, note that the code below will skip valid directories with periods in their name like .config.
$dirs = array_filter(glob('*'), 'is_dir');
print_r($dirs);
Here is how you can retrieve only directories with GLOB:
$directories = glob($somePath . '/*' , GLOB_ONLYDIR);
The Spl DirectoryIterator class provides a simple interface for viewing the contents of filesystem directories.
$dir = new DirectoryIterator($path);
foreach ($dir as $fileinfo) {
if ($fileinfo->isDir() && !$fileinfo->isDot()) {
echo $fileinfo->getFilename().'<br>';
}
}
Almost the same as in your previous question:
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($yourStartingPath),
RecursiveIteratorIterator::SELF_FIRST);
foreach($iterator as $file) {
if($file->isDir()) {
echo strtoupper($file->getRealpath()), PHP_EOL;
}
}
Replace strtoupper with your desired function.
Try this code:
<?php
$path = '/var/www/html/project/somefolder';
$dirs = array();
// directory handle
$dir = dir($path);
while (false !== ($entry = $dir->read())) {
if ($entry != '.' && $entry != '..') {
if (is_dir($path . '/' .$entry)) {
$dirs[] = $entry;
}
}
}
echo "<pre>"; print_r($dirs); exit;
In Array:
function expandDirectoriesMatrix($base_dir, $level = 0) {
$directories = array();
foreach(scandir($base_dir) as $file) {
if($file == '.' || $file == '..') continue;
$dir = $base_dir.DIRECTORY_SEPARATOR.$file;
if(is_dir($dir)) {
$directories[]= array(
'level' => $level
'name' => $file,
'path' => $dir,
'children' => expandDirectoriesMatrix($dir, $level +1)
);
}
}
return $directories;
}
//access:
$dir = '/var/www/';
$directories = expandDirectoriesMatrix($dir);
echo $directories[0]['level'] // 0
echo $directories[0]['name'] // pathA
echo $directories[0]['path'] // /var/www/pathA
echo $directories[0]['children'][0]['name'] // subPathA1
echo $directories[0]['children'][0]['level'] // 1
echo $directories[0]['children'][1]['name'] // subPathA2
echo $directories[0]['children'][1]['level'] // 1
Example to show all:
function showDirectories($list, $parent = array())
{
foreach ($list as $directory){
$parent_name = count($parent) ? " parent: ({$parent['name']}" : '';
$prefix = str_repeat('-', $directory['level']);
echo "$prefix {$directory['name']} $parent_name <br/>"; // <-----------
if(count($directory['children'])){
// list the children directories
showDirectories($directory['children'], $directory);
}
}
}
showDirectories($directories);
// pathA
// - subPathA1 (parent: pathA)
// -- subsubPathA11 (parent: subPathA1)
// - subPathA2
// pathB
// pathC
You can try this function (PHP 7 required)
function getDirectories(string $path) : array
{
$directories = [];
$items = scandir($path);
foreach ($items as $item) {
if($item == '..' || $item == '.')
continue;
if(is_dir($path.'/'.$item))
$directories[] = $item;
}
return $directories;
}
Non-recursively List Only Directories
The only question that direct asked this has been erroneously closed, so I have to put it here.
It also gives the ability to filter directories.
/**
* Copyright © 2020 Theodore R. Smith <https://www.phpexperts.pro/>
* License: MIT
*
* #see https://stackoverflow.com/a/61168906/430062
*
* #param string $path
* #param bool $recursive Default: false
* #param array $filtered Default: [., ..]
* #return array
*/
function getDirs($path, $recursive = false, array $filtered = [])
{
if (!is_dir($path)) {
throw new RuntimeException("$path does not exist.");
}
$filtered += ['.', '..'];
$dirs = [];
$d = dir($path);
while (($entry = $d->read()) !== false) {
if (is_dir("$path/$entry") && !in_array($entry, $filtered)) {
$dirs[] = $entry;
if ($recursive) {
$newDirs = getDirs("$path/$entry");
foreach ($newDirs as $newDir) {
$dirs[] = "$entry/$newDir";
}
}
}
}
return $dirs;
}
<?php
/*this will do what you asked for, it only returns the subdirectory names in a given
path, and you can make hyperlinks and use them:
*/
$yourStartingPath = "photos\\";
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($yourStartingPath),
RecursiveIteratorIterator::SELF_FIRST);
foreach($iterator as $file) {
if($file->isDir()) {
$path = strtoupper($file->getRealpath()) ;
$path2 = PHP_EOL;
$path3 = $path.$path2;
$result = end(explode('/', $path3));
echo "<br />". basename($result );
}
}
/* best regards,
Sanaan Barzinji
Erbil
*/
?>
This is the one liner code:
$sub_directories = array_map('basename', glob($directory_path . '/*', GLOB_ONLYDIR));
Proper way
/**
* Get all of the directories within a given directory.
*
* #param string $directory
* #return array
*/
function directories($directory)
{
$glob = glob($directory . '/*');
if($glob === false)
{
return array();
}
return array_filter($glob, function($dir) {
return is_dir($dir);
});
}
Inspired by Laravel
The following recursive function returns an array with the full list of sub directories
function getSubDirectories($dir)
{
$subDir = array();
$directories = array_filter(glob($dir), 'is_dir');
$subDir = array_merge($subDir, $directories);
foreach ($directories as $directory) $subDir = array_merge($subDir, getSubDirectories($directory.'/*'));
return $subDir;
}
Source: https://www.lucidar.me/en/web-dev/how-to-get-subdirectories-in-php/
You can use the glob() function to do this.
Here is some documentation on it:
http://php.net/manual/en/function.glob.php
Find all PHP files recursively. The logic should be simple enough to tweak and it aims to be fast(er) by avoiding function calls.
function get_all_php_files($directory) {
$directory_stack = array($directory);
$ignored_filename = array(
'.git' => true,
'.svn' => true,
'.hg' => true,
'index.php' => true,
);
$file_list = array();
while ($directory_stack) {
$current_directory = array_shift($directory_stack);
$files = scandir($current_directory);
foreach ($files as $filename) {
// Skip all files/directories with:
// - A starting '.'
// - A starting '_'
// - Ignore 'index.php' files
$pathname = $current_directory . DIRECTORY_SEPARATOR . $filename;
if (isset($filename[0]) && (
$filename[0] === '.' ||
$filename[0] === '_' ||
isset($ignored_filename[$filename])
))
{
continue;
}
else if (is_dir($pathname) === TRUE) {
$directory_stack[] = $pathname;
} else if (pathinfo($pathname, PATHINFO_EXTENSION) === 'php') {
$file_list[] = $pathname;
}
}
}
return $file_list;
}
If you're looking for a recursive directory listing solutions. Use below code I hope it should help you.
<?php
/**
* Function for recursive directory file list search as an array.
*
* #param mixed $dir Main Directory Path.
*
* #return array
*/
function listFolderFiles($dir)
{
$fileInfo = scandir($dir);
$allFileLists = [];
foreach ($fileInfo as $folder) {
if ($folder !== '.' && $folder !== '..') {
if (is_dir($dir . DIRECTORY_SEPARATOR . $folder) === true) {
$allFileLists[$folder . '/'] = listFolderFiles($dir . DIRECTORY_SEPARATOR . $folder);
} else {
$allFileLists[$folder] = $folder;
}
}
}
return $allFileLists;
}//end listFolderFiles()
$dir = listFolderFiles('your searching directory path ex:-F:\xampp\htdocs\abc');
echo '<pre>';
print_r($dir);
echo '</pre>'
?>
Find all subfolders under a specified directory.
<?php
function scanDirAndSubdir($dir, &$fullDir = array()){
$currentDir = scandir($dir);
foreach ($currentDir as $key => $filename) {
$realpath = realpath($dir . DIRECTORY_SEPARATOR . $filename);
if (!is_dir($realpath) && $filename != "." && $filename != "..") {
scanDirAndSubdir($realpath, $fullDir);
} else {
$fullDir[] = $realpath;
}
}
return $fullDir;
}
var_dump(scanDirAndSubdir('C:/web2.0/'));
Sample :
array (size=4)
0 => string 'C:/web2.0/config/' (length=17)
1 => string 'C:/web2.0/js/' (length=13)
2 => string 'C:/web2.0/mydir/' (length=16)
3 => string 'C:/web2.0/myfile/' (length=17)

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

Categories