idci/merge-token-bundle
The IDCI MergeTokenBundle adds merge-token support to Symfony applications, providing services and integration for defining, resolving, and applying tokens across content and data workflows.
Architecture fit
idci/merge-token-bundle solves a narrow, well-understood problem: locate named placeholders in a string and substitute runtime values. That capability maps cleanly onto several roadmap items a TPM is likely already tracking — personalized transactional email, statement/invoice rendering, contract generation, white-label copy, notification templating. Evaluated as a capability, it fits. Evaluated as a dependency, it does not: this is a Symfony bundle (*Bundle suffix, IDCI vendor namespace, expected DependencyInjection/, Resources/config/, kernel registration), while our application is Laravel. There is no ServiceProvider, no facade, no extra.laravel autodiscovery block, and no Symfony kernel to register the bundle against. The TPM's read is that the package is a useful reference for scoping the problem and a poor candidate for the solution.
Integration feasibility Two credible paths, one of which is really a decision to build:
symfony/framework-bundle and its transitive tree into a Laravel app solely to obtain a string-substitution utility, then manually wire the bundle's services around Laravel's container. High blast radius, low payoff, and an ongoing upgrade tax every time Laravel bumps its Symfony component constraints.With 0 stars and 0 dependents there is no Laravel bridge to lean on and no community pressure sustaining one. Building a bridge would mean maintaining someone else's abandoned code inside our stack — a cost the TPM should refuse on principle.
Technical risk
symfony/* to 2.x/3.x and PHP 5.6–7.x. Modern Laravel requires PHP 8.1+ and Symfony 6/7 components, so a Composer conflict is more likely than not. This is a schedule risk, not just a technical one — it surfaces mid-sprint if not front-loaded.Key questions
composer require --dry-run say against our composer.lock?[[token]], {{ token }}, %token%), and does it support defaults, typed formatting, or nested tokens?Stack fit Poor as a dependency; good as a specification. Our stack is Laravel (PHP 8.x) on Composer, Laravel's service container, and Blade for rendering. A Symfony bundle's kernel/extension/compiler-pass model has no natural entry point. The correct fit is either a Laravel-native package we control or a framework-agnostic resolver with zero Symfony coupling. The TPM's framing for stakeholders: we are not buying a component, we are buying a design reference at the cost of one POC sprint.
Migration path Recommend wrap-then-replace, which keeps the roadmap date intact while removing dependency risk:
MergeTokenResolver::resolve(string $template, array $context): string as a first-party interface. Every consumer (mailer, PDF renderer, notification service) depends on the interface only — never on a vendor class. This is the single most important TPM-level constraint because it makes the eventual swap a one-line container binding.This converts an unmaintained third-party risk into a few engineer-days of scoped work, and gives us the functional requirements for free.
Compatibility
config.yml the bundle expects must be re-expressed as config/merge_token.php and bound manually — a translation cost the TPM should budget for rather than discover.Sequencing
composer dry-run against our lock, POC branch, behaviour matrix documented.Maintenance Adopting as-is means forking unmaintained code — the worst position: we pay the upgrade cost without controlling upstream. A 200–400 line in-house resolver is cheaper over a 2–3 year horizon and removes a supply-chain node. Either way, assign a named owner. Token merging is deceptively easy to get subtly wrong (whitespace, case sensitivity, absent-vs-empty keys), so it needs a test suite we control and can evolve.
Support There is no meaningful upstream support channel. Zero dependents means no Stack Overflow corpus, no community patches, no third-party security advisories, and no escalation path. Any production issue becomes a first-party incident. That is an acceptable posture for a utility we wrote and understand; it is not acceptable for an inherited dependency on a customer-facing, PII-adjacent path.
Scaling Runtime cost is negligible — parsing is microsecond-scale and will never be the bottleneck. The real scaling concern is template volume and cache strategy: cache resolved templates keyed by template hash plus context schema, and invalidate on template change. Design this in from the start for high-volume transactional email rather than retrofitting it after volume arrives.
Failure modes
Hello [[first_name]]) — highest probability, highest embarrassment. Mitigate with a strict mode that throws outside production, logs a warning in production, and an assertion in the email-rendering test suite.Ramp-up Conceptually low ramp-up, but knowledge concentration risk is high: with no upstream docs or community, understanding lives entirely with whoever runs the POC. Mitigate by pairing, committing the behaviour matrix to the repo, and ensuring at least two engineers can modify the resolver. Budget roughly 3–5 engineer-days for the port-and-test path. The alternative — keeping a Symfony bundle alive inside Laravel — is open-ended and almost certainly larger.
Recommendation: evaluate and do not adopt as a dependency. Treat it as prior art, implement the capability behind a first-party interface, and close the package out as "evaluated, not adopted."
How can I help you explore Laravel packages today?