goetas/apache-fop
PHP wrapper for Apache FOP that renders XSL-FO documents into PDF and other output formats. It offers a simple API to run FOP transformations and integrate FO-based PDF generation into Laravel or any PHP application.
Architecture fit — poor as packaged, salvageable as a pattern. The core issue is a framework mismatch: goetas/apache-fop is a Symfony2 bundle (Bundle subclass, DI service definitions, Configuration tree, compiler passes). Laravel has no bundle/extension concept, so none of the package's intended integration surface is usable. What remains of value is roughly a few hundred lines of glue: a wrapper that shells out to the Apache FOP CLI, assembles arguments (input XML, XSLT/FO stylesheet, output path), and returns a file handle. That logic is trivially reimplementable in a Laravel service provider — which raises the central TPM question: are we adopting a dependency, or copying ~300 lines of abandoned code?
Integration feasibility — blocked before it starts, in the common case. The bundle's composer.json will constrain symfony/framework-bundle/symfony/http-kernel to the 2.x line. Laravel 8+ ships Symfony 5/6 components; Laravel 5.x/6.x ship Symfony 3/4. Composer will refuse to resolve, and forcing it via --ignore-platform-reqs or aliasing invites runtime breakage from autoloaded Symfony classes. The realistic path is not installing the bundle at all and instead treating the repo as reference implementation. Second-order dependency: Apache FOP is a JVM application. This introduces a second runtime (JDK/JRE) into the deployment image, plus FOP's own config (fop.xconf, font directories), which is an infrastructure concern the PHP ecosystem normally avoids.
Technical risk — high, concentrated in abandonment.
Key questions for the spike.
Stack fit — treat the package as a design reference, not a dependency. Do not composer require the bundle. If the wrapper logic is worth keeping, vendor it under app/Support/Fop/ with attribution, strip all Symfony imports, and expose it as a singleton bound in a FopServiceProvider. The public surface we actually need is narrow: render(string $xmlPath, string $xsltPath, string $outputPath): string.
Migration path.
fop -xml data.xml -xsl style.xsl -pdf out.pdf. If that pipeline cannot produce an acceptable artifact, the PHP layer is irrelevant and the initiative stops here — cheapest possible kill point.Process (Symfony Process is already a Laravel dependency, no version conflict), explicit binary path from config, -c fop.xconf, timeout, and temp-directory isolation.config/fop.php, font path assertions at boot, and a health check that verifies JVM + FOP presence.Compatibility. Pin FOP version in the container image (Dockerfile), not in composer.json. Configure JVM heap (-Xmx) via FOP_OPTS/JAVA_TOOL_OPTIONS — this becomes an ops-owned knob. Ensure the PHP process user has write access to a dedicated /tmp/fop with a cleanup policy.
Sequencing.
Explicit non-goal: do not attempt to register the GoetasApacheFopBundle in Laravel via a shim. The shim cost exceeds the rewrite cost and locks us to Symfony 2 semantics.
Maintenance — this becomes an owned fork on day one. There is no upgrade path, no CVE feed, and no issue tracker we can lean on. Budget an ongoing ownership line: one engineer responsible for the vendored code, FOP version bumps, and font assets. Counterweight: the vendored surface is small and the engine (Apache FOP) carries its own upstream maintenance, so the total cost is bounded — but it is ours, not a vendor's.
Support. No upstream support of any kind. Internal runbooks must cover: JVM not found, FOP config not found, font missing (silent substitution → wrong glyphs, a nasty class of bug), malformed XSLT, and non-zero FOP exit codes. The 8-star / 0-dependent profile means we cannot validate our usage against anyone else's.
Scaling. Two dimensions:
Failure modes.
| Mode | Signal | Blast radius |
|---|---|---|
| JVM OOM on large documents | Non-zero exit, stderr heap dump | Single render fails; retries amplify load |
| Subprocess hang (pathological XSLT) | No exit within timeout | Worker thread pinned — must set a hard timeout |
| Missing/misnamed font | Successful render, wrong glyphs | Silent data-integrity bug, worst case |
| Temp file leak | Disk growth | Host-level degradation over days |
| Composer resolution conflict | Install-time failure | Blocks CI entirely if the bundle is required |
The silent font-substitution case is the one to escalate to the business: renders succeed and corrupt documents go out.
Ramp-up. XSL-FO/XSLT 1.0 is a specialized, declining skill; expect a real learning curve for any engineer asked to modify templates, and near-zero availability of new hires with it. Mitigation: keep stylesheets in version control with golden-file visual diffs, and treat template authoring as a named-owner responsibility rather than general team knowledge.
TPM recommendation. Score 0.41 with zero dependents reflects reality. Do not adopt the bundle. Fund a one-week spike to validate Apache FOP itself against the actual document requirements; if FOP clears, build a ~200-line Laravel client and treat this package purely as a reference. If the requirement is ordinary HTML-to-PDF, stop now and use a maintained PHP-native or headless-browser path — the JVM dependency, the XSL-FO skills gap, and permanent fork ownership are not worth paying for anything less than genuine XSL-FO capability.
How can I help you explore Laravel packages today?