2amigos/qrcode-library
A PHP QR code generation library from 2amigos. Build customizable QR codes with configurable size, colors, margins, and error correction, then output them in common image or vector formats. Its simple object-oriented API works well in Laravel and other PHP apps.
Architecture fit. This is a QR-code rendering library with framework bridges, not a domain service. In a Laravel codebase its footprint should be a thin adapter at the edge of the app (PDF/invoice generation, ticket issuance, 2FA enrollment, vCard/MeCard contact sharing). The 4.0 reorganisation of adapters into Da\QrCode\Bridge\<Framework>\ (Yii2 / Laravel / PSR) is architecturally the right move: the core becomes framework-agnostic and the Laravel piece becomes a swappable shim. That reduces lock-in and means a future framework migration or an extraction of QR generation into a shared internal service is cheap.
Integration feasibility. High, with one hard gate. The headline change — pure-GD GdImageBackEnd, PNG/JPG without ImageMagick — removes the single most common deployment blocker for this class of package (the Windows RegistryKeyLookupFailed 'CoderModulesPath' error is a symptom of the same class of problem: a native binary dependency the platform can't satisfy). For a containerised Laravel estate this converts a "needs a custom base image" item into "needs ext-gd", which most PHP images already ship. The PSR-15 QrCodeAction bridge is only relevant if we have (or plan) a non-Laravel service — worth noting as optional upside, not a reason to upgrade.
Technical risk. Medium — a genuine major with several independent break vectors:
Bridge\Laravel\, with redundant Laravel class-name prefixes dropped. Any use statement, config file, or container binding referencing old FQCNs breaks at runtime, not at install time.QrCode::getGradientType() returning a GradientType instead of a scalar is the sneakiest break: string comparisons, array keys, cache keys, and JSON serialisation can fail silently or produce wrong output rather than throwing. StyleManager::getGradientTye() keeps a deprecated alias, so the typo path is safe; the return-type change is not.bacon/bacon-qr-code ^3, khanamiryan/qrcode-detector-decoder ^2). Any transitive or first-party code that pinned these directly will conflict, and bacon-qr-code v3 is itself a major.Key questions (blocking unless answered):
composer.json before planning.--with-freetype)? Label/text rendering and vCard photo compositing will fail without it, and the failure may be at render time rather than boot.getGradientType() as a string, or persist it?PHOTO and MeCard ORG capabilities, or are they inert?Stack fit. Strong for a Laravel shop. The package presents as a facade/factory plus publishable config, so it slots into existing rendering pipelines without new infrastructure. The one infrastructural requirement is confirming ext-gd is compiled with FreeType in both CI and production images — a one-line Dockerfile assertion and a smoke test in CI is the cheapest possible mitigation. Note the fixed bug where Bridge\Laravel\QrCodeFactory works standalone when config() is unbound: that materially improves testability outside a booted Laravel container (unit tests, artisan-less scripts, queue workers in odd bootstraps), which is worth exploiting rather than just tolerating.
Migration path (3.x → 4.0).
UPGRADE-4.0.md and CHANGELOG.md in full; treat the upgrade guide as the source of truth over the release blurb.grep for Da\QrCode\Laravel, getGradientTye, getGradientType, direct Writer/backend construction, and any direct bacon/bacon-qr-code usage.composer why-not / composer update --dry-run in a branch to surface constraint conflicts before touching code.Compatibility. Two independent compatibility surfaces must both clear: PHP (8.3+ only) and Laravel/Illuminate (unconfirmed). The PHP floor is the long-lead item and should be sequenced first — it may already be satisfied, or it may pull in a broader runtime upgrade that this package merely rides along with.
Sequencing. Do not bundle this with feature work. Order: (a) confirm PHP/Laravel compatibility and GD+FreeType availability; (b) upgrade in isolation behind the existing test suite plus new snapshot coverage; (c) adopt net-new capabilities — vCard PHOTO, MeCard ORG, PSR-15 bridge — as separate, product-led follow-ups once we're stably on 4.x. The PSR-15 bridge in particular is only worth adopting if there's a concrete requirement to serve QR codes from a non-Laravel endpoint.
Maintenance. Ongoing cost is low: one composer dependency, a small API surface, and a stable upgrade guide. The real maintenance lever is our own usage discipline — centralise QR generation behind a single internal service/wrapper rather than calling the facade from a dozen places. That wrapper is what makes the next major (and there will be one) a one-file change. Toolchain modernisation (PHPStan, php-cs-fixer, multi-version CI) is a meaningful positive for long-term health and reduces the odds of a surprise unmaintained dependency.
Support. The PHP 8.3+ floor is effectively an upstream support-window alignment — it puts us on a supported PHP line and gives roughly a two-year runway before the next forced move. The trade is that the package dictates our minimum runtime; that decision should be owned by the platform team, not inherited silently from a QR library upgrade.
Scaling. QR generation is CPU-bound and short-lived, with no external I/O in the GD path — it scales horizontally without ceremony. GD is generally lighter than spawning ImageMagick, which is a net win for throughput and container size. The practical scaling control is caching, not compute: QR output is a pure function of payload + style, so cache rendered PNGs keyed by a hash of those inputs (object storage or HTTP cache) rather than generating per request. Watch per-request memory when combining large QR codes with embedded imagery (vCard PHOTO is the new risk here — base64/data-URI payloads inflate both input size and peak memory), and set explicit memory limits.
Failure modes to plan for.
ext-gd — fatal or degraded rendering at runtime, not install time. Mitigate with a CI smoke test that renders a PNG with a text label.GradientType object reaching a cache key, log context, or JSON response produces garbage rather than an exception.Ramp-up. Low for a Laravel developer: the facade surface is a handful of methods, and the README-level concepts (payload type, size, margin, style) are self-evident. Realistic estimate: half a day for one engineer to read the upgrade guide, do the migration, and validate; one to two days of QA concentrated almost entirely in visual/snapshot verification and any place we store or serve rendered images. The asymmetric cost is not learning the package — it's the PHP 8.3+ platform prerequisite and the visual-regression triage, both of which should be scoped and owned before this becomes someone's sprint ticket.
How can I help you explore Laravel packages today?