Navigation

Php

How to Delete Files and Directories Safely

Safely remove files and directories in PHP with proper validation, error handling, and protection against accidental data loss.

Table Of Contents

Safe Deletion Strategy

Here's how to delete files and directories with multiple safety checks:

<?php

// Safe file deletion with validation
function deleteFileSafely(string $filepath): bool {
    // Validate file exists
    if (!file_exists($filepath)) {
        return true; // Already deleted
    }
    
    // Ensure it's actually a file, not a directory
    if (!is_file($filepath)) {
        throw new InvalidArgumentException("Path is not a file: $filepath");
    }
    
    // Check if file is writable (can be deleted)
    if (!is_writable($filepath)) {
        throw new RuntimeException("File is not writable/deletable: $filepath");
    }
    
    // Perform deletion
    if (!unlink($filepath)) {
        throw new RuntimeException("Failed to delete file: $filepath");
    }
    
    return true;
}

// Delete file with backup option
function deleteWithBackup(string $filepath, string $backupDir = 'trash'): bool {
    if (!file_exists($filepath)) {
        return true;
    }
    
    // Create backup directory if needed
    if (!is_dir($backupDir)) {
        mkdir($backupDir, 0755, true);
    }
    
    // Create backup filename with timestamp
    $filename = basename($filepath);
    $timestamp = date('Y-m-d_H-i-s');
    $backupPath = $backupDir . DIRECTORY_SEPARATOR . $timestamp . '_' . $filename;
    
    // Move to backup instead of permanent deletion
    if (!rename($filepath, $backupPath)) {
        throw new RuntimeException("Failed to move file to backup: $filepath");
    }
    
    return true;
}

// Safe directory deletion (recursive)
function deleteDirectorySafely(string $dirPath): bool {
    if (!is_dir($dirPath)) {
        return true; // Already deleted or doesn't exist
    }
    
    // Prevent deletion of critical system directories
    $protectedPaths = ['/', '/usr', '/var', '/etc', '/home', '/root'];
    $realPath = realpath($dirPath);
    
    foreach ($protectedPaths as $protected) {
        if (strpos($realPath, $protected) === 0 && strlen($realPath) <= strlen($protected) + 10) {
            throw new RuntimeException("Attempted to delete protected directory: $dirPath");
        }
    }
    
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dirPath, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::CHILD_FIRST
    );
    
    // Delete all files and subdirectories
    foreach ($iterator as $file) {
        if ($file->isDir()) {
            if (!rmdir($file->getRealPath())) {
                throw new RuntimeException("Cannot delete directory: " . $file->getRealPath());
            }
        } else {
            if (!unlink($file->getRealPath())) {
                throw new RuntimeException("Cannot delete file: " . $file->getRealPath());
            }
        }
    }
    
    // Delete the main directory
    if (!rmdir($dirPath)) {
        throw new RuntimeException("Cannot delete main directory: $dirPath");
    }
    
    return true;
}

// Delete files older than specified days
function deleteOldFiles(string $directory, int $daysOld, string $pattern = '*'): array {
    $deletedFiles = [];
    $cutoffTime = time() - ($daysOld * 24 * 60 * 60);
    
    $files = glob($directory . DIRECTORY_SEPARATOR . $pattern);
    
    foreach ($files as $file) {
        if (is_file($file) && filemtime($file) < $cutoffTime) {
            try {
                deleteFileSafely($file);
                $deletedFiles[] = basename($file);
            } catch (Exception $e) {
                error_log("Failed to delete old file $file: " . $e->getMessage());
            }
        }
    }
    
    return $deletedFiles;
}

// Clean up temporary files
function cleanupTempFiles(array $tempDirs = ['tmp', 'temp', 'cache/temp']): array {
    $cleaned = [];
    
    foreach ($tempDirs as $dir) {
        if (!is_dir($dir)) {
            continue;
        }
        
        $files = glob($dir . DIRECTORY_SEPARATOR . '*');
        
        foreach ($files as $file) {
            if (is_file($file)) {
                try {
                    deleteFileSafely($file);
                    $cleaned[] = $file;
                } catch (Exception $e) {
                    error_log("Failed to clean temp file $file: " . $e->getMessage());
                }
            }
        }
    }
    
    return $cleaned;
}

