sushidev / php-pdf-concater
Merge PDFs from PHP with a fast Rust engine.
Requires
- php: ^8.2
- composer-runtime-api: ^2.2
- ext-json: *
Requires (Dev)
- league/flysystem-aws-s3-v3: ^3.0
- orchestra/testbench: ^10.0
- phpunit/phpunit: ^11.5
Suggests
- ext-pdf_concater: Optional native PHP backend; otherwise the Rust executable is used.
- illuminate/support: Optional Laravel integration, tested with Laravel 12 and 13.
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-31 22:55:00 UTC
README
Merge PDFs from PHP or Laravel with a fast Rust engine.
Requirements
PHP 8.2+, Composer 2.2+, and either a prebuilt executable or Rust for the initial build.
The executable backend uses PHP's proc_open. Build for the OS and architecture
running PHP; Linux and macOS are supported.
Quick start
From this checkout:
composer install composer build php bin/pdf-concater php/tests/Fixtures output/merged.pdf
This creates a two-page PDF from the included test fixtures. Replace
php/tests/Fixtures with your own PDF directory. Keep the output outside that directory.
composer build compiles the Rust executable once. It does not install a PHP
extension or change PHP configuration. A prebuilt executable
can be copied to production; Rust is only needed when building.
Use in PHP
<?php require __DIR__ . '/vendor/autoload.php'; use SushiDev\PdfConcater\Merger; $result = (new Merger())->merge( [__DIR__ . '/first.pdf', __DIR__ . '/second.pdf'], __DIR__ . '/output/merged.pdf', ); echo "Merged {$result['pages']} pages.\n";
For an entire directory:
$result = (new Merger())->mergeDirectory('/path/to/pdfs', '/path/to/merged.pdf');
File lists keep their supplied order; directories are sorted by filename.
Lossless compression is enabled by default. Errors throw
SushiDev\PdfConcater\MergeException. A failed merge preserves an existing output.
Install in an existing application
Once the code has been published to the repository's master branch:
composer config repositories.pdf-concater vcs https://github.com/sushidev-team/php-pdf-concater composer require sushidev/php-pdf-concater:dev-master vendor/bin/pdf-concater-build
Composer installs the PHP library; the last command builds the Rust executable. For installation from your local checkout before publication, see local installation.
Laravel integration
The package registers its service provider automatically. Use the facade:
use SushiDev\PdfConcater\Facades\PdfConcater; $result = PdfConcater::merge($files, storage_path('app/merged.pdf'));
Inject SushiDev\PdfConcater\Laravel\PdfConcater into a controller, job or service
to use the same service as the facade.
Read PDFs from a Laravel Storage disk and choose the destination disk:
$result = PdfConcater::mergeFromDisk( ['invoices/first.pdf', 'invoices/second.pdf'], 'exports/combined.pdf', disk: 's3', outputDisk: 'archive', );
Local disks use direct file paths. Remote disks use streamed downloads and uploads with temporary files, keeping document contents out of the PHP heap. See Storage disks and streaming for configuration, downloads and performance metrics.
Publish the optional configuration:
php artisan vendor:publish --tag=pdf-concater-config
Configure PDF_CONCATER_BINARY, PDF_CONCATER_THREADS, PDF_CONCATER_BATCH_SIZE
and PDF_CONCATER_COMPRESS in .env. See the Laravel configuration guide.
Laravel example
The included Laravel 13 application has a small PDF upload and download page. Use PHP 8.5 for the supplied example lock file.
cd examples/laravel-app
composer run setup
composer run dev
On a fresh checkout, setup installs dependencies, prepares .env, generates
the application key and builds Rust. Run it once; use composer run dev for
subsequent starts. Open http://127.0.0.1:8000 and choose at least two PDFs.
No database migration or frontend build is needed.
To merge a large directory from Laravel:
php artisan pdf:merge /path/to/pdfs /path/to/merged.pdf --json
Tests and CI
composer test
The GitHub Actions workflow runs PHP, Laravel and Rust tests on pushes and pull requests. Its coverage gates require 100% executable-line coverage of the PHP classes, Laravel example application and runtime Rust sources, including the CLI and native PHP boundary. Reports and caches are ignored by Git.
See testing and coverage for the full local coverage command, requirements, scope and CI checks.
Benchmarks
make benchmark # 10,000 small PDFs make benchmark-250kb # 10,000 PDFs, exactly 250 KB each make benchmark-corpus COUNT=100 RUNS=3 # Pinned real-world PDFs
The targets build Rust, prepare or reuse the datasets, and measure time and peak RAM. See benchmark options.
More information
- Installation, deployment and configuration
- Laravel example
- Optional native PHP extension
- Prebuilt executable
- Storage disks and streaming
- Benchmarks
The merger combines pages and their inherited resources. It does not combine bookmarks, document metadata, attachments or form definitions. Encrypted PDFs and interactive forms are rejected. Performance depends on the actual PDF contents; see the benchmark notes before relying on the synthetic results.
License
MIT · Copyright © 2026 Sushi Dev GmbH.