Docs LATEST

Storage

Filesystem abstraction for working with local and remote file storage.

Services

Namespace: VelvetCMS\Services\StorageManager


StorageManager #

Manages filesystem disks configured in config/filesystems.php.

Definition #

class StorageManager
{
    public function __construct(array $config);
    public function disk(?string $name = null): FilesystemInterface;
}

Accessing Storage #

// Via container
$storage = app('storage');

// Get default disk
$disk = $storage->disk();

// Get specific disk
$disk = $storage->disk('local');
$disk = $storage->disk('public');

FilesystemInterface #

All disks implement this interface.

Definition #

interface FilesystemInterface
{
    public function exists(string $path): bool;
    public function get(string $path): ?string;
    public function put(string $path, mixed $contents, array $config = []): bool;
    public function delete(string $path): bool;
    public function makeDirectory(string $path): bool;
    public function size(string $path): int;
    public function lastModified(string $path): int;
    public function files(string $directory, bool $recursive = false): array;
    public function url(string $path): string;
    public function path(string $path): string;
}

Methods #

exists() #

Check if a file exists.

public function exists(string $path): bool

Example:

$disk = app('storage')->disk();

if ($disk->exists('uploads/photo.jpg')) {
    // File exists
}

get() #

Read file contents.

public function get(string $path): ?string

Returns: File contents or null if not found

Example:

$content = $disk->get('config/settings.json');

if ($content !== null) {
    $settings = json_decode($content, true);
}

put() #

Write content to a file. Creates directories automatically.

public function put(string $path, mixed $contents, array $config = []): bool
Parameter Type Description
$path string File path
$contents string|resource Content to write
$config array Optional configuration

Returns: true on success

Example:

// Write string content
$disk->put('notes/hello.txt', 'Hello, World!');

// Write JSON
$disk->put('data/users.json', json_encode($users, JSON_PRETTY_PRINT));

// Write from stream
$stream = fopen('php://input', 'r');
$disk->put('uploads/file.bin', $stream);

delete() #

Delete a file.

public function delete(string $path): bool

