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...
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.
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.
guess() returns false on failure; detect() returns null. Always guard with === null / === false — loose comparisons can let through unexpected values.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.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.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.$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..php file renamed to .jpg through your validation rule and asserting rejection — it documents intent and catches regressions when you bump the package.How can I help you explore Laravel packages today?