Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Template Bundle Laravel Package

nyrodev/php-template-bundle

A Symfony bundle by nyrodev that adds PHP-based templating support to your application, with helpers and utilities for rendering views directly in PHP instead of relying on a separate template engine. Lightweight, simple to configure, and easy to drop into existing Symfony projects.

View on GitHub
Deep Wiki
Context7

Getting Started

nyrodev/php-template-bundle re-introduces the symfony/templating PhpEngine (removed from Symfony core in 5.0) as a bundle-provided service. Use it when you are maintaining a legacy app on PHP templates, migrating off Symfony 4.x, or when a team is more productive in plain PHP than Twig. It is not a Twig replacement — it is a coexistence layer.

Minimal steps:

  1. Install — composer require nyrodev/php-template-bundle. Confirm the required symfony/templating / symfony/framework-bundle version constraints in its composer.json before committing; the package is small (2 stars, 0 dependents) and version ranges are the most likely source of friction.
  2. Register the bundle in config/bundles.php:
    NyroDev\PhpTemplateBundle\NyroDevPhpTemplateBundle::class => ['all' => true],
    
  3. Verify the service wiring with php bin/console debug:container --show-private | grep -i templating. You are looking for the PhpEngine service (likely templating or nyro_dev_php_template.engine) and its helper set.
  4. Create your first template in the project's templates/ directory, e.g. templates/hello.html.php:
    <?php $view->extend('layout.html.php') ?>
    <?php $view['slots']->set('title', 'Hello') ?>
    <h1>Hello <?= $view->escape($name) ?></h1>
    
  5. Render it from a controller:
    use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
    use Symfony\Component\HttpFoundation\Response;
    
    class HelloController extends AbstractController
    {
        public function hello(string $name, PhpEngine $engine): Response
        {
            return new Response($engine->render('hello.html.php', ['name' => $name]));
        }
    }
    

Where to look first: the bundle's DependencyInjection/ extension and its service definition file. Read those before the README — for a package this size the DI config is the documentation, and it tells you exactly which helper set is registered (slots, request, session, router, assets, translator, actions) and under which service id.

First use case: a single legacy "dashboard" or "print" view that a designer edits directly. Prove the round-trip (layout + slot + $view->escape) works before committing to a full migration strategy.

Implementation Patterns

Layout inheritance via slots. PHP templates have no {% extends %}. The equivalent is $view->extend(), which must be the first statement in the file. Output the child body with $view['slots']->output('_content') in the layout:

{{# layout.html.php #}}
<!DOCTYPE html>
<html>
<head>
    <title><?= $view->escape($view['slots']->get('title', 'App')) ?></title>
</head>
<body>
    <?php $view['slots']->output('_content') ?>
</body>
</html>

Partials via the engine, not include. include will not inject $view. Use the engine so helpers are available:

<?= $view->render('partials/flash.html.php', ['messages' => $messages]) ?>

Controller convention. Wrap rendering in a small trait or abstract controller so injection does not leak into every action:

trait PhpTemplatingTrait
{
    protected function renderPhp(string $template, array $vars = []): Response
    {
        return new Response($this->container->get('templating')->render($template, $vars));
    }
}

Then actions stay readable and the swap point stays in one file.

Mixing with Twig. Run both engines side by side during migration. New features go into .twig, legacy forms and emails stay in .html.php. Route the choice by file extension in a DelegatingEngine-style wrapper, or simpler: keep the PHP render call explicit (renderPhp()) and never rely on AbstractController::render() for PHP templates (see Gotchas).

Forms and error rendering. $view['form'] is not part of the standard templating helper set, and Symfony's form theme renderer is Twig-first by default. Either register a small custom helper that proxies FormRenderer, or keep forms in Twig and embed them in a PHP layout via a sub-request. The latter is usually less work.

Emails. Transactional mail is the strongest use case for this bundle. If you wire the engine into MailerInterface via a TemplatedEmail-equivalent or a small wrapper, PHP templates let non-Symfony developers edit email bodies without learning Twig syntax.

Testing. Assert on the rendered string ($engine->render(...)) rather than on the service. Because PHP templates are compiled by OPcache rather than by a template cache layer, tests see changes immediately without a cache warmup — a real ergonomic advantage over Twig.

Gotchas and Tips

No autoescaping. This is the single biggest footgun. Every dynamic value from user input needs $view->escape($value) (or $view->escape($value, 'js'|'css'|'url')). Twig's safety-by-default does not exist here. Add a lint rule or code review checklist item; XSS regressions in PHP templates are common and silent.

AbstractController::render() is Twig-only. In Symfony 5+, the base controller hardcodes the twig service. Calling $this->render('foo.html.php') will fail or render through Twig. Always fetch the templating engine explicitly (injected or $this->container->get('templating')). Do not rely on suffix-based engine delegation — the DelegatingEngine that used to make that work is not present in modern Symfony.

Helper availability is narrower than you remember. The router, assets, actions, and form helpers all lived in FrameworkBundle/TwigBundle and were removed alongside the engine. Check the bundle's service definitions for what is actually registered; for anything missing, implement a HelperInterface and register it on the helper set (or via a compiler pass) rather than calling $this->urlGenerator from inside the template.

Template resolution. Legacy Bundle:Controller:template notation versus modern @Bundle/... and templates/ paths — confirm which the bundle's TemplateNameParser supports. If both Twig and PHP templates live in templates/, keep extensions distinct (.html.php vs .html.twig) so neither loader picks up the wrong file. IDE indexers and static analysis tools will flag .php files under templates/; add an exclusion.

extend() must be first. Anything before it (even a blank line with echo) will produce a template that renders without a layout and is painful to debug. PHP templates fail with real line numbers, though — use that. php bin/console cache:clear is not usually needed for template changes, but is for container/service changes.

Debugging blank output. If a route returns an empty body, check in this order: (1) $view is undefined (you used include instead of $view->render), (2) extend() called after output, (3) a helper you referenced is not registered — the engine throws InvalidArgumentException for unknown helpers, which is easy to swallow in a try/catch higher up.

Extension points to know. Custom helpers via a class implementing HelperInterface registered on the engine's helper set; custom globals via GlobalVariables; and a compiler pass if you need to swap the template loader (e.g. to load PHP templates from a DB or a plugin directory).

Small-package risk. With 2 stars and no dependents, expect no security advisories, no LTS policy, and possible breakage on Symfony majors. If this bundle becomes load-bearing, either vendor the specific engine classes into your app (src/Templating/) or fork and track upstream. It is a thin wrapper around a component Symfony published for years — copying it is cheap insurance, and removes a transitive dependency you don't control.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console