Returns: true on success (also returns true if file didn't exist)

Example:

$disk->delete('temp/cache.tmp');

makeDirectory() #

Create a directory.

public function makeDirectory(string $path): bool

Example:

$disk->makeDirectory('uploads/2026/01');

size() #

Get file size in bytes.

public function size(string $path): int

Example:

$bytes = $disk->size('uploads/video.mp4');
$mb = round($bytes / 1024 / 1024, 2);
echo "File size: {$mb} MB";

lastModified() #

Get file modification timestamp.

public function lastModified(string $path): int

Returns: Unix timestamp

Example:

$timestamp = $disk->lastModified('data/cache.json');
$date = date('Y-m-d H:i:s', $timestamp);

// Check if file is stale
if (time() - $timestamp > 3600) {
    $this->refreshCache();
}

files() #

List files in a directory.

public function files(string $directory, bool $recursive = false): array
Parameter Type Default Description
$directory string - Directory path
$recursive bool false Include subdirectories

Returns: Array of relative file paths

Example:

// List files in directory
$files = $disk->files('uploads');
// ['uploads/photo1.jpg', 'uploads/photo2.png']

// List recursively
$allFiles = $disk->files('uploads', true);
// ['uploads/photo1.jpg', 'uploads/2026/01/doc.pdf']

url() #

Get public URL for a file.

public function url(string $path): string

Throws: RuntimeException if disk doesn't support URLs

Example:

$disk = app('storage')->disk('public');
$url = $disk->url('images/logo.png');
// https://example.com/storage/images/logo.png

path() #

Get absolute filesystem path.

public function path(string $path): string

Example:

$absolutePath = $disk->path('uploads/file.txt');
// /var/www/app/storage/uploads/file.txt

Configuration #

Configure disks in config/filesystems.php:

return [
    'default' => 'local',

    'disks' => [
        'local' => [
            'driver' => 'local',
            'root' => storage_path('app'),
        ],

        'public' => [
            'driver' => 'local',
            'root' => storage_path('app/public'),
            'url' => '/storage',
        ],

        'uploads' => [
            'driver' => 'local',
            'root' => base_path('uploads'),
            'url' => '/uploads',
            'permissions' => 0755,
        ],
    ],
];

LocalDriver #

The built-in local filesystem driver.

Namespace: VelvetCMS\Drivers\Storage\LocalDriver

Features #

  • Automatic directory creation
  • Path traversal prevention (sanitizes ..)
  • Stream support for large files
  • Configurable permissions

Configuration Options #

Option Type Default Description
root string - Base directory path
url string null Public URL prefix
permissions int 0755 Directory permissions

Usage Examples #

File Upload Handler #

class UploadController
{
    public function store(Request $request): Response
    {
        $file = $request->file('document');

        if (!$file) {
            return Response::json(['error' => 'No file uploaded'], 400);
        }

        $disk = app('storage')->disk('uploads');

        // Generate unique filename
        $filename = sprintf(
            '%s/%s_%s',
            date('Y/m'),
            uniqid(),
            $file['name']
        );

        // Save file
        $stream = fopen($file['tmp_name'], 'r');
        $disk->put($filename, $stream);
        fclose($stream);

        return Response::json([
            'path' => $filename,
            'url' => $disk->url($filename),
            'size' => $disk->size($filename),
        ], 201);
    }
}

Backup Service #

class BackupService
{
    public function __construct(
        private readonly StorageManager $storage
    ) {}

    public function createBackup(): string
    {
        $disk = $this->storage->disk('local');
        $backupDisk = $this->storage->disk('backups');

        $timestamp = date('Y-m-d_His');
        $backupDir = "backup_{$timestamp}";

        // Copy all content files
        $files = $disk->files('content', true);

        foreach ($files as $file) {
            $content = $disk->get($file);
            $backupDisk->put("{$backupDir}/{$file}", $content);
        }

        return $backupDir;
    }

    public function cleanOldBackups(int $keepDays = 7): int
    {
        $disk = $this->storage->disk('backups');
        $cutoff = time() - ($keepDays * 86400);
        $deleted = 0;

        $files = $disk->files('', true);

        foreach ($files as $file) {
            if ($disk->lastModified($file) < $cutoff) {
                $disk->delete($file);
                $deleted++;
            }
        }

        return $deleted;
    }
}

Cache File Manager #

class FileCacheManager
{
    private FilesystemInterface $disk;

    public function __construct(StorageManager $storage)
    {
        $this->disk = $storage->disk('local');
    }

    public function get(string $key): mixed
    {
        $path = $this->path($key);

        if (!$this->disk->exists($path)) {
            return null;
        }

        $content = $this->disk->get($path);
        $data = json_decode($content, true);

        if ($data['expires'] < time()) {
            $this->disk->delete($path);
            return null;
        }

        return $data['value'];
    }

    public function set(string $key, mixed $value, int $ttl = 3600): void
    {
        $path = $this->path($key);

        $data = [
            'value' => $value,
            'expires' => time() + $ttl,
        ];

        $this->disk->put($path, json_encode($data));
    }

    private function path(string $key): string
    {
        return 'cache/' . md5($key) . '.json';
    }
}

Image Processing #

class ImageService
{
    public function __construct(
        private readonly StorageManager $storage
    ) {}

    public function resize(string $path, int $width, int $height): string
    {
        $disk = $this->storage->disk('public');

        // Get original
        $original = $disk->get($path);

        // Create resized version
        $image = imagecreatefromstring($original);
        $resized = imagescale($image, $width, $height);

        // Generate new path
        $info = pathinfo($path);
        $newPath = sprintf(
            '%s/%s_%dx%d.%s',
            $info['dirname'],
            $info['filename'],
            $width,
            $height,
            $info['extension']
        );

        // Save
        ob_start();
        imagejpeg($resized, null, 85);
        $content = ob_get_clean();

        $disk->put($newPath, $content);

        imagedestroy($image);
        imagedestroy($resized);

        return $newPath;
    }
}

Path Safety #

The LocalDriver prevents directory traversal attacks:

// These paths are sanitized
$disk->get('../../../etc/passwd');     // Becomes: /root/etc/passwd
$disk->get('uploads/../../../secret'); // Becomes: /root/uploads/secret

// '..' segments are stripped, keeping you within the disk root