Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Image Mime Type Guesser Laravel Package

rosell-dk/image-mime-type-guesser

A lightweight PHP library for detecting the MIME type of image files using multiple strategies, including finfo, exif_imagetype, getimagesize and magic byte sniffing. It falls back gracefully between methods, making it reliable for validating uploads and identifying images even when some PHP extensi...

Deep Wiki
Context7

Getting Started

rosell-dk/image-mime-type-guesser is a standalone PHP utility, not a Laravel package — there is no service provider, facade, or config file to publish. Install it and call it statically:

composer require rosell-dk/image-mime-type-guesser
use ImageMimeTypeGuesser\ImageMimeTypeGuesser;

$mime = ImageMimeTypeGuesser::detect($path); // 'image/png' | null

Look first at the class ImageMimeTypeGuesser\ImageMimeTypeGuesser. Depending on the installed version you will see either guess() (returns the detected MIME type or false) or detect() (returns the MIME type or null); older code commonly uses guess(), and detect() is the newer, cleaner API. Check the version in composer.lock before choosing which to call in new code.

The first use case for a Laravel developer is nearly always verifying uploaded files — confirming a user-uploaded "photo.jpg" is actually a JPEG/PNG/WebP rather than a renamed script. In a controller:

$path = $request->file('photo')->getRealPath();
$mime = ImageMimeTypeGuesser::detect($path);

if (!in_array($mime, ['image/jpeg', 'image/png', 'image/webp'], true)) {
    abort(422, 'Invalid image upload.');
}

That is the whole integration. Everything else is wrapping it in idiomatic Laravel structures.

Implementation Patterns

Custom validation rule. The most reusable pattern is a rule that inspects the real bytes of an UploadedFile:

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Http\UploadedFile;
use ImageMimeTypeGuesser\ImageMimeTypeGuesser;

class ImageMimeTypeRule implements ValidationRule
{
    public function __construct(private array $allowed) {}

    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        if (!$value instanceof UploadedFile || !$value->isValid()) {
            $fail("The {$attribute} must be a valid upload.");
            return;
        }

        $mime = ImageMimeTypeGuesser::detect($value->getRealPath());

        if (!in_array($mime, $this->allowed, true)) {
            $fail("The {$attribute} must be one of: " . implode(', ', $this->allowed));
        }
    }
}

Usage: 'avatar' => ['required', new ImageMimeTypeRule(['image/jpeg', 'image/png', 'image/webp'])].

Combine with extension checks. Detect the real MIME type, then compare it against the client-supplied extension ($file->extension()) or getClientOriginalExtension(). Reject mismatches outright rather than silently renaming the file. This catches the common "shell.php renamed to shell.jpg" attack.

Verify after moving to Storage. Temp upload paths can differ per driver; after $file->store(...) or Storage::putFile(...), call detect(Storage::path($key)) once more before persisting metadata like dimensions. This is cheap and guards against truncated uploads.

Wrap it in a small service if you call it from multiple places, so you can centralize the allowed-list config, memoize results per path, and swap the backend later without touching call sites:

class ImageInspector
{
    private array $cache = [];

    public function mime(string $path): ?string
    {
        return $this->cache[$path] ??= ImageMimeTypeGuesser::detect($path);
    }
}

Pair with getimagesize() when you also need width/height — one read of the file for both checks. Use the MIME guesser for format verification and getimagesize() for dimensions and a second opinion on the format.

Gotchas and Tips

  • Return types differ by version. guess() returns false on failure; detect() returns null. Always guard with === null / === false — loose comparisons can let through unexpected values.
  • Use getRealPath(), not getClientOriginalName(). UploadedFile::getRealPath() points at the temp file. For stored items, use Storage::path($key); not every driver returns a local path, so remote disks need a download first.
  • finfo is the backbone. Confirm extension_loaded('fileinfo') on your production PHP build. If it's disabled, the package falls back to exif_imagetype / getimagesize, which cover fewer formats and may disagree on edge cases like TIFF or BMP.
  • SVG is a trap. exif_imagetype() does not recognize SVG, and finfo often reports it as text/xml or text/plain. If SVG uploads are allowed, whitelist intentionally, sanitize the XML, and serve it with the correct content type — this package is not an XSS guard.
  • Content sniffing ≠ safety. Detecting image/gif does not mean the GIF is not a decompression bomb or a polyglot. Combine this with size limits, image re-encoding when feasible, and a separate sanitization step for user content.
  • Don't trust $file->getMimeType() alone. Symfony's default uses finfo but can be client-influenced in some configurations; this package gives you an explicit, auditable chain. Prefer it for security-sensitive checks.
  • No config to tune. There are no options to force a backend; if you need deterministic behavior, pin the PHP extension set in your container and add a test that asserts the expected MIME for representative fixtures (JPEG, PNG, WebP, and one renamed-text file).
  • Cache repeated lookups. Each call re-reads the file header; in loops over collections, memoize by path. The filesystem read is the only meaningful cost.
  • Write a failing test. Add a Pest/PHPUnit test feeding a .php file renamed to .jpg through your validation rule and asserting rejection — it documents intent and catches regressions when you bump the package.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast