Views & Frontend
Native View Engine & Bluebird CSS
LilaPHP includes a high-performance native PHP View Engine in _core/View.php. It
eliminates template compiler latency, provides seamless layout inheritance, partial includes,
heredoc execution, tiered multi-driver caching (APCu, Redis, File), automatic asset versioning, SEO
metadata generation, PWA manifests, CSRF context, and live development hot reload.
1. Architectural Philosophy
Instead of compiling third-party template languages into PHP at runtime, LilaPHP views are
native PHP scripts located in app/views/. They execute at C-speed, are compiled
directly by OPcache, and have zero external dependencies.
- Zero Compiler Overhead: Native PHP template rendering with output buffering.
- Layout Inheritance: Wrap templates in a common outer layout
(
app/views/layout.php) or specify custom layouts per route. - Embeddable Views: Render partials or embed views without outer layouts via
['layout' => false]. - Tiered Caching: Fast multi-level view caching across APCu, Redis, and filesystem storage.
- Built-in Security: Automatic injection of
$csrf_tokenand$csrf_inputin all template scopes.
2. Rendering Views (`View::render`)
Invoke View::render() or the global helper view() inside your web route
files located in app/routes/*.php:
<?php
use Core\Request;
use Core\View;
Request::GET(function () {
$products = [
['id' => 1, 'name' => 'Ultralight Laptop', 'price' => 999.00],
['id' => 2, 'name' => 'Wireless Headphones', 'price' => 149.50],
];
View::render('products', [
'products' => $products,
'category' => 'Electronics'
], [
'title' => 'Product Catalog — LilaPHP',
'description' => 'Browse our catalog of high-performance electronics.',
'keywords' => 'electronics, laptops, gadgets',
'canonical' => 'https://example.com/products',
'cache' => 300
]);
});
The view template file is resolved from app/views/products.php or
app/views/products/index.php. Variables passed in the data array are automatically
extracted into local scope.
3. Layouts & Slots
By default, View::render() wraps the view inside app/views/layout.php. The
rendered view content is passed into the layout as $content.
<!doctype html>
<html lang="<?= e($lang ?? 'en') ?>" data-theme="light">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?= $seo_meta ?? '' ?>
<?= $csrf_meta ?? '' ?>
<link rel="stylesheet" href="<?= asset('/css/bluebird.css') ?>">
</head>
<body>
<header class="navbar">
<a href="/" class="brand"><?= e($app_name ?? 'LilaPHP') ?></a>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
</header>
<main class="container">
<?= $content ?>
</main>
<footer class="footer">
<p>© <?= date('Y') ?> <?= e($app_name ?? 'LilaPHP') ?></p>
</footer>
<?= $hot_reload ?? '' ?>
</body>
</html>
Custom Layouts and Embed Mode
You can specify a different layout via the options array or disable layouts completely:
View::render('dashboard', $data, ['layout' => 'admin']);
View::render('components/table', $data, ['layout' => false]);
4. Heredoc View Execution (`View::html`)
Inside template files (such as app/views/about.php), you can render standard PHP
templates or use View::html() with closures or heredoc syntax for structured string
templates:
<?php
use Core\View;
echo View::html(function ($data) {
$version = e($data['version'] ?? '1.0.0');
$name = e($data['app_name'] ?? 'LilaPHP');
return <<<HTML
<section class="card">
<h1>About {$name}</h1>
<p>Running framework version <strong>{$version}</strong>.</p>
</section>
HTML;
}, get_defined_vars());
5. Component Partials (`View::partial`)
Break complex pages into reusable components using View::partial(). Partials are loaded
from app/views/partials/{name}.php or app/views/{name}.php:
<div class="catalog">
<?php foreach ($products as $item): ?>
<?= View::partial('product_card', ['item' => $item]) ?>
<?php endforeach; ?>
</div>
6. Asset Fingerprinting & Versioning (`View::asset`)
Prevent stale browser caching using View::asset() or the global helper
asset(). LilaPHP automatically appends a timestamp hash of the physical file in
public/:
<link rel="stylesheet" href="<?= asset('/css/bluebird.css') ?>">
<script src="<?= asset('/js/app.js') ?>" defer></script>
Generated output in production:
<link rel="stylesheet" href="/css/bluebird.css?v=68f2ba1c">
<script src="/js/app.js?v=9a0d8e21" defer></script>
7. SEO Metadata & Progressive Web App (PWA)
Pass metadata directly into View::render() options to automatically generate canonical
links, meta tags, and structured JSON-LD data:
View::render('article', ['post' => $post], [
'title' => $post['title'] . ' — My Blog',
'description' => $post['summary'],
'keywords' => 'php, webdev, performance',
'canonical' => 'https://example.com/blog/' . $post['slug'],
'jsonLD' => [
'@context' => 'https://schema.org',
'@type' => 'Article',
'headline' => $post['title'],
'datePublished' => $post['created_at'],
],
'pwa' => true,
'manifest' => '/manifest.json'
]);
When pwa => true is specified, the view automatically links
public/manifest.json and injects the client-side install banner handler.
8. CSRF Protection in Views
Every view and layout automatically receives CSRF helper variables in its scope:
$csrf_input: Renders<input type="hidden" id="_csrf" name="_csrf" value="...">.$csrf_token: Raw CSRF token string for JavaScript fetch/axios headers.$csrf_meta: Renders<meta name="csrf-token" content="...">for the document head.
<form method="POST" action="/contact">
<?= $csrf_input ?>
<div class="form-group">
<label for="email">Email Address</label>
<input type="email" id="email" name="email" required>
</div>
<button type="submit" class="btn primary">Send Message</button>
</form>
9. Tiered View Caching
Pass the cache option with a TTL in seconds to enable automated multi-driver view
caching. The cache driver is read from CACHE_DRIVER in .env by default
and can be overridden per call:
// Public page — safe to cache globally (no user-specific content)
View::render('home', $data, [
'cache' => true,
'cacheTtl' => 600, // 10 minutes
'driver' => 'apcu', // Override .env CACHE_DRIVER for this view
]);
// Per-user personalised cache (scoped to user ID to prevent data leaks)
View::render('dashboard', $data, [
'cache' => true,
'cacheTtl' => 60,
'userKey' => (string) ($_SESSION['user_id'] ?? ''),
]);
The engine resolves cached HTML in order:
- APCu Shared Memory: Sub-millisecond RAM retrieval with zero network latency.
- Redis Memory Cluster: Shared distributed cache across multiple web workers.
- File System Cache: Atomic disk storage under
_core/cache/data/.
⚠️ Session & User Data Safety
View caching stores the fully rendered HTML string — including any
user-specific content interpolated from $data or $_SESSION. If two
users request the same route while caching is active, the first rendered HTML is returned to
all subsequent callers until the TTL expires, potentially exposing private
data.
-
Never enable
'cache' => truefor pages that render user-specific data (dashboards, profile pages, authenticated views, shopping carts, etc.) -
If you need per-user caching (e.g. personalised but cacheable content), always supply
'userKey' => $userIdto namespace the cache key per user. - For purely public pages with no session-dependent content (landing pages, blog posts, product listings) view caching is safe and recommended.
10. Development Hot Reload
When APP_DEBUG=true is set in .env, LilaPHP automatically injects a
lightweight background poll client in views. It monitors file modifications across
app/views/ and public/css/, automatically reloading the browser when files
change.
php cli.php dev 8080
11. Bluebird CSS Micro-Framework
Docs
LilaPHP includes public/css/bluebird.css, a semantic zero-dependency CSS
micro-framework providing responsive layouts, dark/light theme switching, cards, forms, tables,
modals, and toasts:
<div class="grid grid-cols-2 gap-4">
<div class="card">
<h3>Card Title</h3>
<p>Card content styled automatically.</p>
<button class="btn primary">Action</button>
</div>
</div>
Toggle themes by setting the data-theme attribute on the <html>
element:
document.documentElement.setAttribute('data-theme', 'dark');