xantios/mimey
xantios/mimey is a PHP MIME type helper for converting file extensions to MIME types and back. It offers a lightweight API for detection and validation, simplifying file upload and content-type handling in Laravel or plain PHP apps.
Install the package and pull in the façade/service provider:
composer require xantios/mimey
If the package ships a publishable config, publish it once so your mapping is versioned in the repo:
php artisan vendor:publish --tag=mimey
Before writing any code, open vendor/xantios/mimey/src and read the main class and the service provider registration. This package is a thin wrapper around Mimey, and the exact public method names have shifted between Mimey versions (e.g. getMimeType/getExtension vs. older getMimeType/getExtensions). Ten minutes of reading the source will save you guessing at the API.
Your first real use case: resolve a MIME type from a file extension so you can set a Content-Type header on a response, or look up the conventional extension for a MIME type you already know:
use Xantios\Mimey\Facades/Mimey; // verify exact namespace in source
$mime = Mimey::getMimeType('csv'); // text/csv
$ext = Mimey::getExtension('image/png'); // png
That's the whole minimal path — no config required, no detection of file contents, no external dependencies to boot.
Header resolution in controllers. The most reliable pattern is using Mimey when you already know the file name or extension and just need the canonical MIME string:
return response()->file($path, [
'Content-Type' => Mimey::getMimeType(pathinfo($path, PATHINFO_EXTENSION)),
]);
This pairs well with storage-backed downloads where you don't want to hit finfo on every request.
Falling back from a stored extension. When users upload a file, persist the extension, then resolve the MIME type at read time. This keeps the DB schema simple and lets you update the mapping without rewriting stored rows.
Custom type registration in a service provider. Register organisation-specific types in AppServiceProvider::boot() rather than in controllers, so the mapping is applied once per request and is testable:
public function boot()
{
Mimey::setCustomMimeType('myext', 'application/x-myext');
}
If the package exposes a config file for custom mappings, prefer that route — it survives config:cache and is visible to other developers without hunting through provider code.
Resolving MIME → extension for generated filenames. Use the reverse lookup to produce a safe download name:
$ext = Mimey::getExtension($record->mime_type) ?? 'bin';
return $record->downloadAs("export.{$ext}");
Enriching validation feedback. Laravel's mimes: rule already validates extensions internally. Mimey is better used after validation to generate friendly messages ("we accept PNG and PDF") or to populate a MIME allowlist config.
Batch lookups. In loops over many files, resolve the class once (app(MimeTypes::class) or the facade) and reuse it. The mapping data is internal; repeatedly resolving from the container is wasteful in bulk imports.
.jpg containing PHP will happily report image/jpeg. Use finfo, File::mimeType(), or Symfony's MimeTypes (which uses magic-byte detection) for any trust decision..jpg → image/jpeg or image/pjpeg). getExtension() returns the first match; don't assume getExtension(getMimeType('x')) === 'x'.null, not exceptions. Always null-coalesce (?? 'application/octet-stream') when writing headers — passing null to a Content-Type header produces a silently wrong response.strtolower(pathinfo($f, PATHINFO_EXTENSION)) before passing in.php artisan config:cache after deploying config changes.composer.json constraint admits your Laravel version, and that auto-discovery works — if Xantios\Mimey\MimeyServiceProvider isn't found in bootstrap/cache/packages.php after composer install, register it manually in config/app.php. Run php artisan package:discover as a first debugging step for "facade root not set" errors.symfony/mime (it ships with Laravel), you have Symfony\Component\Mime\MimeTypes available with a bigger, actively maintained map plus real detection. This package's value is the thin Laravel façade over a small static map; if that façade is all you need, consider whether the dependency earns its place.How can I help you explore Laravel packages today?