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

Qrcode Laravel Package

yakeing/qrcode

yakeing/qrcode is a PHP package for generating QR codes, offering a simple API to create scannable codes from URLs, text, and other data. It fits Laravel and standalone PHP projects that need quick, customizable QR code output.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a standalone PHP QR code generator, not a Laravel package. There is no service provider, no facade, no config publish — Composer will install it and nothing else will happen. Plan for that.

Install and orient yourself, in this order:

composer require yakeing/qrcode
  1. vendor/yakeing/qrcode/composer.json — read the autoload block first. The PSR-4 namespace is the single most important thing here; the README is thin and the class name/namespace is what you actually need to write code.
  2. vendor/yakeing/qrcode/src/ — there is likely one main class (a refactor of the classic phpqrcode library). Read the public method signatures. You are looking for something in the shape of png($text, $outfile, $ecc, $size, $margin) or an equivalent instance API. Confirm before committing to call sites.
  3. README.md — for the one usage example, then ignore it.

First use case: a shareable link, invoice URL, or TOTP enrollment QR rendered on demand. The smallest thing that works, done properly in Laravel:

// routes/web.php
Route::get('/qr/{payload}', QrController::class)->where('payload', '.*');

// app/Http/Controllers/QrController.php
public function __invoke(string $payload)
{
    $tmp = tempnam(sys_get_temp_dir(), 'qr');
    \Yakeing\QRCode\QRCode::png($payload, $tmp, 'M', 8, 2); // verify signature in src/
    $png = file_get_contents($tmp);
    unlink($tmp);

    return response($png, 200, [
        'Content-Type'  => 'image/png',
        'Cache-Control' => 'public, max-age=31536000, immutable',
    ]);
}

Generate to a temp file rather than letting the library write headers — see the Gotchas section on output buffering.

Implementation Patterns

Wrap it before you use it. Even a ten-line wrapper pays for itself here, because this package is archived and you will eventually swap it.

// app/Services/QrCodeService.php
namespace App\Services;

class QrCodeService
{
    public function png(string $payload, array $opts = []): string
    {
        $size   = $opts['size']   ?? config('qr.size', 8);
        $margin = $opts['margin'] ?? config('qr.margin', 2);
        $ecc    = $opts['ecc']    ?? config('qr.ecc', 'M');

        $tmp = tempnam(sys_get_temp_dir(), 'qr');

        try {
            \Yakeing\QRCode\QRCode::png($payload, $tmp, $ecc, $size, $margin);
            return file_get_contents($tmp);
        } finally {
            @unlink($tmp);
        }
    }

    public function dataUri(string $payload, array $opts = []): string
    {
        return 'data:image/png;base64,' . base64_encode($this->png($payload, $opts));
    }
}

Bind it as a singleton in a service provider and inject it. That is the entire integration story — everything below is just usage.

Streaming vs. storing. Two distinct patterns, pick deliberately:

  • Streaming endpoint (route + response($png)): good for user-specific payloads, cache headers do the heavy lifting. Never echo from the library directly.
  • Stored asset (Storage::disk('public')->put("qr/{$hash}.png", $png)): good for email templates, PDF invoices, and anything a mail client or Dompdf has to fetch. Generate the base64 data URI for inline <img src="..."> in Blade mailables — external image URLs are blocked by default in most clients.

Cache by parameter hash. QR output is a pure function of (payload, ecc, size, margin). That makes it trivially cacheable:

public function cached(string $payload, array $opts = []): string
{
    $key = 'qr:' . sha1(json_encode([$payload, $opts]));

    return Cache::remember($key, now()->addDay(), fn () => $this->png($payload, $opts));
}

For high-traffic endpoints, go one step further and point the route at a file on disk, or serve from a CDN — regenerating the same matrix on every request is wasted CPU.

Batch generation goes in a job. If you are producing hundreds of QRs (event badges, ticket PDFs, per-tenant referral codes), do it in a queued job writing to Storage, not in the request cycle. Combine with Bus::batch() and report progress.

Config, not magic numbers. Add config/qr.php with ecc, size, margin, and disk. ECC level is the one knob with real consequences: L gives smaller/denser codes, H survives smudging and logo overlays but inflates the matrix. For screen display, M is the sane default. For print or anything with a center logo, H.

Testing. Don't attempt pixel comparison. Assert on the PNG magic bytes and length:

$this->assertStringStartsWith("\x89PNG\r\n\x1a\n", $service->png('https://example.com'));

Optionally decode with a QR reader library if you need to assert round-tripping, but for most apps the smoke test is enough.

Gotchas and Tips

Output buffering is the trap that will burn you. phpqrcode-lineage libraries frequently call header() and echo inside the generation method when no output file is passed. In Laravel that means corrupted JSON, broken redirects, or headers already sent exceptions. Always pass an explicit output path and read the file back. If you must capture inline output, wrap in ob_start()/ob_get_clean() — but don't; the file approach is clearer.

Long-lived workers and Octane. Static state and any internal caching inside the class can leak across requests under Octane or queue workers. Regenerate per call, or keep the wrapper resolving fresh instances. Test under Octane if you run it.

ext-gd is a hard requirement. Add it to composer.json require and to your Docker image. The class will fatal — not throw a catchable exception — without it. Imagick is not an alternative here.

Tmp paths and permissions. tempnam() respects sys_get_temp_dir(); that's usually fine in containers, but confirm it's writable. If you use storage_path() instead, remember that in a multi-container deploy the file may be written on one node and missed by the next — always go through Storage for anything persisted.

Archived since 2019, zero dependents. Treat this as vendored code you own. Pin the exact version, and strongly consider copying the class into app/Support/QrCode/ with a namespace change so a Composer update, a PHP 8.x deprecation, or an abandoned-package audit can't break you. It is small enough that this is genuinely cheap.

MPL-2.0 specifics. File-level copyleft — linking from a proprietary Laravel app is fine. If you modify the library's files themselves, those modified files must stay under MPL and you must make them available. Keep modifications in your own wrapper or in clearly-separated new files and this is a non-issue; fork the vendor directory and you inherit the obligation.

No facade, no auto-discovery. If you want the ergonomics, write your own QRCode facade pointing at your wrapper class. Don't be surprised that composer require didn't register anything.

Payload hygiene. URL-encode or base64url-encode arbitrary payloads before putting them in a route segment — QR payloads routinely contain ?, &, #, or newlines that will break routing and matrix encoding in confusing ways. Also keep payloads short: dense QR codes at small print sizes become unscannable, and there's a hard capacity ceiling that will throw at generation time rather than degrade gracefully.

Escape hatch / migration path. Given the package is unmaintained and the ecosystem has moved on, evaluate endroid/qr-code (actively maintained, Laravel integration available, SVG + PNG + logo support) or bacon/bacon-qr-code (the low-level renderer endroid builds on). Because everything goes through your QrCodeService, swapping is a one-file change — which is the strongest argument for writing that wrapper on day one rather than calling the static class from twenty controllers.

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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib