grofgraf/laravel-pdf-merger
Laravel PDF Merger offers a simple API for combining multiple PDF files into a single document within Laravel apps. It supports stored or uploaded PDFs and integrates with Laravel's filesystem for straightforward document assembly.
Architecture fit. The package addresses a single, bounded capability: concatenate N PDFs into one file, preserving input order. Architecturally that is a leaf utility, not a cross-cutting concern — it belongs behind an application service, called from a queue job or a controller action, and should never appear in domain models or leak into the request lifecycle. Its footprint in the codebase should be one adapter class and one interface. If the integration is larger than that, the design is wrong.
Note a diligence gap up front: the supplied assessment-pm.md describes service providers, publishable config, migrations, and artisan commands. A PDF merger has no business shipping migrations, and the assessment reads as a generic Laravel-package template rather than a review of this repository. Treat that document as unvalidated; the real surface is likely a single service-provider registration, a facade or container binding, and a thin wrapper around a third-party merge library (probably iio/libmergepdf or similar). Confirm the actual public API before planning against it.
Integration feasibility. Low effort, high uncertainty. The integration itself is an afternoon: composer require, bind the provider, write the adapter, feed it file paths. The uncertainty is entirely in the Composer solve. Last release is 2019-03-28 — the Laravel 5.8 / PHP 7.x era. Modern Laravel (10/11) requires PHP 8.1+, and a 2019 package will almost certainly pin illuminate/support and php constraints that conflict. If it installs at all, it installs because the constraints are permissively loose, which is its own risk: the package has never been tested against the framework version you are running.
Technical risk. Ranked:
| Risk | Severity | Notes |
|---|---|---|
| Abandonment / framework drift | High | Single maintainer, no release in ~6 years, 28 stars. No Laravel 9/10/11 support evidence. |
| Transitive dependency decay | High | The underlying PDF library carries the real code and the real CVEs. composer audit will flag whatever it drags in. |
| Composer constraint conflict | Medium-High | Blocks install on current Laravel; workaround is a fork or a version pin, both of which are permanent maintenance. |
| Runtime failure modes | Medium | Memory exhaustion on large merges, encrypted/password-protected inputs, malformed PDFs, page-rotation and form-field handling. Verify empirically. |
| Bus factor | Medium | One author, no evidence of test coverage or CI. |
| Licensing | Low | MIT, compatible with commercial and self-hosted distribution. The one clean signal. |
Key questions for the spike (timeboxed to 1–2 days):
composer.json actually constrain, and does it resolve against our Laravel/PHP versions?UploadedFile, base64? Does it return a path, a stream, or write in place?The honest read on the score (6.14 overall, 6.75 opportunity): the opportunity is real — merging is table stakes, and buying it is correct — but this specific artifact is a stale wrapper over a commodity capability. The decision the package informs is not "adopt or build," it is "wrap this wrapper, or go one level down to the underlying library and skip the abandoned layer."
Stack fit. Idiomatic Laravel only. No new infrastructure, no sidecar, no daemon, no new datastore. It runs in-process in the existing PHP-FPM or queue worker. That is the package's strongest property: it does not add an operational surface, and it does not change how we deploy, containerize, or monitor. It fits any Laravel app; it fits nothing else.
Migration path. Do not install it into main. Sequence:
composer require grofgraf/laravel-pdf-merger against our current Laravel/PHP versions and record the result. A hard conflict here ends the evaluation and we go directly to the underlying library or setasign/fpdi/qpdf. Budget: half a day.App\Contracts\PdfMerger with merge(array $paths): string (returns a path or stream) and a PdfMergerAdapter implementation. Every caller — controllers, jobs, exporters — depends only on the interface. This is non-negotiable regardless of which implementation wins, and it is what makes the eventual swap a one-file change.iio/libmergepdf (likely the package's own dependency) or shell out to qpdf/Ghostscript via a queued job. Same interface, comparable effort, better maintenance posture.Compatibility. Two axes matter. Framework axis: the package must tolerate our Laravel major and PHP minor, and we must accept that it will not be updated when we upgrade — budget the wrapper's replacement into any framework-upgrade epic. Input axis: verify behavior with the actual files our product produces, not with synthetic fixtures only. Invoices and generated reports from our own PDF pipeline are the ones that will break it.
Sequencing. Ship the interface and adapter before any feature that needs merging. Merging is almost always a downstream step in a document-assembly workflow (generate → merge → store → notify), so land it early in the milestone and keep it off the critical path to the first release. If merging is on the critical path, prefer the system-binary approach (qpdf) for the first release: it is boring, battle-tested, and has no Composer constraints.
Maintenance. The dominant cost is not integration, it is ownership. No upstream releases since 2019 means no security patches, no PHP 8.x fixes, and no Laravel compatibility work. If a transitive dependency gets a CVE, composer audit will fail our build and there will be no upstream fix — we patch a fork or rip it out. Budget that as a known liability, not a surprise. The mitigation is containment: one interface, one adapter class, pinned versions, and a golden-file test suite that proves the swap is safe.
Support. Bus factor is one, and that author has not shipped a release in six years. Issue triage, PR review, and CVE response should be assumed to be zero. The team that integrates this also owns it. Communicate that to stakeholders plainly: we are adopting code, not a support relationship.
Scaling. Merging is CPU- and memory-bound in PHP and does not scale horizontally without care. For anything beyond a handful of small files, run merges in queue workers, not in the request cycle, with explicit memory limits and timeouts. Large documents will consume memory proportional to total input size; verify whether the library streams to temp files or buffers. Concurrency is limited by worker count, not by the package — which is fine, because document merging is almost never a high-QPS path. It is a throughput problem, and queues solve it.
Failure modes to instrument:
Emit an event on every merge with input count, total bytes, duration, and outcome. That is three fields of instrumentation and it is the difference between "merging is flaky" and "merging fails on files over 80 MB with embedded fonts."
Ramp-up. Trivial for anyone fluent in Laravel — under a day to read the API, write the adapter, and get the golden-file suite green. The ramp-up cost is not learning the package; it is the eventual un-learning, i.e., the half-day refactor when the dependency is replaced. That cost is bounded and acceptable precisely because the interface exists.
Recommendation to stakeholders. Do not budget this as a build-vs-buy decision — the "buy" saves perhaps one to two days of wiring, not weeks. Budget it as a one-day spike whose output is a decision: adopt the wrapper if it installs cleanly and passes the fixture suite; otherwise go one level down to the underlying merge library or a system binary behind the same interface. Either way, the interface and the golden-file tests are the durable assets; the package is interchangeable. Adopt behind the adapter, pin the version, and set an explicit revisit trigger at the next Laravel major upgrade.
How can I help you explore Laravel packages today?