FileManager is a utility class that provides a wide range of common filesystem operations β removing files, traversing directories, copying, cleaning, and more β all with built-in error handling.
To start using it, import the class at the top of your script:
<?php
use Krystal\Filesystem\FileManager;
?>
Deletes a file.
Throws RuntimeException if the given path is not a valid file.
Usage:
FileManager::rmfile('/path/to/file.txt');
Returns a flat array of all paths within a directory.
Throws RuntimeException if the path is not a valid directory.
Usage:
$files = FileManager::getDirTree('/path/to/dir', true);
Calculates the total size in bytes of all files in a directory (including subdirectories).
Throws RuntimeException if the path is invalid.
Usage:
$totalBytes = FileManager::getDirSizeCount('/var/www/html/uploads');
Creates a directory (including parents) if it doesnβt exist. Returns false if already present.
FileManager::createDir('/var/www/html/cache');
Removes all contents from a directory but keeps the directory itself.
FileManager::cleanDir('/var/www/html/tmp');
Applies the given permission mode to a file or directory recursively.
Populates $ignored with paths that failed.
Throws UnexpectedValueException if the target is invalid.
$ignored = [];
FileManager::chmod('/var/www/html/', 0755, $ignored);
Recursively deletes a directory and all its contents.
Throws RuntimeException if the path is invalid.
Usage:
FileManager::rmdir('/var/www/html/old_folder');
Recursively copies the source directory into the destination.
Throws RuntimeException if the source path is invalid.
Usage:
FileManager::copy('/var/www/html/source', '/backup/html/source_copy');
Moves a directory.
Usage:
FileManager::move('/var/www/html/temp', '/var/www/html/archive');
Checks whether a file is empty.
Throws RuntimeException if the given path is not a valid file.
Usage:
$isEmpty = FileManager::isFileEmpty('/var/log/debug.log');
Returns an array of directories found directly under the given path.
Throws UnexpectedValueException if the directory cannot be read.
Usage:
$dirs = FileManager::getFirstLevelDirs('/var/www/html');
Determines the MIME type of a given file using PHPβs finfo or similar internal methods. Throws RuntimeException if the given path is not a valid file.
$mimeType = FileManager::getMimeType('/path/to/image.jpg'); // e.g., returns "image/jpeg"
Reads the file's magic bytes (file signature) and returns the real file type as a normalized lowercase extension, regardless of the file's name or claimed extension.
Returns null when the type cannot be detected.
Usage:
$realExtension = FileManager::getRealExtension('/path/to/uploaded-file');
// e.g., returns "jpg" even if the file is named "upload.php"
Validates that the actual file content matches one of the allowed extensions. Uses magic byte detection under the hood.
This is the recommended way to validate uploads β it catches cases where an attacker renames an executable to .jpg.
Usage:
$allowed = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
if (FileManager::isContentAllowed('/path/to/uploaded-file', $allowed)) {
// Safe to proceed
}
Detects whether the file is an executable binary (ELF, Mach-O, Windows PE, or Java class).
Use this as an additional safety layer on top of extension validation.
Usage:
if (FileManager::isExecutable('/path/to/uploaded-file')) {
// Reject the upload β file is an executable
}
Verifies whether the given file path ends with the specified extension.
Throws RuntimeException if the given path is not a valid file.
Usage:
$hasExt = FileManager::hasExtension('/path/to/document.pdf', ['pdf']);
Takes a file size in bytes and converts it to a more readable format (KB, MB, GB, etc.), rounded to two decimal places by default.
Usage:
echo FileManager::humanSize(1048576); // Output: "1 MB"
Returns the size of the specified file in a human-readable format (e.g., KB, MB, GB). Internally uses humanSize() to convert bytes.
Throws RuntimeException if the path is not a valid file.
Usage:
$size = FileManager::filesize('/path/to/file.txt');
// e.g., returns "1 KB"
Extracts just the file name from a full file path.
Usage:
$name = FileManager::getFileName('/var/www/index.php'); // Output: "index"
Returns the directory portion of a file path.
Usage:
$dir = FileManager::getDirName('/var/www/index.php'); // Output: "/var/www"
Retrieves the extension of a given file (without the dot).
Usage:
$ext = FileManager::getExtension('/var/www/index.php'); // Output: "php"
Returns the base file name including its extension.
Usage:
$base = FileManager::getBaseName('/var/www/index.php'); // Output: "index.php"
Replaces the extension of a given file with a new one.
Usage:
$newFile = FileManager::replaceExtension('/var/www/index.php', 'html'); // Output: "/var/www/index.html"
MagicBytesDetector is a dedicated class that detects a file's real type by reading its magic bytes (file signature). It supports images (JPEG, PNG, GIF, WEBP, TIFF, HEIC, AVIF), documents (PDF, PS, RTF, OLE-based), archives (ZIP, RAR, 7z, GZIP, BZIP2, TAR), media (MP3, FLAC, OGG, WAV, MP4, AVI, MKV, FLV), data formats (WASM, SQLite), and executables (ELF, Mach-O, PE, Java class).
Unlike FileManager::getMimeType(), which relies on the file's extension, MagicBytesDetector reads the actual bytes and cannot be fooled by renaming.
To start using it, import the class at the top of your script:
<?php
use Krystal\Filesystem\MagicBytesDetector;
?>
The constructor accepts an absolute path to a file. If the path is not a valid file, it throws InvalidArgumentException. Detection results are cached per instance, so calling multiple methods on the same instance reads the file only once.
Returns the real file type as a normalized lowercase extension (e.g. "jpg", "png", "pdf"), or null when the type cannot be detected.
Usage:
$detector = new MagicBytesDetector('/path/to/uploaded-file');
$realExtension = $detector->getRealExtension();
Validates that the actual content of the file matches one of the allowed extensions. Returns true only if the detected type is in the whitelist.
Usage:
$detector = new MagicBytesDetector('/path/to/uploaded-file');
if ($detector->isAllowed(['jpg', 'jpeg', 'png', 'gif', 'webp'])) {
// Safe to proceed
}
Returns true if the file is a recognized executable binary (ELF, Mach-O, Windows PE, or Java class). Useful as a hard deny-check during upload validation.
Usage:
$detector = new MagicBytesDetector('/path/to/uploaded-file');
if ($detector->isExecutable()) {
// Reject the upload
}
Because detection is cached, calling isExecutable() and isAllowed() on the same instance performs the file read only once β which is faster than calling FileManager's static wrappers separately.
Usage:
$detector = new MagicBytesDetector($file->getTmpName());
if ($detector->isExecutable()) {
throw new RuntimeException('Executable files are not allowed');
}
if (!$detector->isAllowed(['jpg', 'png'])) {
throw new RuntimeException('Invalid image content');
}
$file->move($uploadDir);
If the underlying file has been replaced since the detector was constructed, call resetCache() to force re-detection on the next call.
Usage:
$detector->resetCache();
$newType = $detector->getRealExtension();
Returns the absolute path that was provided to the constructor.
Usage:
$detector = new MagicBytesDetector('/path/to/file');
echo $detector->getFile(); // "/path/to/file"
Returns the full signature map as an array. Useful for external validation, building admin panels, or debugging.
Usage:
$detector = new MagicBytesDetector('/path/to/file');
$signatures = $detector->getSignatures();
Returns the list of extensions treated as executables.
Usage:
$extensions = $detector->getExecutableExtensions();
// ['elf', 'macho', 'exe', 'class']
Magic bytes are a strong first line of defense, but not a complete solution:
getimagesize() to catch polyglot files.ftyp container. The detector returns "mp4" for all of them.isAllowed() with isExecutable() and with a stored-extension check β no single method is sufficient on its own.