typo3fluid/fluid
TYPO3Fluid is a standalone PHP templating engine extracted from TYPO3 CMS. It provides secure, flexible templates with ViewHelpers, layouts, sections and partials, plus extensibility and caching, making it suitable for MVC apps and reusable component rendering.
To integrate typo3fluid/fluid into a Laravel project, start by installing the package via Composer:
composer require typo3fluid/fluid
Create a Fluid Template (resources/views/hello.fluid.html):
<html>
<body>
<h1>{greeting}</h1>
</body>
</html>
Create a Controller Method (app/Http/Controllers/FluidController.php):
use Fluid\Fluid;
use Fluid\View\TemplatePaths;
public function renderHello()
{
$fluid = new Fluid();
$fluid->getRequest()->setFormat('html');
$templatePaths = new TemplatePaths();
$templatePaths->addRootPath(__DIR__ . '/../../resources/views');
$fluid->getTemplateResolver()->setTemplatePaths($templatePaths);
$fluid->getTemplateParser()->parseFile('hello.fluid.html');
$fluid->getView()->assign('greeting', 'Hello, Fluid!');
return response($fluid->render());
}
Route the Controller (routes/web.php):
use App\Http\Controllers\FluidController;
Route::get('/hello', [FluidController::class, 'renderHello']);
f:format or f:for.TemplateParser for parsing .fluid.html files.Fluid supports template inheritance via <f:layout> and <f:section>:
<!-- resources/views/layouts/base.fluid.html -->
<html>
<body>
<f:section name="content"></f:section>
</body>
</html>
<!-- resources/views/child.fluid.html -->
<f:layout name="base">...</f:layout>
<f:section name="content">
<h1>Child Content</h1>
</f:section>
Laravel Integration:
$fluid->getView()->assign('layoutName', 'base');
$fluid->getView()->assign('templateName', 'child');
Use <f:render> to include partial templates dynamically:
<!-- resources/views/partials/user.fluid.html -->
<p>User: {username}</p>
Controller:
$fluid->getView()->assignMultiple([
'username' => 'JohnDoe',
'partial' => 'partials/user'
]);
Render in main template:
<f:render partial="{partial}" arguments="{username: username}" />
Extend AbstractViewHelper to create reusable components:
// app/ViewHelpers/CustomViewHelper.php
use Fluid\ViewHelpers\AbstractViewHelper;
class CustomViewHelper extends AbstractViewHelper
{
public function render()
{
return $this->renderChildren();
}
}
Register in Fluid instance:
$fluid->getViewHelperResolver()->registerViewHelper(
new \App\ViewHelpers\CustomViewHelper()
);
Use in template:
<app:custom>Hello from CustomViewHelper!</app:custom>
Leverage Fluid’s component API (introduced in v4.6+) for modular templates:
<!-- resources/views/components/user-card.fluid.html -->
<div class="user-card">
<h2>{user.name}</h2>
<p>{user.bio}</p>
</div>
Controller:
$fluid->getView()->assign('user', [
'name' => 'Alice',
'bio' => 'Developer'
]);
Render via <f:render component="user-card" />.
Use Fluid for complex logic while keeping Blade for simplicity:
resources/views/fluid/.// In a Blade view
<?php
$fluidContent = app(\App\Services\FluidRenderer::class)->render('fluid/partial');
echo $fluidContent;
?>
Case Sensitivity in Template Names:
Fluid v5+ resolves templates case-insensitively (e.g., hello.fluid.html vs Hello.fluid.html). Ensure consistency in your TemplatePaths.
$templatePaths->addRootPath(__DIR__ . '/../../resources/views', 'app');
Variable Escaping:
Fluid auto-escapes variables by default. Use UnsafeHTML for raw HTML:
use Fluid\Core\ViewHelper\Arguments\UnsafeHTMLArgument;
$fluid->getView()->assign('rawHtml', new UnsafeHTMLArgument('<script>...</script>'));
Caching Quirks: Clear the Fluid cache after changes:
php artisan cache:clear
Or manually delete bootstrap/cache/fluid.
Template Parser Errors: Enable debug mode for detailed errors:
$fluid->getConfiguration()->setDebug(true);
Errors now include file paths and line numbers (v5.2+).
ViewHelper Argument Validation:
Use StrictArgumentProcessor (default in v5+) to catch type mismatches early. Example:
// Throws: "Argument 'age' must be of type int, string given."
<app:validateAge age="{user.age}" />
CLI Template Analysis: Run the built-in CLI tool to validate templates:
vendor/bin/fluid analyze resources/views --format=json
Cache Warmup: Pre-compile templates during deployment:
vendor/bin/fluid warmup \
--cacheDirectory=bootstrap/cache/fluid \
--path=resources/views
Template Path Prioritization: Override paths dynamically:
$templatePaths->addRootPath(__DIR__ . '/../../resources/views/overrides', 100);
Higher priority = earlier resolution.
Custom Template Resolver:
Implement TemplateInterface for custom template logic (e.g., database-backed templates).
$fluid->getTemplateResolver()->setTemplateSource(new CustomTemplateSource());
ViewHelper Modifiers:
Use @modify annotations to alter ViewHelper behavior:
/**
* @modify ViewHelper\Format\DateViewHelper
*/
class CustomDateViewHelper extends DateViewHelper {
// Override logic
}
Annotations API: Parse PHPDoc annotations for metadata:
use Fluid\Core\Annotations\AnnotationParser;
$parser = new AnnotationParser();
$annotations = $parser->parse($viewHelperClass);
Service Provider Integration:
Bind Fluid to Laravel’s container in AppServiceProvider:
public function register()
{
$this->app->singleton(Fluid::class, function ($app) {
$fluid = new Fluid();
$fluid->getTemplateResolver()->setTemplatePaths(
new TemplatePaths([__DIR__ . '/../../resources/views'])
);
return $fluid;
});
}
Blade + Fluid Hybrid:
Use Fluid in Blade views via a helper:
// app/Helpers/FluidHelper.php
class FluidHelper {
public static function render(string $template, array $data = []): string
{
$fluid = app(Fluid::class);
$fluid->getView()->assignMultiple($data);
return $fluid->renderTemplate($template);
}
}
Blade usage:
{!! FluidHelper::render('fluid/partial', ['var' => 'value']) !!}
Testing:
Mock Fluid in PHPUnit:
$fluid = Mockery::mock(Fluid::class);
$fluid->shouldReceive('renderTemplate')->andReturn('<div>Mocked</div>');
How can I help you explore Laravel packages today?