Deleting folders using PHP
Table of Contents
1 Intro
PHP lets you delete folders using the built-in rmdir() function. In this guide, we'll walk through several ways to handle folder deletion, starting with the most basic approach, moving on to security checks for handling user input, and finishing with advanced techniques for deleting non-empty folders containing files or subdirectories.
2 Basic folder deletion
This is the most basic way to delete a folder in PHP:
<?php
rmdir('empty_folder');
This will delete the folder called empty_folder, but only if it exists and is empty. If the folder still contains anything, PHP will show a warning.
3 Add safety checks
To keep the code clean and easy to maintain, we can store the folder path in a variable like $folder. We can then pass that variable into safety checks before attempting to delete it.
<?php
$folder = 'empty_folder';
// Check if the folder exists and is a directory
if (is_dir($folder)) {
// Try to delete the folder
if (rmdir($folder)) {
echo 'Folder deleted.';
} else {
echo 'Failed to delete folder.';
}
} else {
// Folder does not exist or is not a directory
echo 'Folder does not exist or is not a directory.';
}
The is_dir() function checks that the target exists and confirms it is a folder rather than a regular file. Nesting rmdir() inside an if statement allows you to display custom feedback if the deletion succeeds or fails, preventing PHP from throwing an unhandled warning on your page.
4 Best practices
- Always check if a folder actually exists before trying to delete it.
- Be careful of directory traversal: attackers might try to use
../../in the path to break out of the intended folder and delete something important. - Use
realpath()to resolve the actual filesystem path and make sure it's inside a known safe folder (likeuploads/). - Always check that you're deleting a folder, not a regular file.
5 Advanced: Securing folder deletion against user input
Note: You only need this extra security if you delete folders based on untrusted user input (like a URL parameter delete.php?folder=example). Accepting folder names directly from users is risky because attackers can use ../../ tricks to escape and delete core project files.
Think of this code as setting up a fenced sandbox:
- Safe parent directory: We pick one allowed location, like an
uploads/folder. - Inside only: Users can delete subfolders inside that location, but never the main parent folder itself.
- No escaping:
realpath()resolves relative paths like../to make sure the target stays strictly inside the safe directory.
<?php
// Folder requested by the user
$folder = $_GET['folder'] ?? '';
// Define the safe base folder
$baseDir = __DIR__ . DIRECTORY_SEPARATOR . 'uploads';
$realBase = realpath($baseDir);
// Resolve the requested target path
$targetPath = $realBase ? realpath($realBase . DIRECTORY_SEPARATOR . $folder) : false;
// Security checks:
// 1. Target exists
// 2. Target isn't the base folder itself
// 3. Target is inside the base folder
if ($realBase && $targetPath && $targetPath !== $realBase && strpos($targetPath, $realBase . DIRECTORY_SEPARATOR) === 0) {
if (is_dir($targetPath)) {
if (rmdir($targetPath)) {
echo 'Folder successfully deleted.';
} else {
echo 'Folder is not empty or could not be deleted.';
}
} else {
echo 'Not a valid folder.';
}
} else {
echo 'Invalid or unauthorized folder path.';
}6 Bonus: Delete folders with content (recursively)
If a folder contains files or subfolders, rmdir() will fail. To delete a non-empty folder, you have to clear out everything inside it first. Having a function call itself to clear out each nested subfolder is called recursion. Here is a custom function that handles this automatically:
<?php
function deleteFolderRecursive($path) {
// Check if the folder exists and is a directory
if (!is_dir($path)) return false;
// Go through every file and subfolder
$items = scandir($path);
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
$fullPath = $path . DIRECTORY_SEPARATOR . $item;
if (is_link($fullPath)) {
unlink($fullPath);
} elseif (is_dir($fullPath)) {
deleteFolderRecursive($fullPath);
} else {
unlink($fullPath);
}
}
// Finally delete the now-empty folder
return rmdir($path);
}
You can save this helper in a functions.php file and include it whenever you need it.
Basic usage example:
<?php
include('functions.php');
$folder = 'target_folder';
// Check if the folder exists and is a directory
if (is_dir($folder)) {
// Try to delete the folder and all of its contents
if (deleteFolderRecursive($folder)) {
echo 'Folder deleted.';
} else {
echo 'Failed to delete folder.';
}
} else {
echo 'Folder does not exist or is not a directory.';
}
Note: If you are accepting the folder path from user input (like a URL parameter), swap in the recursive function while keeping your sandbox security checks intact:
<?php
include('functions.php');
$folder = $_GET['folder'] ?? '';
// Define the safe base folder
$baseDir = __DIR__ . DIRECTORY_SEPARATOR . 'uploads';
$realBase = realpath($baseDir);
// Resolve target path safely
$targetPath = $realBase ? realpath($realBase . DIRECTORY_SEPARATOR . $folder) : false;
// Ensure target exists, is not the base itself, and resides strictly inside the base
if ($realBase && $targetPath && $targetPath !== $realBase && strpos($targetPath, $realBase . DIRECTORY_SEPARATOR) === 0) {
if (is_dir($targetPath)) {
if (deleteFolderRecursive($targetPath)) {
echo 'Folder and all contents deleted.';
} else {
echo 'Failed to delete folder contents.';
}
} else {
echo 'Not a valid folder.';
}
} else {
echo 'Invalid folder path.';
}
Alternative approach using PHP iterators:
If you prefer an object-oriented approach without writing custom recursive logic, PHP has built-in iterators in its Standard PHP Library (SPL) that can loop through directory trees for you:
<?php
function deleteFolderSpl(string $dir): bool {
if (!is_dir($dir)) return false;
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($files as $file) {
$file->isDir() && !$file->isLink() ? rmdir($file->getRealPath()) : unlink($file->getRealPath());
}
return rmdir($dir);
}