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 Analysis Laravel Package

zetacomponents/image-analysis

PHP component from Zeta Components for analyzing image files. It detects MIME type, image format, dimensions, and animation status using GD or ImageMagick backends, making it useful for validating uploads and describing images in PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and verify the API surface first.

composer require zetacomponents/image-analysis

Before writing any code, open the package in vendor/ and read three files in this order:

  1. composer.json — confirms the autoload namespace (older Zeta releases were ezc*; the maintained ports use Zeta\Components\ImageAnalysis) and the required PHP range.
  2. src/Analyzer.php — the only class you will normally touch. Look at analyze(), getMimeType(), getWidth(), getHeight(), getIsImage(), and getData().
  3. src/Handlers/ — the pure-PHP handler is the reason to use this package. It reads image headers without GD or Imagick, so it works in slim containers and serverless runtimes that ship no image extensions.

Minimal first use case — validate an upload without trusting the client:

use Zeta\Components\ImageAnalysis\Analyzer;

$analyzer = new Analyzer();
$analyzer->analyze($request->file('photo')->getRealPath());

if (! $analyzer->getIsImage()) {
    abort(422, 'Not an image');
}

$mime   = $analyzer->getMimeType();   // content-sniffed, not extension-based
$width  = $analyzer->getWidth();
$height = $analyzer->getHeight();

That is the whole value proposition: real MIME + dimensions from file bytes, cheaply, before anything decodes pixels.

Implementation Patterns

Wrap it in a tiny service, not in controllers. The analyzer is stateful (each analyze() overwrites the last result), so instantiate per use and expose an immutable DTO:

final class ImageMeta
{
    public function __construct(
        public readonly string $mime,
        public readonly int $width,
        public readonly int $height,
    ) {}
}

final class ImageInspector
{
    public function inspect(string $path, ?string $mimeHint = null): ImageMeta
    {
        $a = new Analyzer();
        $a->analyze($path, $mimeHint);

        return new ImageMeta($a->getMimeType(), $a->getWidth(), $a->getHeight());
    }
}

Bind it as bind (not singleton) in a service provider — see Gotchas for Octane.

Use it as a validation backstop. Laravel's image rule and mimes: rely on the client-declared type plus an extension guess. Combine them with a real content check behind the scenes:

class ImageMetadataRule implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        if (! $value instanceof UploadedFile || ! $value->isValid()) {
            $fail('Invalid upload.'); return;
        }

        try {
            $a = new Analyzer();
            $a->analyze($value->getRealPath());
        } catch (\Throwable) {
            $fail('Unreadable image.'); return;
        }

        if (! $a->getIsImage()) {
            $fail('The file is not a valid image.'); return;
        }
        if (! in_array($a->getMimeType(), ['image/jpeg', 'image/png'], true)) {
            $fail('Unsupported image type.'); return;
        }
        if ($a->getWidth() > 8000 || $a->getHeight() > 8000) {
            $fail('Image dimensions exceed the limit.');
        }
    }
}

Gate expensive work. Run the inspector before dispatching an Intervention\Image job or an S3 conversion. It costs microseconds and prevents a 200 MP decompression bomb from killing a queue worker.

Cache by content hash. Metadata for an immutable object never changes:

$key = 'imgmeta:'.hash_file('xxh128', $path);
$meta = Cache::remember($key, 86400, fn () => app(ImageInspector::class)->inspect($path));

Pass a MIME hint when you already know it. The second argument to analyze() skips sniffing and short-circuits to the right handler, which is useful if you have already filtered the upload by extension server-side.

Gotchas and Tips

  • Check the license, not the registry. The registry reports NOASSERTION, but the Zeta Components source is Apache-2.0. Read LICENSE in vendor/ before shipping this in a commercial product; keep a copy of the notice.
  • Treat it as a vendored utility, not a dependency. 3 stars and 0 dependents means no upstream momentum. Pin the exact version in composer.json, add your own feature tests around the formats you accept, and be prepared to fork. Run the suite on your target PHP version first — PHP 8.1+ will surface deprecations in older Zeta code.
  • Do not use it for NSFW/nudity detection. Historic Zeta documentation advertised flesh-tone heuristics; they are unreliable and will not satisfy any moderation or compliance obligation. Use a hosted moderation API for that.
  • EXIF orientation is ignored. A phone JPEG stored landscape with an Orientation=6 tag is reported with swapped width/height. If the number is user-facing or feeds a crop, read orientation separately via exif_read_data() or let Imagick auto-orient.
  • Headers lie on truncated files. A partially uploaded or hand-crafted file can yield a plausible MIME with bogus dimensions. Wrap analyze() in try/catch and treat any exception as a validation failure, not a 500.
  • Never pass a URL. analyze() accepts a path that may go through stream wrappers; a user-supplied value is an SSRF vector. Always hand it $file->getRealPath() or Storage::path(...).
  • Storage::fake() returns a different root. UploadedFile objects created by UploadedFile::fake()->image() do not necessarily sit where your storage disk does. For integration tests, copy real fixture images into place and assert against those, not fake-generated ones.
  • Format coverage varies by version. WebP, AVIF, and HEIC support depends on the bundled handlers. Confirm each format you accept actually returns getIsImage() === true before you rely on it; otherwise fall back to getimagesize() for those cases.
  • Statefulness and Octane. Because analyze() mutates the instance, do not resolve the Analyzer as a singleton in a long-running worker or Octane process; a reused instance across requests is a subtle cross-contamination bug. bind or new is correct.
  • Extension point: the handlers implement a common interface. If you need a format the pure-PHP handler does not cover, supply a custom handler backed by Imagick or finfo rather than patching the package in place.
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.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource