Navigation

Php

How to Copy Files and Directories

Master file and directory copying in PHP using copy() for files and recursive functions for directories - essential for backups and file management.

Table Of Contents

The Implementation

You can solve file and directory copying needs by using PHP's built-in functions with recursive handling:

<?php

// Basic file copying
if (!copy('source.txt', 'destination.txt')) {
    throw new RuntimeException('Failed to copy file');
}

// Safe file copy with validation
function copyFileSafely(string $source, string $destination): bool {
    // Validate source file
    if (!file_exists($source)) {
        throw new InvalidArgumentException("Source file does not exist: $source");
    }
    
    if (!is_readable($source)) {
        throw new RuntimeException("Source file is not readable: $source");
    }
    
    // Ensure destination directory exists
    $destDir = dirname($destination);
    if (!is_dir($destDir)) {
        if (!mkdir($destDir, 0755, true)) {
            throw new RuntimeException("Cannot create destination directory: $destDir");
        }
    }
    
    // Check if destination is writable
    if (file_exists($destination) && !is_writable($destination)) {
        throw new RuntimeException("Destination file is not writable: $destination");
    }
    
    // Perform the copy
    if (!copy($source, $destination)) {
        throw new RuntimeException("Failed to copy file from $source to $destination");
    }
    
    return true;
}

// Copy with backup of existing file
function copyWithBackup(string $source, string $destination): bool {
    if (file_exists($destination)) {
        $backupName = $destination . '.backup.' . date('Y-m-d_H-i-s');
        if (!copy($destination, $backupName)) {
            throw new RuntimeException("Failed to create backup of existing file");
        }
    }
    
    return copyFileSafely($source, $destination);
}

// Recursive directory copying
function copyDirectory(string $source, string $destination): bool {
    if (!is_dir($source)) {
        throw new InvalidArgumentException("Source is not a directory: $source");
    }
    
    // Create destination directory
    if (!is_dir($destination)) {
        if (!mkdir($destination, 0755, true)) {
            throw new RuntimeException("Cannot create destination directory: $destination");
        }
    }
    
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::SELF_FIRST
    );
    
    foreach ($iterator as $file) {
        $destPath = $destination . DIRECTORY_SEPARATOR . $iterator->getSubPathName();
        
        if ($file->isDir()) {
            if (!mkdir($destPath, 0755, true)) {
                throw new RuntimeException("Cannot create directory: $destPath");
            }
        } else {
            if (!copy($file->getRealPath(), $destPath)) {
                throw new RuntimeException("Cannot copy file: {$file->getRealPath()} to $destPath");
            }
        }
    }
    
    return true;
}

// Copy files matching pattern
function copyFilesByPattern(string $sourceDir, string $pattern, string $destinationDir): array {
    $copiedFiles = [];
    
    if (!is_dir($destinationDir)) {
        mkdir($destinationDir, 0755, true);
    }
    
    $files = glob($sourceDir . DIRECTORY_SEPARATOR . $pattern);
    
    foreach ($files as $file) {
        if (is_file($file)) {
            $filename = basename($file);
            $destination = $destinationDir . DIRECTORY_SEPARATOR . $filename;
            
            if (copyFileSafely($file, $destination)) {
                $copiedFiles[] = $filename;
            }
        }
    }
    
    return $copiedFiles;
}

// Copy files newer than specified date
function copyNewerFiles(string $sourceDir, string $destinationDir, string $sinceDate): array {
    $copiedFiles = [];
    $sinceTimestamp = strtotime($sinceDate);
    
    if (!is_dir($destinationDir)) {
        mkdir($destinationDir, 0755, true);
    }
    
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($sourceDir, RecursiveDirectoryIterator::SKIP_DOTS)
    );
    
    foreach ($iterator as $file) {
        if ($file->isFile() && $file->getMTime() > $sinceTimestamp) {
            $relativePath = substr($file->getPathname(), strlen($sourceDir) + 1);
            $destPath = $destinationDir . DIRECTORY_SEPARATOR . $relativePath;
            
            // Create destination directory structure
            $destDir = dirname($destPath);
            if (!is_dir($destDir)) {
                mkdir($destDir, 0755, true);
            }
            
            if (copyFileSafely($file->getPathname(), $destPath)) {
                $copiedFiles[] = $relativePath;
            }
        }
    }
    
    return $copiedFiles;
}

// Create backup of directory
function createDirectoryBackup(string $sourceDir, string $backupRoot = 'backups'): string {
    $timestamp = date('Y-m-d_H-i-s');
    $dirName = basename($sourceDir);
    $backupPath = $backupRoot . DIRECTORY_SEPARATOR . $dirName . '_' . $timestamp;
    
    copyDirectory($sourceDir, $backupPath);
    
    return $backupPath;
}

// Copy with progress tracking
function copyWithProgress(string $source, string $destination, callable $progressCallback = null): bool {
    $sourceSize = filesize($source);
    $sourceHandle = fopen($source, 'rb');
    $destHandle = fopen($destination, 'wb');
    
    if (!$sourceHandle || !$destHandle) {
        throw new RuntimeException('Cannot open files for copying');
    }
    
    $copiedBytes = 0;
    $chunkSize = 1024 * 1024; // 1MB chunks
    
    while (!feof($sourceHandle)) {
        $chunk = fread($sourceHandle, $chunkSize);
        fwrite($destHandle, $chunk);
        
        $copiedBytes += strlen($chunk);
        
        if ($progressCallback) {
            $progressCallback($copiedBytes, $sourceSize);
        }
    }
    
    fclose($sourceHandle);
    fclose($destHandle);
    
    return true;
}

// Usage examples
try {
    // Simple file copy
    copyFileSafely('important.txt', 'backup/important.txt');
    
    // Copy with backup
    copyWithBackup('config.php', 'config.php.new');
    
    // Copy entire directory
    copyDirectory('project', 'project_backup');
    
    // Copy specific file types
    $copiedImages = copyFilesByPattern('uploads', '*.{jpg,png,gif}', 'backup/images');
    echo "Copied images: " . implode(', ', $copiedImages) . "\n";
    
    // Copy files modified in last 7 days
    $recentFiles = copyNewerFiles('documents', 'recent_backup', '-7 days');
    echo "Copied " . count($recentFiles) . " recent files\n";
    
    // Create timestamped backup
    $backupPath = createDirectoryBackup('important_data');
    echo "Backup created at: $backupPath\n";
    
    // Copy large file with progress
    copyWithProgress('large_file.zip', 'backup/large_file.zip', function($copied, $total) {
        $percent = round(($copied / $total) * 100, 2);
        echo "Progress: $percent%\r";
    });
    
} catch (Exception $e) {
    echo "Copy error: " . $e->getMessage() . "\n";
}

Key Points

PHP file copying provides several approaches for different needs:

  1. File Copying: Use copy() for simple file-to-file operations
  2. Directory Copying: Requires recursive iteration through all files
  3. Validation: Always check source exists and destination is writable
  4. Error Handling: Proper exception handling for robust operations

Essential Functions:

  • copy($source, $dest) - Basic file copying
  • RecursiveDirectoryIterator - Directory traversal
  • filesize(), filemtime() - File information
  • mkdir() - Create destination directories

Best Practices:

  • Validate source and destination paths
  • Create destination directories as needed
  • Handle large files with chunked copying
  • Provide progress feedback for long operations

Perfect for backups, file organization, and data migration tasks.

Share this article

Add Comment

No comments yet. Be the first to comment!

More from Php