secit-pl/json-ld-bundle
A Symfony bundle for generating JSON-LD structured data in your applications. It provides services and Twig helpers to define schema.org objects, embed SEO-friendly rich snippets, and improve search visibility with minimal configuration and flexible integration.
Provenance caveat. The prior TPM assessment supplied with this package is a Laravel-package template and does not describe this artifact. It asserts a service provider with extra.laravel.providers auto-discovery, publishable migrations, Eloquent coupling, and queue-worker serialization — none of which exist here. This is a Symfony bundle: registered in config/bundles.php, exposing stateless builder services through the DI container, with a Twig extension for rendering. The practical consequence is that the template over-weights risks this package cannot have (schema ownership, global middleware, job serialization) and under-weights the two that actually block adoption (Twig 3 base-class removal, PHP 8 incompatibility). That assessment should be discarded rather than appended to.
Architecture fit. Conceptually, the fit is excellent. JSON-LD is a presentation-layer concern: no persistence, no migrations, no request lifecycle hooks, no network calls. It composes nested PHP objects and json_encodes them into a <script type="application/ld+json"> block. That is the lowest-coupling class of dependency available, and it belongs in a Symfony app. The mismatch is temporal, not architectural: the codebase targets Symfony 2.8/3.0 and the 2017 Twig/PHP era, and any modern Symfony baseline is six to eight majors past that. The bundle interface itself is stable — Bundle::build(), getContainerExtension(), Twig extensions — so the glue is salvageable, but only if we patch it.
Integration feasibility. Two concrete blockers, both knowable in an afternoon:
Twig_* class aliases, including Twig_Extension. A 2017 extension will reference the legacy base class and fail at container compile or first render. Mechanical fix, but the fix is ours.each(), curly-brace string offsets, and several loosely-typed call patterns common in that era. Expect fatal errors or deprecation noise depending on which paths we hit.Neither is difficult. That is precisely the problem: if we patch it, we own a fork, and we own it indefinitely. The honest feasibility read is not installable as-is on a modern stack — roughly 1–2 days to fork and patch, or 2–4 days to rewrite the subset we'd actually use (Thing, Organization, Product, BreadcrumbList). Given a score of 0.57, 6 stars, and a last release of November 2017, the build-versus-buy calculation is not close.
Technical risk.
composer.json will constrain symfony/* and twig/twig to 2017-era ranges. Installing it may force downgrades or, worse, become the single package blocking a platform upgrade. Check this before anything else.json_encode. That caps the "buy" premium at near-zero and makes "copy the good parts under our namespace, with MIT attribution" the rational default.</script> breaking out of the script block from user-controlled fields (product names, breadcrumb labels). A 2017 library may not set JSON_HEX_TAG. This is the one finding with security weight and should be read out of the source before adoption, not after.Key questions.
composer.lock?JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT on output? (Security-relevant; verify in source.)Stack fit. Symfony-native, stateless, no persistence, no middleware, no scheduled tasks. Verify the builders hold no accumulating state: a container-shared builder service reused across a loop or a long-running worker (RoadRunner, FrankenPHP, Messenger) could leak entities between renders if any builder mutates itself. The Twig function should be a pure function of its arguments. If it isn't, instantiate per call.
Migration path. Recommended: extract and own, not composer require.
App\Seo\JsonLd), retaining the MIT header and attribution.Bundle class and DependencyInjection\Extension — they exist only to wire services we can declare in services.yaml with autowiring.Twig_Extension to AbstractExtension with TwigFunction objects and namespaced classes; add return types; remove any removed-in-PHP-8 constructs.^ on an abandoned package — the constraint buys nothing since no patch releases will arrive), patch via fork, register manually in bundles.php, and override its service definitions.Compatibility. Test against our Twig major explicitly. Assertions that matter are output-level, not unit-level: is the emitted block valid JSON, is it valid schema.org, and is a hostile string in a product name safely escaped? Golden-file tests per page type catch silent schema drift across framework upgrades in a way that testing the builders in isolation will not.
Sequencing.
composer.lock diff and breakage list (half day).Maintenance. We will own this code. Assign a named owner, schedule a review at every Symfony and Twig major, and treat it as internal source rather than a dependency — no Dependabot or Renovate signal will ever fire for it. Vendoring into our own tree makes this ownership explicit; leaving it as an upstream require creates the illusion of a supported dependency while the fork obligation is identical.
Support. There is none. No commercial backing, no disclosed security contact, no issue triage, no changelog history to reason from. Internal SLA is bounded by what the owning engineer knows. Ramp-up cost for the next engineer is reading the source — acceptable at a few thousand lines if it sits in our tree under our conventions, materially worse if it is an opaque third-party vendor directory.
Scaling. Runtime cost is negligible: building a nested array and encoding it is microseconds and allocates nothing durable. The only scaling concern is recomputing identical site-wide markup (Organization, WebSite, sitewide BreadcrumbList) on every request — precompute once into a Twig global or a container parameter rather than per-render. No database, no network, no queue, no cache invalidation surface. This being one of the cheapest dependencies we could adopt is exactly why it does not warrant taking on an unmaintained one.
Failure modes.
</script> breakout via user-controlled builder input. Highest-severity failure here. Assert the encode flags and add a test that feeds a hostile string through a builder and inspects raw output.Ramp-up. Half a day for a Symfony-fluent developer to read the builders and the Twig extension and produce valid output for one page type; a further day for golden-file coverage and escaping tests. Taking the upstream package unpatched adds the fork-and-patch work in front of that plus permanent overhead: knowing we are on an unmaintained branch with no upgrade signal, which disproportionately penalizes whoever inherits this.
How can I help you explore Laravel packages today?