Storage::exists

Reports whether a path exists on a disk, returning true for directories as well as files.

Signature
Storage::exists(string $path): bool
  • Added in Laravel 5.0

Parameters

ParameterTypeDescription
$$pathstringPath to check, relative to the disk's configured root. A single path — there is no array form.

Return value

booltrue when a file or a directory exists at the path, false otherwise.

Errors

Nothing for a missing path. A driver-level failure such as an S3 credential error surfaces as a Flysystem exception rather than false.

Storage::exists asks the disk whether something is at the path. Storage::missing is the inverse and reads better in a guard clause: if (Storage::missing($path)) { abort(404); } beats a negated exists.

When to reach for it

Use it when you need to branch without reading the file: deciding whether to regenerate a thumbnail, reporting a broken attachment in a health check, choosing between a stored avatar and a default. Use it in tests through assertExists and assertMissing on a faked disk.

What trips people up

It is not a cheap call on a remote driver. Each invocation is an HTTP request to S3, so a loop over a thousand records makes a thousand requests. Prefer Storage::files($directory) once and check membership in PHP.

Do not use it to guard a read. exists followed by get is two round trips with a race between them, and get already returns null when the file is gone — checking that one return value is both cheaper and correct.

It returns true for directories, because it checks file existence and directory existence. Storage::exists('avatars') is true as soon as one avatar is in there, which surprises code that assumes a file. And it takes one path only — the array form belongs to assertExists, not to exists.

Example

Example · php
<?php

use Illuminate\Support\Facades\Storage;

Storage::disk('public')->put('avatars/7.png', 'binary');

var_dump(Storage::disk('public')->exists('avatars/7.png'));    // bool(true)
var_dump(Storage::disk('public')->exists('avatars/99.png'));   // bool(false)

// missing() is the inverse and reads better as a guard.
if (Storage::disk('public')->missing('avatars/99.png')) {
    echo 'falling back to the default avatar', PHP_EOL;
}

// Careful: directories count as existing too.
var_dump(Storage::disk('public')->exists('avatars'));          // bool(true)

// One path per call - there is no array form here.
// Storage::exists(['a.png', 'b.png']);  // not supported

When to use it

  • Skip regenerating a thumbnail when the derived image is already on the disk.
  • Report attachments referenced by a database row that are no longer present, in a nightly integrity check.
  • Fall back to a default avatar image when the user's stored file is gone.

Frequently asked questions

What is the difference between Storage::exists and Storage::missing?

missing is simply the negation of exists, and it exists so guard clauses read naturally — if (Storage::missing($path)) { abort(404); }. Both take a single path and both return true for directories.

Can Storage::exists check several files at once?

No, it takes one path per call. The array form belongs to the testing assertions, Storage::disk('photos')->assertExists(['a.jpg', 'b.jpg']). To check many real files efficiently, list the directory once with Storage::files() and compare in PHP rather than making one request per path.

Is Storage::exists expensive?

On the local driver it is a stat call and effectively free; on S3, SFTP or FTP it is a network round trip. A loop calling it per database row is a common cause of slow admin pages — list the directory once instead.

More examples

Do not use exists to guard a read

On S3 each exists is a HEAD request and each get is a GET, so the guarded version doubles the network cost for no benefit — get already tells you the file was not there. The last block is the legitimate use: the decision is about whether to do work, and no contents are needed.

Example · php
<?php

use Illuminate\Support\Facades\Storage;

// Two round trips, and the file can vanish in between.
if (Storage::disk('s3')->exists('invoices/42.pdf')) {
    $pdf = Storage::disk('s3')->get('invoices/42.pdf');   // may still be null
}

// One round trip, no race, and it handles every failure mode.
if (($pdf = Storage::disk('s3')->get('invoices/42.pdf')) !== null) {
    echo strlen($pdf).' bytes', PHP_EOL;
} else {
    echo 'invoice not available', PHP_EOL;
}

// exists earns its place when you are NOT going to read the file.
if (Storage::disk('public')->missing("thumbs/{$id}.jpg")) {
    // dispatch(new GenerateThumbnail($id));
    echo 'thumbnail queued', PHP_EOL;
}

Checking many files without one request each

files() returns the directory's contents in a single listing call, turning N network round trips into one. The sort at the end matters for anything you assert on or render: neither the local driver nor S3 promises a particular order, so an unsorted comparison passes on one machine and fails on another.

Example · php
<?php

use Illuminate\Support\Facades\Storage;

$expected = ['avatars/1.png', 'avatars/2.png', 'avatars/3.png'];

// Slow: three HTTP requests against S3.
foreach ($expected as $path) {
    Storage::disk('s3')->exists($path);
}

// Fast: one listing, then membership tests in PHP.
$present = Storage::disk('s3')->files('avatars');

$missing = array_values(array_diff($expected, $present));

print_r($missing);

// Expected output when 2.png is gone:
// Array
// (
//     [0] => avatars/2.png
// )

// Sort any listing you intend to compare or display - the order
// a driver returns entries in is not guaranteed.
sort($present);
print_r($present);

Discussion

  • Be the first to comment on this lesson.