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

Media Type Laravel Package

innmind/media-type

A PHP library for working with media types (MIME types): parse, validate, and compare content types, handle parameters, and map file extensions. Lightweight and framework-agnostic, it helps Laravel and PHP applications negotiate HTTP responses, validate uploads, and manage content-type headers relia...

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require innmind/media-type

There is no service provider, no config file, and no facade — it is a plain value-object library. If you are on Laravel 10/11 you only need to confirm the PHP constraint in composer.json is satisfied; nothing is auto-discovered.

Where to look first

  1. src/ — the public surface is small (typically a single MediaType class plus an exception type). Read it end to end; it will take ten minutes and is more reliable than the README.
  2. tests/ — this is where the actual contract lives: which strings are accepted, how parameters are handled, whether comparison is case-sensitive.
  3. CHANGELOG / tags — with 0 dependents and 0 stars, treat every minor release as potentially breaking and pin the version ("innmind/media-type": "^1.0" rather than *).

First use case

The highest-value entry point is validating a media type that arrives from your own system or from a trusted header — not from an end user:

use Innmind\MediaType\MediaType;

$type = MediaType::of('application/vnd.api+json');

(string) $type;      // normalized string form

Wire it into a form request or a small validation rule and stop comparing MIME types with === across your codebase:

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Innmind\MediaType\MediaType;

final class MediaTypeRule implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        if (!is_string($value)) {
            $fail("The {$attribute} must be a media type string.");
            return;
        }

        try {
            MediaType::of($value);
        } catch (\Throwable) {
            $fail("The {$attribute} is not a valid media type.");
        }
    }
}

Implementation Patterns

Keep it at the edges, not in your models. The value object earns its keep when it converts untrusted strings into a guaranteed-valid representation once, then flows through your domain as a typed value. Parse on input, serialize on output.

Custom Eloquent cast (store as string column).

final class MediaTypeCast implements CastsAttributes
{
    public function get($model, $key, $value, $attributes): ?MediaType
    {
        return $value === null ? null : MediaType::of($value);
    }

    public function set($model, $key, $value, $attributes): ?string
    {
        return $value === null ? null : (string) $value;
    }
}

Because get() throws on corrupt rows, either wrap it in a try/catch returning null, or validate at write time and treat a throw here as a genuine data-integrity alarm.

Comparison instead of string equality. The whole point is that text/html; charset=UTF-8 and text/html;charset=utf-8 are the same media type. Centralize that:

if ($stored->equals($incoming)) { /* ... */ }

Then add a single MediaType::of() call site per boundary and delete the scattered strtolower(explode(';', $header)[0]) helpers.

Content negotiation for API responses. Split Accept on , yourself, strip q= weights (this package models one media type, not a weighted list), then use the package to normalize and compare:

$accepted = collect(explode(',', $request->header('Accept', '*/*')))
    ->map(fn (string $part) => trim(explode(';', $part)[0]))
    ->filter()
    ->map(fn (string $raw) => rescue(fn () => MediaType::of($raw), null, false))
    ->filter();

$supportsJsonApi = $accepted->contains(
    fn (MediaType $t) => $t->equals(MediaType::of('application/vnd.api+json')),
);

File uploads — the important caveat. Use the package to model what the client claims ($file->getClientMimeType()), and use finfo/mime_content_type() or $file->getMimeType() for what the file actually is. Validate the sniffed value with the package; never trust the claimed one. A pattern:

$declared = rescue(fn () => MediaType::of($file->getClientMimeType()), null, false);
$actual   = MediaType::of($file->getMimeType()); // from the filesystem, not the request

abort_unless($actual->equals(MediaType::of('image/png')), 422);

Adapters over inheritance. The classes are almost certainly final and immutable. Do not try to extend MediaType to add toExtension() or isImage(); write small helper classes/functions that accept a MediaType and delegate. That also keeps the vendor type out of your public APIs if you later swap libraries.

Symfony/HttpFoundation interop. $request->getContentTypeFormat() and Response::prepare() already do their own thing. Do not mix them with this package in the same code path or you will get two different notions of "the content type" that disagree on parameters.

Gotchas and Tips

  • It validates syntax, not truth. MediaType::of('application/octet-stream') and MediaType::of('foo/bar') both succeed — the package cannot know whether a type is registered with IANA. If you need a whitelist, keep your own enum/config array and check membership after parsing.
  • Accept is a list, not a type. The most common misuse is passing a full Accept header (text/html, application/json;q=0.9) into the constructor. That is not a media type; it will throw (or, worse, silently parse something you did not intend). Split first.
  • Parameters are the sharp edge. Per RFC, parameter names are case-insensitive, parameter values may not be (and quoted strings can contain ;). Verify in tests/ how MediaType treats charset, quoted values, and duplicate parameters before you build equality logic on top of it.
  • Equality semantics. Decide explicitly whether text/html equals text/html; charset=utf-8 for your use case. If the package's equals() is parameter-sensitive, comparing a stored type (no charset) against an incoming one (with charset) will silently return false. Normalize by comparing string forms after stripping parameters when you only care about the type/subtype.
  • Exceptions on get() casts. A bad row in the database will throw during model hydration, which surfaces as a 500 far from the cause. If you inherit legacy data, sanitize the column before enabling the cast.
  • Case handling. Type and subtype are case-insensitive; the package likely normalizes on construction. Do not assume (string) $mediaType === $input — always compare through the object, and never use the raw input as a cache key or DB unique index without normalizing.
  • Not a replacement for symfony/mime, league/mime-type-detection, or ralouphie/mimey. Those map extensions ↔ MIME types. This package only models the string. You will likely need both in a real app, and that is fine.
  • Zero dependents means zero field reports. Budget for reading the source rather than searching for answers; there is no Stack Overflow corpus. Write a thin wrapper (App\Support\MediaTypes) exposing just the three or four operations you use, so a breaking upstream change is a one-file fix.
  • Debugging tip: when a MediaType::of() call throws, log the raw input with json_encode() so trailing whitespace, NBSP, or a stray ; is visible — those are the cause 90% of the time.
  • Extension point is effectively "none." Treat the vendor type as a leaf dependency; your composition layer is the extension point.
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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route