Working with file-system

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;

?>

Remove a file

Deletes a file.
Throws RuntimeException if the given path is not a valid file.

Usage:

FileManager::rmfile('/path/to/file.txt');

Get directory tree

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);

Get total directory size

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

Create a directory

Creates a directory (including parents) if it doesn’t exist. Returns false if already present.

FileManager::createDir('/var/www/html/cache');

Clean a directory

Removes all contents from a directory but keeps the directory itself.

FileManager::cleanDir('/var/www/html/tmp');

Change permissions recursively

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);

Remove a directory

Recursively deletes a directory and all its contents.
Throws RuntimeException if the path is invalid.

Usage:

FileManager::rmdir('/var/www/html/old_folder');

Copy a directory

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

Move a directory

Moves a directory.

Usage:

FileManager::move('/var/www/html/temp', '/var/www/html/archive');

Check if a file is empty

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

Get first-level directories

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

Get MIME type of a file

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"

Detect the real file type by magic bytes

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"

Check if file content matches allowed extensions

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
}

Check if file is an executable binary

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
}

Check if a file has a specific extension

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']);

Convert bytes to a human-readable size

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"

Get file size

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"

Get file name (without path)

Extracts just the file name from a full file path.

Usage:

$name = FileManager::getFileName('/var/www/index.php'); // Output: "index"

Get directory name from a path

Returns the directory portion of a file path.

Usage:

$dir = FileManager::getDirName('/var/www/index.php'); // Output: "/var/www"

Get file extension

Retrieves the extension of a given file (without the dot).

Usage:

$ext = FileManager::getExtension('/var/www/index.php'); // Output: "php"

Get base name

Returns the base file name including its extension.

Usage:

$base = FileManager::getBaseName('/var/www/index.php'); // Output: "index.php"

Replace file extension

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"

Working with file signatures

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.

Detect the real file type

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();

Check whether file content matches allowed extensions

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
}

Check whether the file is an executable

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
}

Reuse a single instance for multiple checks

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);

Reset the cache

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();

Get the path to the inspected file

Returns the absolute path that was provided to the constructor.

Usage:

$detector = new MagicBytesDetector('/path/to/file');

echo $detector->getFile(); // "/path/to/file"

Inspect known signatures

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();

Inspect executable extensions

Returns the list of extensions treated as executables.

Usage:

$extensions = $detector->getExecutableExtensions();
// ['elf', 'macho', 'exe', 'class']

Security notes

Magic bytes are a strong first line of defense, but not a complete solution:

  • SVG is text-based XML and can carry embedded scripts. Sanitize it with a dedicated library before serving, or forbid SVG uploads entirely.
  • Images should be additionally validated with getimagesize() to catch polyglot files.
  • ZIP-based documents (docx, xlsx, pptx, odt, ods) share the same ZIP signature. To verify the specific type, inspect the archive's internal structure.
  • MP4 family (mp4, m4a, m4v, 3gp) shares the same ftyp container. The detector returns "mp4" for all of them.
  • Always combine isAllowed() with isExecutable() and with a stored-extension check β€” no single method is sufficient on its own.