marc1706/fast-image-size
Fast, dependency-free PHP library that reads image dimensions from file headers without loading the whole file, supporting JPEG, PNG, GIF, BMP, WebP, and more. A quick drop-in alternative to getimagesize for speed and low memory use.
Architecture fit
marc1706/fast-image-size is a framework-agnostic PHP utility, not a Laravel package — no service provider, facade, config, or published assets. That is a scoping fact the TPM should set expectations around: the library delivers a single capability (derive width/height/type from a local path or remote URL by reading header bytes only), and the integration surface — container binding, HTTP client wiring, cache adapter, SSRF guard — is work we own. Architecturally it is a thin, leaf dependency that slots behind an application service (ImageDimensionResolver / ImageDimensions) and does not constrain domain design. It fits naturally into media-ingestion jobs, URL import flows, and backfill commands. The correct framing to stakeholders is "small single-purpose dependency plus a thin adapter we maintain," not "install and configure a package."
Integration feasibility
Low effort, high certainty. composer require marc1706/fast-image-size plus a PSR-18 client (Guzzle, already present in most Laravel stacks) and a Laravel-cache-backed adapter. Expected engineering effort is hours-to-a-day for the happy path; the real cost is policy work — host allow-listing, timeouts, retry budgets, cache TTL and invalidation. Delivery risk is concentrated in security review and test coverage, not in the library itself. Because it is a leaf dependency with a small API (one entry class), it can be adopted incrementally behind a feature flag rather than as a platform-wide commitment.
Technical risk
169.254.169.254 is the classic failure). This requires application-security sign-off before any untrusted-URL surface ships.HEAD/range requests and returning accurate Content-Type/Content-Length. Misconfigured CDNs and hotlink protection yield inconsistent results — expect a long tail of "unknown dimensions" per origin rather than a clean pass/fail.null/false rather than throwing. Callers must handle "unknown" explicitly or we ship nulls and broken aspect ratios into the UI. This is a product decision (fallback strategy), not just an engineering one.200 responses, so non-200 responses carrying bytes are now parsed. This is a net win for 206 Partial Content (range requests are exactly the happy path) and signed-URL/redirect chains, and should reduce failed imports and support tickets. The corresponding risk: a 403/404/500 that happens to include bytes may now parse "successfully," so "probe succeeded" no longer implies "the URL is healthy." Any dead-link sweep, retry policy, or alerting that treats failure as a signal must instead validate HTTP status at our call site. Regression risk is low but the test matrix must explicitly cover 206, redirect, 404-with-body, and HTML error pages.Key questions
^1.3 now, and who owns the quarterly upstream review and the upgrade-as-tested-event cadence?Stack fit Works on any Laravel version on PHP 7.4+/8.x with Composer 2, with or without GD/Imagick (no extension requirement is a genuine deployment simplification for containerized or restricted environments). Pairs naturally with Guzzle for HTTP and Laravel's cache repository for the caching interface the library exposes. No queue, console, or asset-publishing integration beyond what we author. It belongs in the service layer and in jobs/Artisan commands for backfills — not scattered across controllers.
Migration path
Greenfield adoption, no database or config migration. Install, bind the HTTP client and cache adapter in a MediaServiceProvider, wrap in one resolver service with a single method (dimensionsFor(string $url): ?array), and expose that as the only entry point. If we currently call getimagesize() on already-downloaded files, this is a drop-in replacement for the remote-URL case only — local files should stay on native functions, which are faster and carry no network dependency. Backfilling width/height/type columns across an existing S3/CDN/legacy library is cheap enough to run as a batch job and is a strong candidate for an early, visible win.
Compatibility
v1.3.1 is backwards compatible; no API surface was removed. Our integration must not depend on the prior "only 200" behaviour in either direction, and anything that infers URL health from probe success must be refactored to check status explicitly. Pin to ^1.3 and treat minor upgrades as deliberate, tested events — not automatic merges — given the single-maintainer cadence.
Sequencing
200, 206, redirect chains, 404/503, HTML-body responses, and truncated/oversized payloads — this is the regression guard for the v1.3.1 semantics.<img> sizing once the resolver's miss rate is understood in production.Maintenance
Low code footprint, low ongoing cost — but a single-author dependency with infrequent releases means bus-factor risk is the primary maintenance concern. Mitigation: keep the wrapper thin enough that replacement is a day's work; getimagesize() on a ranged download and a Guzzle-based header parse are both credible fallbacks. Budget a light quarterly upstream review; the v1.3.1 patch is exactly the kind of small change that still deserves a changelog read. Total cost of ownership is effectively the cost of the adapter we write, not the library.
Support
Failures present as "dimensions unknown," surfacing as missing aspect ratios or placeholder sizing rather than exceptions — so support burden lands on the feature owner, not infrastructure, and rarely escalates to an incident. Document the null contract explicitly and ship a product-defined fallback (default aspect ratio, lazy client-side measurement) so a resolver miss is a degraded experience. The expected v1.3.1 benefit — fewer false negatives against CDNs, object stores, and signed URLs returning 206 — should measurably reduce "could not determine image size" tickets on import flows. The trade-off is that a dead-link cleanup or retry workflow keyed on probe failure will silently stop working; that logic must move to explicit status checks, and the owning team should be told so.
Scaling Cost is network egress and latency, not CPU — each lookup is one or more outbound HTTP requests reading a few hundred bytes. At volume, cache aggressively keyed on URL, prefer queueing for bulk imports and backfills, and add a per-host concurrency cap plus a circuit breaker so a slow or hostile origin cannot exhaust PHP-FPM workers. Caching also materially reduces blast radius when an upstream origin goes dark, and keeps lookups off the synchronous request path wherever possible.
Failure modes
HEAD → full download or null; watch egress and add per-lookup byte caps.Ramp-up
A mid-level Laravel engineer can own this in under a day: the library is one class, and the surrounding work (DI bindings, Guzzle config, cache adapter, SSRF guard) uses patterns already established in the codebase. The genuinely non-obvious areas are PSR-18 client wiring and the untrusted-URL security posture, which application security should review before the first external-input surface ships. Plan for roughly one engineer-day of build, a half-day of security review, and a half-day of test authoring around the 206/error-body matrix — a small, low-risk increment that unblocks media metadata, URL import, and aspect-ratio-driven rendering work.
How can I help you explore Laravel packages today?