topthink/think-template
A lightweight PHP template engine for ThinkPHP that compiles templates into plain PHP. It supports template inheritance, includes, variable output, custom tags and caching, making view rendering fast, simple and easy to integrate into existing applications.
Architecture fit. topthink/think-template is the standalone extraction of the ThinkPHP template engine — a compile-to-PHP engine with its own tag dialect ({volist}, {if}, {extend}, {block}, {$var|filter}). Laravel's view layer is Blade, built on a different compiler, different inheritance model, and a deep ecosystem of directives (@vite, @csrf, @livewire, @error) that packages depend on. The two are architecturally parallel, not complementary: adopting think-template means running a second template language inside an application whose entire package ecosystem assumes Blade. Fit is therefore poor for greenfield, and only defensible as a narrowly scoped secondary renderer (email bodies, PDF/HTML generation, legacy template replay) that never touches resources/views or any package-supplied view.
Integration feasibility. Technically achievable. Laravel's View factory allows registering a custom engine resolver and extension (View::getEngineResolver()->register(...) plus addExtension('tpl', 'think') in a service provider), so think-template can be wired as an EngineInterface implementation in roughly 150–250 lines: bootstrapping the engine, mapping Laravel's view paths to think-template's view_path/cache_path, forwarding shared data, and normalizing exceptions into Laravel's handler. The real cost is not the adapter — it is the perimeter: every Blade-dependent capability (components/slots, view composers semantics, assertViewHas test helpers, mail Markdown, pagination views, error-page rendering, Filament/Nova/Jetstream which hard-depend on Blade) must either be reimplemented or explicitly excluded. Anything requiring Filament or Nova is a hard blocker.
Technical risk. Three items dominate:
default_filter), not guaranteed by default; verify the escaping default and pin it explicitly before any user-influenced data flows through. ThinkPHP's advisory history includes template/expression-evaluation paths, so expect ongoing CVE review burden.view:cache does not solve for you.Key questions. (1) What is the actual driver — legacy ThinkPHP template parity during a framework migration, or a preference? Only the former justifies the cost. (2) How many templates, and how complex (inheritance depth, custom tags)? Below roughly 50 simple templates, mechanical rewrite to Blade almost certainly wins. (3) Does any Blade-dependent package need to coexist? If yes, stop. (4) Who owns CVE triage for a single-vendor package with 52 stars and zero dependents on Packagist? (5) Does it hold up on the PHP versions in our support matrix (verify 8.3/8.4), and what is the upgrade cadence?
Stack fit. Assume isolation by default: dedicated namespace (resources/think/), dedicated cache path (storage/framework/think/), dedicated service provider, and a facade or renderer service — no changes to config/view.php or the default engine. Blade remains the engine for all HTTP responses. This keeps the blast radius to the one feature that needs it.
Migration path. The only credible sequence is a strangler pattern during a ThinkPHP→Laravel move:
.tpl files.If there is no ThinkPHP migration in scope, the recommended path is do not integrate — the package's value proposition is template-format compatibility, and without legacy templates there is nothing to be compatible with.
Compatibility. Pin an exact version and record the PHP constraint in the adapter's composer requirements. Verify PHP 8.3/8.4 behavior explicitly rather than trusting the constraint. Confirm the Apache-2.0 license is compatible with distribution requirements — it is permissive and low-risk, but attribution obligations should be captured in the dependency register.
Sequencing. Security review of escaping configuration and filter usage before any adapter code; adapter plus golden-file tests; then feature cutovers; then sunset. Do not let the adapter land before the escaping decision, or the default will ship by accident.
Maintenance. A permanently maintained second template language is the dominant cost. Budget ongoing ownership for the adapter, the escaping configuration, the compiler-cache lifecycle, and a CVE watch on a package with a single-vendor maintainer base, 52 stars, and no downstream dependents to surface breakage early. Every Laravel major upgrade adds a compatibility check for the adapter. Blast radius on abandonment is contained only because of the isolation strategy above.
Support. Expect elevated support load: developers will file bugs against think-template syntax while searching for Blade answers, and new hires will not know the dialect. Mitigate with a short internal README, a lint rule or CI check that forbids think-template usage outside the designated namespace, and an explicit "no new templates" policy from day one.
Scaling. Compiled output is per-node unless the cache directory is on shared storage; on autoscaled fleets, plan a build-time warmup step and a shared or pre-baked cache. Runtime performance is broadly comparable to Blade (both compile to opcache-able PHP), so the risk is cold-start and invalidation, not throughput. Add cache-directory writability to health checks and deploy smoke tests.
Failure modes. Highest-severity: template injection reaching evaluation paths (treat as RCE-class). Next: silent output escaping regressions after config drift; stale compiled templates after deploy producing mismatched output between nodes; and adapter-level data-sharing gaps where a view composer populates Blade expectations that think-template never receives. Each needs a detection path — golden-file tests for output, a deploy assertion on cache freshness, and an escaping test that renders known-dangerous payloads.
Ramp-up. Adapter work is roughly 1–2 engineer-weeks including tests; a security review adds a few days; full team fluency is the larger, unquantified cost. Against an opportunity score of 41.9 and a composite of 5.0/10, the honest TPM recommendation is: reject as a primary view engine; approve only as a time-boxed, namespaced legacy-compatibility renderer with a funded sunset plan. If the legacy-template driver is absent, the correct decision is to take no dependency at all.
How can I help you explore Laravel packages today?