Core ships two static utility classes for working with the filesystem:
Webrium\File— byte-level file I/O: reading, writing, copying, moving, deleting, streaming, downloading, and querying metadata.Webrium\Directory— a registry-based directory manager: register named directories relative to the application root, then create, inspect, copy, and delete them safely.
Both are entirely static; you never instantiate them.
Webrium\File is a focused file I/O helper. It deliberately does not include PHP execution (include/require) or controller dispatch — those belong to the framework's Kernel. Every method is static and takes a filesystem path.
| Method | Description |
|---|---|
exists(string $path): bool |
Whether a file or directory exists at the path |
isFile(string $path): bool |
Whether the path is a file (not a directory) |
isDirectory(string $path): bool |
Whether the path is a directory |
isReadable(string $path): bool |
Whether the file is readable |
isWritable(string $path): bool |
Whether the file is writable |
use Webrium\File;
if (File::exists('/var/data/report.csv') && File::isReadable('/var/data/report.csv')) {
// safe to read
}| Method | Returns |
|---|---|
size(string $path) |
Size in bytes, or false if the file is missing |
humanSize(string $path, int $precision = 2) |
Human-readable size (e.g. "1.5 MB"), or false |
lastModified(string $path) |
Unix timestamp of last modification, or false |
extension(string $path): string |
Lower-cased extension, without the dot |
name(string $path): string |
Filename without extension |
basename(string $path): string |
Filename with extension |
dirname(string $path): string |
Containing directory path |
mimeType(string $path) |
Real MIME type via finfo, or false |
permissions(string $path) |
File permissions, or false |
owner(string $path) |
Owner user ID, or false |
File::extension('/uploads/photo.JPG'); // "jpg"
File::name('/uploads/photo.jpg'); // "photo"
File::humanSize('/uploads/photo.jpg'); // "248.31 KB"
File::mimeType('/uploads/photo.jpg'); // "image/jpeg"| Method | Description |
|---|---|
read(string $path) |
Full contents as a string, or false if missing |
getContent(string $path) |
Alias of read() |
lines(string $path) |
Array of lines (empty lines and trailing newlines skipped), or false |
$config = File::read('/etc/app/config.json');
foreach (File::lines('/var/log/access.log') ?: [] as $line) {
// process each line
}| Method | Description |
|---|---|
write(string $path, string $content, bool $append = false) |
Write content; returns bytes written or false |
putContent(string $path, string $content) |
Alias of write() (no append) |
append(string $path, string $content) |
Append to the end of the file |
prepend(string $path, string $content) |
Prepend to the start, using an exclusive lock to avoid concurrent-write data loss |
File::write('/tmp/out.txt', "first line\n");
File::append('/tmp/out.txt', "second line\n");
File::prepend('/tmp/out.txt', "header\n");
prepend()reads the whole file into memory and rewrites it underLOCK_EX. For very large files prefer a streaming approach. If the file does not exist yet, it is created with the given content.
| Method | Description |
|---|---|
copy(string $source, string $destination): bool |
Copy a file; false if the source is missing |
move(string $source, string $destination): bool |
Move/rename a file; false if the source is missing |
delete(string $path): bool |
Delete a file; false if it does not exist |
deleteMultiple(array $paths): int |
Delete several files; returns the count actually deleted |
File::copy('/tmp/a.txt', '/backup/a.txt');
File::move('/tmp/a.txt', '/archive/a.txt');
File::delete('/archive/a.txt');
$removed = File::deleteMultiple(['/tmp/1.tmp', '/tmp/2.tmp']);These convenience listers exist on File; for richer directory management use the Directory class.
| Method | Description |
|---|---|
getFiles(string $path, array $exclude = ['.', '..', '.gitignore']) |
Names of entries directly inside a directory |
getFilesRecursive(string $path, array $exclude = …) |
Full paths of all files in the tree |
glob(string $pattern, int $flags = 0) |
Files matching a glob pattern |
matches(string $path, string $pattern): bool |
Whether a path matches a glob pattern (fnmatch) |
$names = File::getFiles('/uploads'); // ['a.jpg', 'b.png', ...]
$all = File::getFilesRecursive('/uploads'); // ['/uploads/2024/a.jpg', ...]
$imgs = File::glob('/uploads/*.jpg');
getFiles()returns the entry names directly inside the directory (both files and sub-directory names), whilegetFilesRecursive()walks the tree and returns the full paths of files only.
File::makeDirectory()also exists but is deprecated — useDirectory::make()instead.
These methods send headers and file content directly to the client, then call exit. Use them inside a controller action that is responsible for the response.
Streams a file inline with HTTP range support — ideal for video and audio, since it lets the browser seek. Responds with 206 Partial Content for range requests and 416 for invalid ranges. A missing file returns 404. The download name is sanitized for the Content-Disposition header.
File::stream('/media/lecture.mp4');Sends the file as an attachment (forces a download dialog) with the correct length and MIME type. A missing file returns 404. The download name is sanitized to prevent header injection.
File::download('/invoices/2024-01.pdf', 'invoice.pdf');Serves an image inline. It first verifies the file's real MIME type begins with image/; non-image files are rejected with 403. This prevents this method from being abused to disclose sensitive files such as .php or .env. Missing files return 404.
File::showImage('/uploads/avatars/user_42.png');
stream()anddownload()sanitize the outgoing filename (stripping path separators, null bytes, and control characters, and escaping quotes) before writing theContent-Dispositionheader.showImage()serves inline without aContent-Dispositionfilename.
| Method | Description |
|---|---|
hash(string $path) |
MD5 hash of the file, or false |
sha1(string $path) |
SHA-1 hash, or false |
hashFile(string $path, string $algorithm = 'md5') |
Hash using any algorithm supported by hash_file() |
File::hash('/downloads/setup.bin'); // md5
File::hashFile('/downloads/setup.bin', 'sha256'); // sha256| Method | Description |
|---|---|
chmod(string $path, int $mode): bool |
Change permissions; false if the file is missing |
permissions(string $path) |
Read current permissions, or false |
owner(string $path) |
Owner user ID, or false |
File::chmod('/uploads/private.txt', 0600);Webrium\Directory manages directories by registered name rather than raw paths. You register a logical name (e.g. uploads) mapped to a path relative to the application root, then refer to it by name everywhere. Path resolution is cached and guarded against directory traversal.
Most methods accept either a registered name or, where noted, an absolute path.
| Method | Description |
|---|---|
set(string $name, string $path): void |
Register one directory (path relative to root) |
setMultiple(array $directories): void |
Register many from a name => path map |
get(string $name): ?string |
The registered relative path, or null |
path(string $name, string $append = ''): ?string |
The absolute path, optionally with a sanitized sub-path appended |
all(): array |
All registered name => relative path pairs |
allPaths(): array |
All registered name => absolute path pairs |
has(string $name): bool |
Whether a name is registered |
forget(string $name): bool |
Unregister a name (does not touch the filesystem) |
clear(): void |
Unregister everything |
use Webrium\Directory;
Directory::set('uploads', 'public/uploads');
Directory::set('logs', 'storage/logs');
Directory::path('uploads'); // /var/www/app/public/uploads
Directory::path('uploads', 'avatars/42.png'); // .../public/uploads/avatars/42.pngWhen you append a sub-path via
path(), the result is sanitized:..and.segments are resolved without touching the filesystem, and anInvalidArgumentExceptionis thrown if the path would escape the application root.
Registers a complete conventional layout in one call — application directories (app, controllers, models, views, routes, config, middleware, helpers, services), storage (storage, storage_app, sessions, cache, rendered/static views), logs, langs, and public directories (public, assets, uploads). Application sub-paths follow PSR-4 PascalCase (e.g. app/Controllers, app/Models).
Directory::initDefaultStructure();
Directory::makeAll(); // create them all on disk| Method | Description |
|---|---|
make(string $path, int $permissions = 0755, bool $recursive = true): bool |
Create a directory (accepts a registered name or absolute path); returns true if it already exists |
makeAll(int $permissions = 0755): array |
Create all registered directories; returns the names created |
makes(): array |
Alias of makeAll() |
Directory::make('uploads'); // by registered name
Directory::make('/var/www/cache'); // by absolute path
$created = Directory::makeAll();| Method | Description |
|---|---|
exists(string $name): bool |
Whether a registered directory exists on disk |
validate(): bool |
Whether all registered directories exist |
getMissing(): array |
Names that are registered but missing on disk |
getExisting(): array |
Names that exist on disk |
isReadable(string $name): bool |
Whether the directory is readable |
isWritable(string $name): bool |
Whether the directory is writable |
if (!Directory::validate()) {
$missing = Directory::getMissing();
Directory::makeAll();
}| Method | Description |
|---|---|
size(string $name) |
Total size in bytes (recursive), or false |
humanSize(string $name, int $precision = 2) |
Human-readable total size, or false |
fileCount(string $name, bool $recursive = false) |
Number of files (optionally recursive), or false |
stats(string $name) |
Rich stats array, or false |
stats() returns: path, exists, readable, writable, files, directories, total_items, size_bytes, size_human, and permissions (the last as a four-digit octal string such as "0755").
Directory::humanSize('uploads'); // "12.4 MB"
Directory::fileCount('uploads', true);
$info = Directory::stats('uploads');
// ['files' => 128, 'directories' => 6, 'size_human' => '12.4 MB', ...]| Method | Description |
|---|---|
copy(string $source, string $destination, bool $overwrite = false): bool |
Recursively copy a directory (source by name or path; destination is an absolute path) |
delete(string $name): bool |
Recursively delete a directory and its contents (by name or absolute path) |
empty(string $name): bool |
Delete the contents but keep the directory itself |
Directory::copy('uploads', '/backup/uploads');
Directory::empty('cache'); // clear cache but keep the folder
Directory::delete('temp'); // remove the folder entirelySafety guarantees.
delete()andempty()only operate on paths inside the application root — an attempt to remove a path outside it returnsfalse. Symlinks are unlinked directly and never followed, so a symlink pointing outside the tree cannot cause collateral deletion.
Returns a nested array representing the directory structure. Sub-directories become nested arrays keyed by name; files appear as values. Use $maxDepth to limit recursion (-1 = unlimited).
$tree = Directory::tree('app', 2);
/*
[
'Controllers' => ['HomeController.php', 'UserController.php'],
'Models' => ['User.php'],
'routes.php',
]
*/| Method | Description |
|---|---|
permissions(string $name) |
Read directory permissions, or false |
chmod(string $name, int $permissions): bool |
Change directory permissions |
Directory::chmod('uploads', 0775);Reach for File when you have a concrete path and need to read, write, hash, or serve a single file. Reach for Directory when you want to manage your application's folder layout by logical name — registering the structure once, creating it, checking its health, and performing safe recursive operations within the application root.