A Symfony bundle providing context-aware PDF generation with swappable adapter backends.
php artisan boost:add-skill sylius/pdf-generation-bundle
Save this content to: AGENTS.md
# SyliusPdfGenerationBundle - Agent Guide
A Symfony bundle providing context-aware PDF generation with swappable adapter backends.
## What This Bundle Does
Converts HTML (or Twig templates) into PDF files. The caller picks a **context** (e.g. `invoice`, `coupon`), and the bundle routes the request to the correct **adapter** (e.g. KnpSnappy/wkhtmltopdf, Dompdf). Each context can use a different adapter with different options.
## Key Interfaces
```
TwigToPdfRendererInterface::render(string $template, array $parameters = [], string $context = 'default'): string
```
High-level entry point. Renders a Twig template to HTML, then delegates to HtmlToPdfRenderer.
```
HtmlToPdfRendererInterface::render(string $html, string $context = 'default'): string
```
Takes raw HTML, selects the adapter for the given context from a ServiceLocator, and returns PDF bytes.
```
PdfGenerationAdapterInterface::generate(string $html): string
```
Low-level adapter interface. Takes HTML, returns PDF bytes. Adapters are context-unaware.
```
OptionsProcessorInterface::process(object $generator, string $context = 'default'): void
```
Configures the underlying PDF generator object (e.g. `Knp\Snappy\AbstractGenerator` or `Dompdf\Dompdf`) before PDF generation. Multiple processors run in sequence via `CompositeOptionsProcessor`.
```
GeneratorProviderInterface::get(?string $context = null): object
```
Provides the underlying generator instance (e.g. a `Knp\Snappy\Pdf` or `Dompdf\Dompdf`). Used by adapters to get a fresh or shared generator per request.
```
PdfStorageInterface::save(PdfFile $file): void
PdfStorageInterface::remove(string $filename): void
PdfStorageInterface::has(string $filename): bool
PdfStorageInterface::get(string $filename): PdfFile
PdfStorageInterface::resolveLocalPath(string $filename): string
```
Context-unaware storage contract. Each instance handles one location. `resolveLocalPath()` returns an absolute local filesystem path — for `SymfonyFilesystemPdfStorage` this is the existing path; for Flysystem/Gaufrette it lazily materializes a local copy in the configured `local_cache_directory`. Implementations: `FlysystemPdfStorage` (default), `SymfonyFilesystemPdfStorage`, `GaufrettePdfStorage`.
```
PdfFileManagerInterface::save(PdfFile $file, string $context = 'default'): void
PdfFileManagerInterface::remove(string $filename, string $context = 'default'): void
PdfFileManagerInterface::has(string $filename, string $context = 'default'): bool
PdfFileManagerInterface::get(string $filename, string $context = 'default'): PdfFile
PdfFileManagerInterface::resolveLocalPath(string $filename, string $context = 'default'): string
```
Manages PDF file persistence. `PdfFileManager` delegates to per-context `PdfStorageInterface` instances via a ServiceLocator.
```
PdfFileGeneratorInterface::generate(string $filename, string $content, string $context = 'default'): PdfFile
```
Wraps already-rendered PDF content into a `PdfFile` model and saves it via `PdfFileManagerInterface`. Does not perform rendering itself.
## Architecture
### Request Flow
```
TwigToPdfRenderer
-> renders Twig template to HTML
-> HtmlToPdfRenderer
-> picks adapter from ServiceLocator by context name
-> Adapter (e.g. KnpSnappyAdapter)
-> GeneratorProviderRegistry::get(adapterType, context)
returns underlying generator (Knp\Snappy\Pdf or Dompdf\Dompdf)
-> CompositeOptionsProcessor::process(generator, context)
runs default processors first, then context-specific ones
-> generator produces PDF bytes
-> returns PDF string
```
### Storage Flow
```
PdfFileManager (context-aware)
-> ServiceLocator keyed by context name
-> PdfStorageInterface instance per context
FlysystemPdfStorage (league/flysystem-bundle — optional)
SymfonyFilesystemPdfStorage (symfony/filesystem — default, hard dep)
GaufrettePdfStorage (knp-gaufrette-bundle — optional)
```
### Options Processing Chain
Each adapter type has a `CompositeOptionsProcessor` that groups processors by context:
```
CompositeOptionsProcessor (per adapter type)
'default' -> [ProcessorA, ProcessorB] # always run
'sylius_invoicing' -> [ProcessorC] # run after defaults, only for this context
```
When processing context `sylius_invoicing`, execution order is: ProcessorA, ProcessorB, ProcessorC.
When processing context `default`, only: ProcessorA, ProcessorB.
### DI Wiring
**Extension phase** (`SyliusPdfGenerationExtension::load`):
- Processes config, loads `config/services.php` (core services)
- For each context (default + named contexts):
- If adapter is built-in (`knp_snappy`, `dompdf`): loads adapter service file once, creates a `ChildDefinition` for the context, registers an options processor tagged `sylius_pdf_generation.options_processor`
- If adapter is unknown: stores in `.sylius_pdf_generation.deferred_adapter_contexts` parameter (dot-prefixed = internal, auto-removed by Symfony)
- Builds per-context `PdfStorageInterface` instances based on `storage` config (validates required classes for each storage type, throws `LogicException` with `composer require` hint); wires into `PdfFileManager`'s ServiceLocator
- Wires the adapter references into `HtmlToPdfRenderer`'s ServiceLocator
**Compiler passes** (in registration order):
1. `RegisterKnpSnappyPrototypePass` - clones the `knp_snappy.pdf` definition as a non-shared prototype so the generator provider can create fresh instances per call
2. `RegisterGeneratorProvidersPass` - collects `sylius_pdf_generation.generator_provider` tagged services into the `GeneratorProviderRegistry`
3. `RegisterOptionsProcessorsPass` - collects all `sylius_pdf_generation.options_processor` tagged services, groups by adapter+context, sorts by priority, builds `CompositeOptionsProcessor` per adapter
4. `RegisterPdfGenerationAdaptersPass` - resolves deferred (custom) adapters by matching `sylius_pdf_generation.adapter` tags to context names, merges into the ServiceLocator
## Configuration
```yaml
sylius_pdf_generation:
gotenberg:
base_url: 'http://localhost:3000' # Gotenberg server URL (default)
default:
adapter: knp_snappy # knp_snappy | dompdf | gotenberg | <custom_key>
storage: # storage defaults for all contexts
type: flysystem # flysystem | filesystem | gaufrette
filesystem: 'default.storage' # service ID (flysystem/gaufrette)
prefix: 'pdf' # path prefix (flysystem/gaufrette)
directory: ~ # local path (filesystem only)
local_cache_directory: ~ # local cache for resolveLocalPath() (flysystem/gaufrette)
contexts:
invoice:
adapter: dompdf
storage:
type: flysystem
filesystem: 's3.storage'
prefix: 'invoices'
local_cache_directory: '%kernel.project_dir%/var/pdf_cache'
receipt:
adapter: knp_snappy
storage:
type: filesystem
directory: '%kernel.project_dir%/private/receipts'
```
- `default` is always present; name `default` is forbidden under `contexts`
- Each entry has two fields: `adapter` (string) and `storage` (array|null)
- `storage` cascades: default -> context override
- Built-in adapters (`knp_snappy`, `dompdf`, `gotenberg`) require their packages to be installed; a `LogicException` is thrown otherwise
- Unknown adapter names are deferred to the compiler pass
- Adapter-specific options are not configured via YAML; use custom `OptionsProcessorInterface` implementations instead (see Extension Points)
## Directory Structure
```
src/
Core/
Adapter/PdfGenerationAdapterInterface.php
Attribute/
AsPdfGenerationAdapter.php # marks custom adapter classes
AsPdfOptionsProcessor.php # marks custom options processors
AsPdfGeneratorProvider.php # marks custom generator providers
Generator/
PdfFileGenerator.php # wraps content into PdfFile + saves via manager
PdfFileGeneratorInterface.php
Filesystem/
Manager/
PdfFileManager.php # delegates to per-context PdfStorageInterface via ServiceLocator
PdfFileManagerInterface.php
Storage/
PdfStorageInterface.php # context-unaware storage contract
Model/PdfFile.php # simple model (filename + content + mutable storagePath)
Processor/
CompositeOptionsProcessor.php # chains processors by context
OptionsProcessorInterface.php
Provider/GeneratorProviderInterface.php
Registry/
GeneratorProviderRegistry.php # keyed by adapter type + context
GeneratorProviderRegistryInterface.php
Renderer/
HtmlToPdfRenderer.php # selects adapter by context
HtmlToPdfRendererInterface.php
TwigToPdfRenderer.php # Twig -> HTML -> HtmlToPdfRenderer
TwigToPdfRendererInterface.php
Filesystem/
Flysystem/
FlysystemPdfStorage.php # Flysystem storage (default, hard dep)
Symfony/
SymfonyFilesystemPdfStorage.php # Symfony Filesystem storage (optional)
Gaufrette/
GaufrettePdfStorage.php # Gaufrette storage (optional)
Adapter/
KnpSnappy/
KnpSnappyAdapter.php # wraps knplabs/knp-snappy-bundle
KnpSnappyGeneratorProvider.php # creates fresh Knp\Snappy\Pdf instances via prototype factory
KnpSnappyOptionsProcessor.php # sets snappy options + allowed_files
Dompdf/
DompdfAdapter.php # wraps dompdf/dompdf
DompdfGeneratorProvider.php # creates fresh Dompdf\Dompdf per call
Gotenberg/
GotenbergAdapter.php # wraps gotenberg/gotenberg-php
GotenbergGeneratorProvider.php # creates fresh ChromiumPdf builder per call
DependencyInjection/
Compiler/
RegisterKnpSnappyPrototypePass.php # clones knp_snappy.pdf as non-shared prototype
RegisterGeneratorProvidersPass.php # populates provider registry
RegisterOptionsProcessorsPass.php # builds composite processors
RegisterPdfGenerationAdaptersPass.php # resolves custom adapters
Configuration.php
SyliusPdfGenerationExtension.php
SyliusPdfGenerationBundle.php
config/
services.php # core services (HtmlToPdfRenderer, TwigToPdfRenderer, manager, registry)
adapter/knp_snappy.php # KnpSnappy adapter + processor + provider (abstract)
adapter/dompdf.php # Dompdf adapter + processor + provider (abstract)
adapter/gotenberg.php # Gotenberg adapter + provider (abstract)
```
## Extension Points
### 1. Custom Adapter
For integrating a PDF library not bundled with this package.
**Via attribute (recommended):**
```php
use Sylius\PdfGenerationBundle\Core\Adapter\PdfGenerationAdapterInterface;
use Sylius\PdfGenerationBundle\Core\Attribute\AsPdfGenerationAdapter;
#[AsPdfGenerationAdapter('my_adapter')]
final class MyAdapter implements PdfGenerationAdapterInterface
{
public function generate(string $html): string { /* ... */ }
}
```
**Via service tag:**
```yaml
App\Pdf\MyAdapter:
tags:
- { name: 'sylius_pdf_generation.adapter', key: 'my_adapter' }
```
Then reference in config:
```yaml
sylius_pdf_generation:
default:
adapter: my_adapter
```
Custom adapters are user-managed services. The bundle does not create instances or pass options to them.
### 2. Custom Options Processor
For modifying the underlying generator's configuration before PDF generation. Useful for adding allowed files, setting paper size, or injecting security options.
**Via attribute:**
```php
use Sylius\PdfGenerationBundle\Core\Attribute\AsPdfOptionsProcessor;
use Sylius\PdfGenerationBundle\Core\Processor\OptionsProcessorInterface;
#[AsPdfOptionsProcessor(adapter: 'knp_snappy', context: 'invoice', priority: 10)]
final class InvoiceOptionsProcessor implements OptionsProcessorInterface
{
public function process(object $generator, string $context = 'default'): void
{
// $generator is Knp\Snappy\AbstractGenerator for knp_snappy
$generator->setOption('page-size', 'A4');
}
}
```
**Via service tag:**
```yaml
App\Pdf\InvoiceOptionsProcessor:
tags:
- { name: 'sylius_pdf_generation.options_processor', adapter: 'knp_snappy', context: 'invoice', priority: 10 }
```
Tag attributes:
- `adapter` (required): which adapter type this processor applies to
- `context` (optional, default: `'default'`): which context; omit to apply to all contexts using this adapter
- `priority` (optional, default: `0`): higher = runs first
### 3. Custom Generator Provider
For controlling how the underlying generator object is created or reused per context.
**Via attribute:**
```php
use Sylius\PdfGenerationBundle\Core\Attribute\AsPdfGeneratorProvider;
use Sylius\PdfGenerationBundle\Core\Provider\GeneratorProviderInterface;
#[AsPdfGeneratorProvider(adapter: 'knp_snappy', context: 'invoice')]
final class InvoiceGeneratorProvider implements GeneratorProviderInterface
{
public function get(?string $context = null): object
{
// Return a custom-configured Knp\Snappy\Pdf instance
}
}
```
**Via service tag:**
```yaml
App\Pdf\InvoiceGeneratorProvider:
tags:
- { name: 'sylius_pdf_generation.generator_provider', adapter: 'knp_snappy', context: 'invoice' }
```
### 4. Replacing Core Services
All core services are aliased by interface. Override via Symfony's service decoration:
```yaml
Sylius\PdfGenerationBundle\Core\Renderer\HtmlToPdfRendererInterface: # alias -> sylius_pdf_generation.renderer.html
Sylius\PdfGenerationBundle\Core\Renderer\TwigToPdfRendererInterface: # alias -> sylius_pdf_generation.renderer.twig
Sylius\PdfGenerationBundle\Core\Filesystem\Manager\PdfFileManagerInterface: # alias -> sylius_pdf_generation.manager
Sylius\PdfGenerationBundle\Core\Adapter\PdfGenerationAdapterInterface: # alias -> sylius_pdf_generation.adapter.default
Sylius\PdfGenerationBundle\Core\Registry\GeneratorProviderRegistryInterface: # alias -> sylius_pdf_generation.registry.generator_provider
```
## Built-in Adapters
### KnpSnappy (`knp_snappy`)
- Requires: `knplabs/knp-snappy-bundle` ^1.10
- Uses wkhtmltopdf binary under the hood
- `KnpSnappyOptionsProcessor` sets options on `AbstractGenerator` and resolves `allowed_files` via `FileLocator`
- `KnpSnappyGeneratorProvider` creates a fresh `Knp\Snappy\Pdf` instance per call via a prototype factory closure (backed by `RegisterKnpSnappyPrototypePass`)
### Dompdf (`dompdf`)
- Requires: `dompdf/dompdf` ^3.1
- Pure PHP, no external binary
- `DompdfGeneratorProvider` creates fresh `Dompdf\Dompdf` instances
### Gotenberg (`gotenberg`)
- Requires: `gotenberg/gotenberg-php` ^2.0
- Uses Gotenberg's Docker-based API with headless Chromium for HTML-to-PDF conversion
- `GotenbergGeneratorProvider` creates a fresh `ChromiumPdf` builder per call, connecting to the Gotenberg server at the configured `base_url`
- Adapter flow: gets `ChromiumPdf` builder → processes options → calls `$builder->html(Stream::string(...))` to build the request → sends via `Gotenberg::send()` → returns PDF bytes
- Configure the Gotenberg server URL via `sylius_pdf_generation.gotenberg.base_url` (defaults to `http://localhost:3000`)
All adapters are optional dependencies. Configuring an adapter whose package is not installed throws a `LogicException` with a `composer require` hint.
## Built-in Storage Backends
### Flysystem (`flysystem`)
- Requires: `league/flysystem-bundle` ^3.0 (optional)
- Constructor: `FilesystemOperator $filesystem, LocalFilesystem $localFilesystem, string $prefix = '', ?string $localCacheDirectory = null`
- Paths use `/` separator (Flysystem normalizes), sets `storagePath` on saved/retrieved files
- `resolveLocalPath()` requires `local_cache_directory` config; lazily fetches and caches locally, invalidates on save/remove
### Symfony Filesystem (`filesystem`)
- Requires: `symfony/filesystem` (hard dependency)
- Constructor: `Filesystem $filesystem, string $directory`
- Uses `DIRECTORY_SEPARATOR` for paths, sets `storagePath` on saved/retrieved files
- `resolveLocalPath()` delegates to internal `resolvePath()` — file is already local
### Gaufrette (`gaufrette`)
- Requires: `knplabs/knp-gaufrette-bundle` (optional)
- Constructor: `GaufretteFilesystem $filesystem, LocalFilesystem $localFilesystem, string $prefix = '', ?string $localCacheDirectory = null`
- Paths use `/` separator, sets `storagePath` on saved/retrieved files
- `resolveLocalPath()` requires `local_cache_directory` config; lazily fetches and caches locally, invalidates on save/remove
## Development
```bash
vendor/bin/phpunit # tests
vendor/bin/phpstan analyse -c phpstan.dist.neon # static analysis
vendor/bin/ecs check # coding standards
```
Tests skip gracefully when optional dependencies are missing via `setUpBeforeClass()` + `markTestSkipped()`.
A Symfony bundle providing context-aware PDF generation with swappable adapter backends.
Converts HTML (or Twig templates) into PDF files. The caller picks a context (e.g. invoice, coupon), and the bundle routes the request to the correct adapter (e.g. KnpSnappy/wkhtmltopdf, Dompdf). Each context can use a different adapter with different options.
TwigToPdfRendererInterface::render(string $template, array $parameters = [], string $context = 'default'): string
High-level entry point. Renders a Twig template to HTML, then delegates to HtmlToPdfRenderer.
HtmlToPdfRendererInterface::render(string $html, string $context = 'default'): string
Takes raw HTML, selects the adapter for the given context from a ServiceLocator, and returns PDF bytes.
PdfGenerationAdapterInterface::generate(string $html): string
Low-level adapter interface. Takes HTML, returns PDF bytes. Adapters are context-unaware.
OptionsProcessorInterface::process(object $generator, string $context = 'default'): void
Configures the underlying PDF generator object (e.g. Knp\Snappy\AbstractGenerator or Dompdf\Dompdf) before PDF generation. Multiple processors run in sequence via CompositeOptionsProcessor.
GeneratorProviderInterface::get(?string $context = null): object
Provides the underlying generator instance (e.g. a Knp\Snappy\Pdf or Dompdf\Dompdf). Used by adapters to get a fresh or shared generator per request.
PdfStorageInterface::save(PdfFile $file): void
PdfStorageInterface::remove(string $filename): void
PdfStorageInterface::has(string $filename): bool
PdfStorageInterface::get(string $filename): PdfFile
PdfStorageInterface::resolveLocalPath(string $filename): string
Context-unaware storage contract. Each instance handles one location. resolveLocalPath() returns an absolute local filesystem path — for SymfonyFilesystemPdfStorage this is the existing path; for Flysystem/Gaufrette it lazily materializes a local copy in the configured local_cache_directory. Implementations: FlysystemPdfStorage (default), SymfonyFilesystemPdfStorage, GaufrettePdfStorage.
PdfFileManagerInterface::save(PdfFile $file, string $context = 'default'): void
PdfFileManagerInterface::remove(string $filename, string $context = 'default'): void
PdfFileManagerInterface::has(string $filename, string $context = 'default'): bool
PdfFileManagerInterface::get(string $filename, string $context = 'default'): PdfFile
PdfFileManagerInterface::resolveLocalPath(string $filename, string $context = 'default'): string
Manages PDF file persistence. PdfFileManager delegates to per-context PdfStorageInterface instances via a ServiceLocator.
PdfFileGeneratorInterface::generate(string $filename, string $content, string $context = 'default'): PdfFile
Wraps already-rendered PDF content into a PdfFile model and saves it via PdfFileManagerInterface. Does not perform rendering itself.
TwigToPdfRenderer
-> renders Twig template to HTML
-> HtmlToPdfRenderer
-> picks adapter from ServiceLocator by context name
-> Adapter (e.g. KnpSnappyAdapter)
-> GeneratorProviderRegistry::get(adapterType, context)
returns underlying generator (Knp\Snappy\Pdf or Dompdf\Dompdf)
-> CompositeOptionsProcessor::process(generator, context)
runs default processors first, then context-specific ones
-> generator produces PDF bytes
-> returns PDF string
PdfFileManager (context-aware)
-> ServiceLocator keyed by context name
-> PdfStorageInterface instance per context
FlysystemPdfStorage (league/flysystem-bundle — optional)
SymfonyFilesystemPdfStorage (symfony/filesystem — default, hard dep)
GaufrettePdfStorage (knp-gaufrette-bundle — optional)
Each adapter type has a CompositeOptionsProcessor that groups processors by context:
CompositeOptionsProcessor (per adapter type)
'default' -> [ProcessorA, ProcessorB] # always run
'sylius_invoicing' -> [ProcessorC] # run after defaults, only for this context
When processing context sylius_invoicing, execution order is: ProcessorA, ProcessorB, ProcessorC.
When processing context default, only: ProcessorA, ProcessorB.
Extension phase (SyliusPdfGenerationExtension::load):
config/services.php (core services)knp_snappy, dompdf): loads adapter service file once, creates a ChildDefinition for the context, registers an options processor tagged sylius_pdf_generation.options_processor.sylius_pdf_generation.deferred_adapter_contexts parameter (dot-prefixed = internal, auto-removed by Symfony)PdfStorageInterface instances based on storage config (validates required classes for each storage type, throws LogicException with composer require hint); wires into PdfFileManager's ServiceLocatorHtmlToPdfRenderer's ServiceLocatorCompiler passes (in registration order):
RegisterKnpSnappyPrototypePass - clones the knp_snappy.pdf definition as a non-shared prototype so the generator provider can create fresh instances per callRegisterGeneratorProvidersPass - collects sylius_pdf_generation.generator_provider tagged services into the GeneratorProviderRegistryRegisterOptionsProcessorsPass - collects all sylius_pdf_generation.options_processor tagged services, groups by adapter+context, sorts by priority, builds CompositeOptionsProcessor per adapterRegisterPdfGenerationAdaptersPass - resolves deferred (custom) adapters by matching sylius_pdf_generation.adapter tags to context names, merges into the ServiceLocatorsylius_pdf_generation:
gotenberg:
base_url: 'http://localhost:3000' # Gotenberg server URL (default)
default:
adapter: knp_snappy # knp_snappy | dompdf | gotenberg | <custom_key>
storage: # storage defaults for all contexts
type: flysystem # flysystem | filesystem | gaufrette
filesystem: 'default.storage' # service ID (flysystem/gaufrette)
prefix: 'pdf' # path prefix (flysystem/gaufrette)
directory: ~ # local path (filesystem only)
local_cache_directory: ~ # local cache for resolveLocalPath() (flysystem/gaufrette)
contexts:
invoice:
adapter: dompdf
storage:
type: flysystem
filesystem: 's3.storage'
prefix: 'invoices'
local_cache_directory: '%kernel.project_dir%/var/pdf_cache'
receipt:
adapter: knp_snappy
storage:
type: filesystem
directory: '%kernel.project_dir%/private/receipts'
default is always present; name default is forbidden under contextsadapter (string) and storage (array|null)storage cascades: default -> context overrideknp_snappy, dompdf, gotenberg) require their packages to be installed; a LogicException is thrown otherwiseOptionsProcessorInterface implementations instead (see Extension Points)src/
Core/
Adapter/PdfGenerationAdapterInterface.php
Attribute/
AsPdfGenerationAdapter.php # marks custom adapter classes
AsPdfOptionsProcessor.php # marks custom options processors
AsPdfGeneratorProvider.php # marks custom generator providers
Generator/
PdfFileGenerator.php # wraps content into PdfFile + saves via manager
PdfFileGeneratorInterface.php
Filesystem/
Manager/
PdfFileManager.php # delegates to per-context PdfStorageInterface via ServiceLocator
PdfFileManagerInterface.php
Storage/
PdfStorageInterface.php # context-unaware storage contract
Model/PdfFile.php # simple model (filename + content + mutable storagePath)
Processor/
CompositeOptionsProcessor.php # chains processors by context
OptionsProcessorInterface.php
Provider/GeneratorProviderInterface.php
Registry/
GeneratorProviderRegistry.php # keyed by adapter type + context
GeneratorProviderRegistryInterface.php
Renderer/
HtmlToPdfRenderer.php # selects adapter by context
HtmlToPdfRendererInterface.php
TwigToPdfRenderer.php # Twig -> HTML -> HtmlToPdfRenderer
TwigToPdfRendererInterface.php
Filesystem/
Flysystem/
FlysystemPdfStorage.php # Flysystem storage (default, hard dep)
Symfony/
SymfonyFilesystemPdfStorage.php # Symfony Filesystem storage (optional)
Gaufrette/
GaufrettePdfStorage.php # Gaufrette storage (optional)
Adapter/
KnpSnappy/
KnpSnappyAdapter.php # wraps knplabs/knp-snappy-bundle
KnpSnappyGeneratorProvider.php # creates fresh Knp\Snappy\Pdf instances via prototype factory
KnpSnappyOptionsProcessor.php # sets snappy options + allowed_files
Dompdf/
DompdfAdapter.php # wraps dompdf/dompdf
DompdfGeneratorProvider.php # creates fresh Dompdf\Dompdf per call
Gotenberg/
GotenbergAdapter.php # wraps gotenberg/gotenberg-php
GotenbergGeneratorProvider.php # creates fresh ChromiumPdf builder per call
DependencyInjection/
Compiler/
RegisterKnpSnappyPrototypePass.php # clones knp_snappy.pdf as non-shared prototype
RegisterGeneratorProvidersPass.php # populates provider registry
RegisterOptionsProcessorsPass.php # builds composite processors
RegisterPdfGenerationAdaptersPass.php # resolves custom adapters
Configuration.php
SyliusPdfGenerationExtension.php
SyliusPdfGenerationBundle.php
config/
services.php # core services (HtmlToPdfRenderer, TwigToPdfRenderer, manager, registry)
adapter/knp_snappy.php # KnpSnappy adapter + processor + provider (abstract)
adapter/dompdf.php # Dompdf adapter + processor + provider (abstract)
adapter/gotenberg.php # Gotenberg adapter + provider (abstract)
For integrating a PDF library not bundled with this package.
Via attribute (recommended):
use Sylius\PdfGenerationBundle\Core\Adapter\PdfGenerationAdapterInterface;
use Sylius\PdfGenerationBundle\Core\Attribute\AsPdfGenerationAdapter;
#[AsPdfGenerationAdapter('my_adapter')]
final class MyAdapter implements PdfGenerationAdapterInterface
{
public function generate(string $html): string { /* ... */ }
}
Via service tag:
App\Pdf\MyAdapter:
tags:
- { name: 'sylius_pdf_generation.adapter', key: 'my_adapter' }
Then reference in config:
sylius_pdf_generation:
default:
adapter: my_adapter
Custom adapters are user-managed services. The bundle does not create instances or pass options to them.
For modifying the underlying generator's configuration before PDF generation. Useful for adding allowed files, setting paper size, or injecting security options.
Via attribute:
use Sylius\PdfGenerationBundle\Core\Attribute\AsPdfOptionsProcessor;
use Sylius\PdfGenerationBundle\Core\Processor\OptionsProcessorInterface;
#[AsPdfOptionsProcessor(adapter: 'knp_snappy', context: 'invoice', priority: 10)]
final class InvoiceOptionsProcessor implements OptionsProcessorInterface
{
public function process(object $generator, string $context = 'default'): void
{
// $generator is Knp\Snappy\AbstractGenerator for knp_snappy
$generator->setOption('page-size', 'A4');
}
}
Via service tag:
App\Pdf\InvoiceOptionsProcessor:
tags:
- { name: 'sylius_pdf_generation.options_processor', adapter: 'knp_snappy', context: 'invoice', priority: 10 }
Tag attributes:
adapter (required): which adapter type this processor applies tocontext (optional, default: 'default'): which context; omit to apply to all contexts using this adapterpriority (optional, default: 0): higher = runs firstFor controlling how the underlying generator object is created or reused per context.
Via attribute:
use Sylius\PdfGenerationBundle\Core\Attribute\AsPdfGeneratorProvider;
use Sylius\PdfGenerationBundle\Core\Provider\GeneratorProviderInterface;
#[AsPdfGeneratorProvider(adapter: 'knp_snappy', context: 'invoice')]
final class InvoiceGeneratorProvider implements GeneratorProviderInterface
{
public function get(?string $context = null): object
{
// Return a custom-configured Knp\Snappy\Pdf instance
}
}
Via service tag:
App\Pdf\InvoiceGeneratorProvider:
tags:
- { name: 'sylius_pdf_generation.generator_provider', adapter: 'knp_snappy', context: 'invoice' }
All core services are aliased by interface. Override via Symfony's service decoration:
Sylius\PdfGenerationBundle\Core\Renderer\HtmlToPdfRendererInterface: # alias -> sylius_pdf_generation.renderer.html
Sylius\PdfGenerationBundle\Core\Renderer\TwigToPdfRendererInterface: # alias -> sylius_pdf_generation.renderer.twig
Sylius\PdfGenerationBundle\Core\Filesystem\Manager\PdfFileManagerInterface: # alias -> sylius_pdf_generation.manager
Sylius\PdfGenerationBundle\Core\Adapter\PdfGenerationAdapterInterface: # alias -> sylius_pdf_generation.adapter.default
Sylius\PdfGenerationBundle\Core\Registry\GeneratorProviderRegistryInterface: # alias -> sylius_pdf_generation.registry.generator_provider
knp_snappy)knplabs/knp-snappy-bundle ^1.10KnpSnappyOptionsProcessor sets options on AbstractGenerator and resolves allowed_files via FileLocatorKnpSnappyGeneratorProvider creates a fresh Knp\Snappy\Pdf instance per call via a prototype factory closure (backed by RegisterKnpSnappyPrototypePass)dompdf)dompdf/dompdf ^3.1DompdfGeneratorProvider creates fresh Dompdf\Dompdf instancesgotenberg)gotenberg/gotenberg-php ^2.0GotenbergGeneratorProvider creates a fresh ChromiumPdf builder per call, connecting to the Gotenberg server at the configured base_urlChromiumPdf builder → processes options → calls $builder->html(Stream::string(...)) to build the request → sends via Gotenberg::send() → returns PDF bytessylius_pdf_generation.gotenberg.base_url (defaults to http://localhost:3000)All adapters are optional dependencies. Configuring an adapter whose package is not installed throws a LogicException with a composer require hint.
flysystem)league/flysystem-bundle ^3.0 (optional)FilesystemOperator $filesystem, LocalFilesystem $localFilesystem, string $prefix = '', ?string $localCacheDirectory = null/ separator (Flysystem normalizes), sets storagePath on saved/retrieved filesresolveLocalPath() requires local_cache_directory config; lazily fetches and caches locally, invalidates on save/removefilesystem)symfony/filesystem (hard dependency)Filesystem $filesystem, string $directoryDIRECTORY_SEPARATOR for paths, sets storagePath on saved/retrieved filesresolveLocalPath() delegates to internal resolvePath() — file is already localgaufrette)knplabs/knp-gaufrette-bundle (optional)GaufretteFilesystem $filesystem, LocalFilesystem $localFilesystem, string $prefix = '', ?string $localCacheDirectory = null/ separator, sets storagePath on saved/retrieved filesresolveLocalPath() requires local_cache_directory config; lazily fetches and caches locally, invalidates on save/removevendor/bin/phpunit # tests
vendor/bin/phpstan analyse -c phpstan.dist.neon # static analysis
vendor/bin/ecs check # coding standards
Tests skip gracefully when optional dependencies are missing via setUpBeforeClass() + markTestSkipped().
How can I help you explore Laravel packages today?