Native PHP templates with a handful of Blade-inspired directives. Files live under resources/views/ and can opt into layouts, sections, includes, and view caching.
use Zero\Lib\View;Implementation: View.php, compiler at ViewCompiler.php.
Render a template. The view name is the path under resources/views/, with / or . as the separator. Returns the compiled HTML.
$html = View::render('pages.home', ['user' => $user]);
$html = View::render('pages/home', ['user' => $user]); // equivalentInside the template, every key in $data is available as a local variable:
<!-- resources/views/pages/home.php -->
<h1>Hello, <?= htmlspecialchars($user->name) ?></h1>In controllers prefer the global view() helper, which wraps render() in a Response:
return view('pages.home', ['user' => $user]);Render a Blade-style string template (no file lookup). Useful for emails compiled at runtime.
$html = View::renderString('<h1>{{ $title }}</h1>', ['title' => 'Hi']);Render a partial inline (echoes directly). Inside templates use the @include('partials.header') directive — View::include() is the runtime call it compiles to.
View::include('partials.header', ['user' => $user]);Set the parent layout for the current render. Usually invoked from inside a template via the @layout('layouts.app') directive.
// inside a template
@layout('layouts.app')Start / finish capturing a named section. Wired up via @section(...) / @endsection.
@section('title')
Dashboard
@endsectionOutput the captured section content. Used inside layouts via @yield(...).
<!-- resources/views/layouts/app.php -->
<title>@yield('title')</title>Override the view config at runtime. Keys: cache_enabled, cache_path, cache_lifetime, debug. Defaults are resolved from environment variables on first use (see Configuration below).
View::configure([
'cache_enabled' => true,
'cache_lifetime' => 3600,
]);View::share(string $key, mixed $value): void / View::shared(string $key, mixed $default = null): mixed
Share a value with every template in the current render (the page, its includes, and its layout). Because the view runs before the layout, a page can share() at the top and the layout/head will see it. Shared state is cleared between renders.
// inside a page template
@php(View::share('pageTitle', 'Dashboard'))
// inside the layout
<title>{{ View::shared('pageTitle', 'Zero') }}</title>Append to a shared array bucket — handy when several places contribute to the same hook (extra <link> tags, preload hints, body classes). Read it back with View::shared($key, []).
@php(View::push('head', '<link rel="preload" href="/app.css" as="style">'))
{{-- in the layout --}}
@foreach (View::shared('head', []) as $tag)
{!! $tag !!}
@endforeachRegister a custom Blade-style directive. The callback receives the raw argument string (everything between the parentheses) and returns the PHP snippet to inline. The compiler picks these up automatically. Register directives once during bootstrap.
View::directive('uppercase', fn ($args) => "<?= strtoupper({$args}) ?>");
{{-- template --}}
@uppercase($user->name)Run a callback right before a matching view renders — useful for injecting shared state without editing every page. $pattern matches the dotted view path and supports * wildcards; pass * to match all views.
View::composer('pages.*', function (string $view) {
View::share('year', date('Y'));
});Drop every compiled view in the configured cache path.
View::clearCache();Drop the cache file for one view.
View::clearViewCache('pages.home');Compiled by ViewCompiler. All directives transparently compile to native PHP.
{{ $name }} {{-- escaped --}}
{!! $trustedHtml !!} {{-- raw --}}
{{{ $alsoEscaped }}} {{-- legacy: escaped --}}
@{{ literal }} {{-- escape the curly braces themselves --}}@if ($user)
Welcome, {{ $user->name }}
@elseif ($guest)
Hi guest
@else
Sign in
@endif
@for ($i = 0; $i < 5; $i++)
{{ $i }}
@endfor
@foreach ($items as $item)
{{ $item }}
@empty
Nothing here yet.
@endforeach@empty only fires when the iterable yields zero items.
{{-- resources/views/layouts/app.php --}}
<!doctype html>
<html>
<head><title>@yield('title')</title></head>
<body>
@yield('body')
</body>
</html>{{-- resources/views/pages/home.php --}}
@layout('layouts.app')
@section('title') Dashboard @endsection
@section('body')
<h1>Hello, {{ $user->name }}</h1>
@include('partials.footer')
@endsection@include('partials.header')
@include('partials.alert', ['type' => 'error', 'message' => $msg])See i18n.md for the full picture.
@t('common.welcome') {{-- translates --}}
@i18n('mail/welcome') {{-- switch translation file --}}
@i18n(['title' => 'Hello'])
{{ $title }}
@endi18n@php
$count = count($items);
@endphp
@php($count = count($items)) {{-- single-expression form --}}
{{ $count }}
@dd($payload) {{-- dump and exit --}}View settings are read from environment variables the first time the view system is used. Override them per-request with View::configure() if needed.
| Env var | Config key | Default | Purpose |
|---|---|---|---|
VIEW_CACHE |
cache_enabled |
false |
Enable compiled-view caching. |
VIEW_CACHE_PATH |
cache_path |
storage/framework |
Base directory for the cache (compiled views land in <path>/views/cache). |
VIEW_CACHE_LIFETIME |
cache_lifetime |
86400 |
Seconds before a cached view is considered stale. 0 = never expires (only mtime invalidates). |
VIEW_DEBUG |
debug |
false |
Log compile/cache activity to the cache directory. |
VIEW_CACHE=true
VIEW_CACHE_LIFETIME=0Compiled templates live under storage/framework/views/cache (the VIEW_CACHE_PATH base plus /views/cache). Toggle caching with VIEW_CACHE=true or View::configure(['cache_enabled' => true]). Each compiled file is keyed by an MD5 of the view identifier and re-validated against the source mtime on every render. Clear it from the CLI with php zero cache:clear.
To bust the cache for one view (e.g. after deploying a hot fix without a deploy script):
View::clearViewCache('pages.home');To wipe everything:
View::clearCache();namespace App\Controllers;
use Zero\Lib\Http\Request;
class UserController
{
public function show(int $id, Request $request)
{
$user = \App\Models\User::find($id);
if ($request->wantsJson()) {
return $user; // → JSON
}
return view('users.show', compact('user'));
}
}Controllers can return:
- a
Response(view()returns one) - a string (HTML)
- a model / array (auto-JSON)
null(204 No Content)
The router runs everything through Response::resolve().