maestroerror/heif-converter
A Laravel/PHP package for converting HEIF/HEIC images into web-friendly formats like JPEG or PNG. It provides a simple API for handling uploads, transforming Apple-style photos, and generating compatible files for storage or display.
Architecture fit. This is not a pure-PHP library. The repository is a Go CLI plus a Docker image wrapping go-libheif (a binding over the native libheif), and the PHP package is essentially a typed convenience wrapper that shells out to that binary or invokes the container. That means the integration adds an out-of-process, CPU-bound transcode step to the application. It fits well where the platform already runs containers and asynchronous workers (Kubernetes/ECS, Laravel queues, S3-backed media pipeline). It fits poorly for shared hosting, bare FPM deployments without the binary, or serverless runtimes with no native libheif. The genuine value proposition is that distro imagick/ImageMagick builds almost never ship a working libheif delegate, so HEIC decode is a recurring gap — this package closes it by bringing its own codec stack. That is a legitimate architectural justification, but it means we are adopting a codec runtime, not a Composer utility.
Integration feasibility. Straightforward at the API surface (composer require, a facade/service, input/output paths), non-trivial at the deployment surface. Two acquisition modes must be validated before commitment: (a) invoke a prebuilt binary installed into the image, (b) invoke a Docker container. Mode (b) is a red flag in production — mounting /var/run/docker.sock into a PHP process is effectively host root and should be rejected in any multi-tenant or PCI-scoped environment. The defensible pattern is a long-lived sidecar/HTTP conversion service, or the binary executed under a locked-down exec wrapper with fixed argv, no shell interpolation, and a hard timeout. Expect per-image latency in the hundreds of milliseconds plus container cold-start if not pre-warmed.
Technical risk.
| Risk | Severity | Notes |
|---|---|---|
| Maintenance abandonment | High | 8 stars, 0 dependents, last release 2023-06-23, single maintainer. Effectively a vendored dependency from day one. |
| Codec drift / CVEs | High | libheif, libde265, libaom move continuously; a pinned 2023 image accumulates known vulnerabilities. We would own rebuilds. |
| Licensing | Medium-High | Package is MIT, but libheif/libde265 are LGPL-3 and an x265/HEVC encoder path may be GPL. Redistribution of the image requires legal review and dynamic-linking discipline. |
| Patent exposure | Medium | HEIC encoding/decoding touches HEVC patent pools. Decode-only, internal-use is usually tolerated; shipping HEIC encoding commercially is a business decision, not an engineering one. |
| Supply chain | Medium | Unverified image provenance, no SBOM, no signed digests. |
| Coupling to a shell-out | Medium | Exit codes, stderr parsing, temp-file lifecycle, and path quoting become our problem. |
Key questions.
Assessment. Viable as an interim capability behind a stable interface; not viable as a strategic, unisolated dependency. Score 0.50 is consistent with "useful, unmaintained, narrow."
Stack fit. Treat this as an infrastructure adapter, not a domain dependency. Define a first-party port — e.g. ImageConverter::toJpeg(string $path, array $opts): ConversionResult — with HeifConverterAdapter as one implementation and ImagickAdapter / ManagedApiAdapter as alternates. No controller, job, or model should reference the package namespace. The adapter owns: binary/container resolution, argv construction, temp-directory allocation, timeout, exit-code mapping to domain exceptions, and metadata preservation policy.
Migration path.
Compatibility. Verify against the actual matrix before committing: PHP 8.2–8.4 (package predates 8.4), Laravel 10/11/12, amd64 and arm64 images, and the container's filesystem layout (storage/app vs /tmp, read-only root filesystems, non-root user). Confirm Composer constraints are not pinned to a Laravel version we plan to leave. Confirm proc_open/exec are not in disable_functions and that open_basedir does not block the binary path or temp dir. Confirm the wrapper surfaces stderr — silently returning a zero-byte file on failure is the most likely production surprise.
Sequencing. Keep the change shippable in slices: adapter + interface (no behavior change) → async job + queue wiring → canary with fallback → remove fallback → open a fork-and-own ticket regardless of outcome. Budget two engineer-days for the spike and one sprint end-to-end including ops hardening.
Maintenance. Assume upstream is frozen. Plan to fork or vendor the Dockerfile and rebuild monthly against refreshed base images and current libheif/libde265 to absorb security patches. Budget roughly 0.1–0.2 FTE steady-state for image rebuilds, CVE triage, and regression testing against the codec corpus. Pin by digest, never by floating tag; publish internal SBOMs; treat binary upgrades as dependency upgrades with a fidelity test gate.
Support. The failure surface spans four layers (PHP → exec → container/runtime → libheif), which lengthens triage. Write a runbook covering: binary/container not found, permission denied, timeout, nonzero exit, empty output, and unsupported variant. Require the adapter to log the resolved command, exit code, and truncated stderr at debug level, and to emit a metric per outcome. Without this, every user report becomes an archaeology exercise.
Scaling. Conversion is CPU-bound and memory-hungry — a 48MP HEIC can transiently consume 100–300MB. Never run it inline on web requests; use a dedicated queue with a bounded worker pool sized to container CPU limits, and cap concurrent conversions per node. Object storage in, object storage out; stream results, do not hold blobs in memory. Watch cold-start cost if using per-request containers, and pre-warm during traffic ramps. Cost model is roughly linear in image count, dominated by CPU seconds — compare against a managed transform API before scaling out.
Failure modes.
Ramp-up. PHP-side ramp-up is roughly half a day for a mid-level engineer. The real cost is operational literacy: containers, codec stacks, and process isolation — call it two to three days for whoever owns the build and runbook, plus a legal/compliance review cycle for LGPL/GPL and HEVC patents that procurement, not engineering, must own. Document the internal adapter contract thoroughly, because upstream has effectively no roadmap and users will read the source, not the README.
Recommendation. Approve a two-day spike behind a first-party interface with mandatory fallback; do not approve a direct dependency in application code; and open the buy-vs-own decision in parallel, since the cheapest outcome is likely discovering an existing licensed transform service already solves HEIC without adding a codec runtime we must maintain ourselves.
How can I help you explore Laravel packages today?