evgeniy-it/widget-bundle
Widget Bundle brings MODX-style shortcodes to your CMS, parsing nested [[widget? &option=value]] tags through a Twig filter. Includes simple template-based widgets, Doctrine repository widgets, array settings, and nesting.
Architecture fit
The first and most material finding: evgeniy-it/widget-bundle is a Symfony bundle, not a Laravel package. It ships as an EvgeniyIt\WidgetBundle\WidgetBundle-style bundle intended for registration in AppKernel/bundles.php, and exposes functionality through Twig — the README's only public entry point is the |widget filter ({{ some_string_var|widget }}). There is no service provider, no facade, no config publish tag, and no Laravel-style auto-discovery. So the "Laravel/PHP package" framing only holds in the narrow sense that both frameworks use Twig-adjacent templating and Composer; the integration surface does not exist.
What is potentially reusable is the shortcode engine itself: the [[widget_name? &key=\value` &arr=`[1,2,3]`]]` grammar, the nested-render resolution, and the "render settings into a template" behavior. That logic is framework-agnostic in principle, but it is packaged as a bundle and coupled to Symfony's container for widget discovery/registration. Extracting it is a port, not a configuration change.
Integration feasibility
Low-to-moderate, and asymmetric:
[[widget_name2? &setting2=\[[widget3]]`]]) shows a real recursive resolver, not just string substitution. If the product need is "let CMS editors embed parameterized components in body copy," the conceptual model is sound and portable to a Laravel Blade directive/Illuminate\Support\Str`-based macro.Technical risk
]] inside a value, deep nesting).Key questions
spatie/laravel-shortcodes-class library) before touching a 2017 orphan.composer require spike and should gate everything else.Stack fit
Partial. Twig is the shared substrate — if the target application uses Twig (Laravel can, via twigbridge, and Symfony natively), the |widget filter contract could survive a port. If the target is Blade (the Laravel default), there is no direct fit; the equivalent is a Blade::directive('widget', ...) plus a shared rendering service, which means we are writing the integration layer regardless.
Recommendation: treat the package as a specification and reference implementation, not a dependency.
Migration path
&content, custom template, array/assoc-array settings, and the two-level nested case). These become the acceptance tests for whatever we build.composer require on a pinned legacy PHP image purely to observe behavior — specifically recursion depth handling and how it treats ]] and backticks inside values. Do not ship it.&key=\value`` pairs with type coercion for [...]/{...}, resolve nested content bottom-up, and dispatch widget names through an explicit registry — an explicit allowlist, not container auto-discovery.Blade::directive (or Twig filter if Twig is in play) that delegates to the resolver service. Keep parsing and rendering as separate services so the renderer can be swapped.[[name? &k=\v`]]` grammar means existing authored content (and any docs written against this bundle) remain valid, which is the only thing the package genuinely gives us.Compatibility
ourorg/widget-bundle), vendored or in a private registry, and explicitly documented as a permanent internal fork — not a temporary override.Sequencing
Dependency audit spike → grammar fixture suite → build-vs-fork decision (default: build) → resolver implementation → framework adapter → author-facing docs and escaping rules → deprecation plan for any existing content. The grammar fixtures must land before the implementation so the decision is evidence-based.
Maintenance
This is the dominant cost. Adopting the package as-is means acquiring an unmaintained Symfony-era codebase: we own PHP-version bumps, security review, and any framework-API drift, with no upstream to merge from. Reimplementing shifts the cost to a one-time build plus a small, readable surface we control — which is materially cheaper over a 2–3 year horizon. Either way, the "0 stars, 0 dependents, last release 2017" profile means the maintenance line item is not small and must be budgeted explicitly.
Support
No maintainer, no issue triage, no release notes to correlate against incidents. Internal support means the adopting team is the sole escalation path, and that must be named in the runbook. The reimplementation approach at least keeps the knowledge in-house by construction.
Scaling
Parsing and rendering are CPU-light per request and unlikely to be a throughput bottleneck. Two real scaling concerns:
Template resolution also scales badly if it hits the filesystem per widget — resolve once, memoize per request.
Failure modes
[[, backtick inside a value) → silent partial render or literal leakage into published content. Needs an explicit "unparseable, render literal" policy so authors see their syntax rather than a broken page.Ramp-up
Low for syntax — the README is sufficient for an author to learn [[name? &k=\v`]]` in under an hour, and the grammar is familiar to anyone who has used MODX or shortcode-style CMSes. Higher for engineering: anyone touching the resolver needs to understand the tokenizer's edge-case handling and the deliberate deviations from the original. Budget a half-day of onboarding for the resolver plus the fixture suite as living documentation. If the original bundle is adopted rather than reimplemented, add a multi-day ramp for the Symfony bundle internals — reading someone else's 2017 container wiring to fix a parser bug is the worst of both worlds.
How can I help you explore Laravel packages today?