// Delete files by extension
function deleteFilesByExtension(string $directory, array $extensions): array {
    $deletedFiles = [];
    
    foreach ($extensions as $ext) {
        $pattern = $directory . DIRECTORY_SEPARATOR . '*.' . ltrim($ext, '.');
        $files = glob($pattern);
        
        foreach ($files as $file) {
            if (is_file($file)) {
                try {
                    deleteFileSafely($file);
                    $deletedFiles[] = basename($file);
                } catch (Exception $e) {
                    error_log("Failed to delete file $file: " . $e->getMessage());
                }
            }
        }
    }
    
    return $deletedFiles;
}

// Secure deletion with multiple passes (basic)
function secureDelete(string $filepath): bool {
    if (!file_exists($filepath) || !is_file($filepath)) {
        return true;
    }
    
    $filesize = filesize($filepath);
    $handle = fopen($filepath, 'r+b');
    
    if (!$handle) {
        throw new RuntimeException("Cannot open file for secure deletion: $filepath");
    }
    
    // Overwrite with random data (3 passes)
    for ($pass = 0; $pass < 3; $pass++) {
        fseek($handle, 0);
        
        for ($i = 0; $i < $filesize; $i += 1024) {
            $chunk = str_repeat(chr(random_int(0, 255)), min(1024, $filesize - $i));
            fwrite($handle, $chunk);
        }
        
        fflush($handle);
    }
    
    fclose($handle);
    
    // Final deletion
    return deleteFileSafely($filepath);
}

// Batch delete with confirmation
function batchDelete(array $files, bool $requireConfirmation = true): array {
    $results = ['deleted' => [], 'failed' => [], 'skipped' => []];
    
    if ($requireConfirmation && !empty($files)) {
        echo "About to delete " . count($files) . " files:\n";
        foreach ($files as $file) {
            echo "  - $file\n";
        }
        
        echo "Continue? (y/N): ";
        $confirmation = trim(fgets(STDIN));
        
        if (strtolower($confirmation) !== 'y') {
            $results['skipped'] = $files;
            return $results;
        }
    }
    
    foreach ($files as $file) {
        try {
            deleteFileSafely($file);
            $results['deleted'][] = $file;
        } catch (Exception $e) {
            $results['failed'][] = ['file' => $file, 'error' => $e->getMessage()];
        }
    }
    
    return $results;
}

// Usage examples
try {
    // Delete single file safely
    deleteFileSafely('unwanted.txt');
    
    // Delete with backup
    deleteWithBackup('important.txt', 'backup_folder');
    
    // Delete entire directory
    deleteDirectorySafely('old_project');
    
    // Clean up old files (older than 30 days)
    $deletedOld = deleteOldFiles('uploads', 30);
    echo "Deleted " . count($deletedOld) . " old files\n";
    
    // Clean temporary files
    $cleanedTemp = cleanupTempFiles();
    echo "Cleaned " . count($cleanedTemp) . " temporary files\n";
    
    // Delete specific file types
    $deletedLogs = deleteFilesByExtension('logs', ['log', 'tmp']);
    echo "Deleted log files: " . implode(', ', $deletedLogs) . "\n";
    
    // Secure delete sensitive file
    secureDelete('sensitive_data.txt');
    
    // Batch delete with confirmation
    $filesToDelete = ['file1.txt', 'file2.txt', 'file3.txt'];
    $batchResults = batchDelete($filesToDelete, false);
    print_r($batchResults);
    
} catch (Exception $e) {
    echo "Deletion error: " . $e->getMessage() . "\n";
}

Safety Measures

Safe file deletion requires multiple protection layers:

  1. Validation: Check file exists and is actually a file/directory
  2. Permissions: Verify write permissions before attempting deletion
  3. Protection: Prevent deletion of system-critical directories
  4. Backup Options: Move to trash/backup instead of permanent deletion

Critical Safety Checks:

  • File/directory existence validation
  • Permission verification
  • Protected path prevention
  • Error handling and logging
  • Optional backup before deletion

Best Practices:

  • Always validate before deletion
  • Use backup/trash system for important files
  • Log deletion operations
  • Implement confirmation for batch operations
  • Consider secure deletion for sensitive data

Essential for file cleanup, temporary file management, and safe data removal operations.

Share this article

Add Comment

No comments yet. Be the first to comment!

More from Php