Appearance
Views
Table of Contents
- Introduction
- Basic Rendering
- Passing Data to Views
- Template Syntax
- Layout Inheritance
- Custom Directives
- Including Sub-Views
- Naming Conventions
- View Caching
- Configuration
- CLI Commands
Introduction
PhenixPHP is a framework designed primarily for creating RESTful APIs, however, it includes a lightweight view rendering system that can be useful in specific situations:
- Email Templates: The Mailing module uses the view system to render email content.
- Single Page Applications (SPAs): You might need to serve a simple interface that consumes your API.
- Landing Pages or Static Pages: Simple HTML content without complex logic.
Important Note: PhenixPHP's view system is not designed for web applications with intensive view usage or for creating traditional multi-page applications. For those cases, consider using frontend-specialized frameworks.
PhenixPHP's template engine compiles views into pure PHP files, providing excellent performance with a clear and familiar syntax.
Basic Rendering
Returning Views from Controllers
The most common way to render a view is using the response()->view() helper method. This is the recommended approach for returning views from your controllers as HTTP responses:
php
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Phenix\Http\Controller;
use Phenix\Http\Response;
class WelcomeController extends Controller
{
public function index(): Response
{
return response()->view('welcome');
}
}The response()->view() method automatically:
- Creates an HTTP response with the rendered HTML
- Sets the appropriate
Content-Typeheader (text/html) - Returns a
Responseobject ready to be sent to the client
Using the View Facade Directly
For advanced use cases where you need to access the raw HTML output (for example, to generate email content or manipulate the HTML before sending), you can use the View facade directly:
php
use Phenix\Facades\View;
// Get the rendered HTML as a string
$html = View::view('welcome')->render();
// Now you can manipulate the HTML or use it for other purposes
$modifiedHtml = ...;When to use each approach:
response()->view(): For returning views in controllers (most common)View::view()->render(): When you need the raw HTML string for processing
Note: Internally,
response()->view()uses theViewfacade to render the view and then wraps the result in an HTTP response object.
Views are stored in the resources/views directory by default. For example, a view named welcome corresponds to the file resources/views/welcome.php.
Passing Data to Views
You can pass data to views by providing an associative array as the second argument. This works with both approaches:
php
// In a controller using response()->view()
return response()->view('welcome', [
'title' => 'Welcome to PhenixPHP',
'user' => $user,
'items' => ['Item 1', 'Item 2', 'Item 3']
]);
// Or using the facade directly
$html = View::view('welcome', [
'title' => 'Welcome to PhenixPHP',
'user' => $user,
'items' => ['Item 1', 'Item 2', 'Item 3']
])->render();Inside the view, you can access these variables directly:
php
<!-- resources/views/welcome.php -->
<h1>{{ $title }}</h1>
<p>Hello, {{ $user->name }}</p>Template Syntax
Displaying Data
PhenixPHP provides two ways to display data in views:
Escaped Output (XSS-safe):
php
{{ $variable }}This syntax automatically escapes HTML content, protecting you against XSS attacks. Internally it uses PHP's e() function.
Unescaped Output (Raw HTML):
php
{!! $variable !!}Use this syntax only when you fully trust the content, for example, when rendering HTML generated by your application.
php
<!-- resources/views/post.php -->
<h1>{{ $post->title }}</h1>
<!-- Escaped content for security -->
<p>Author: {{ $post->author }}</p>
<!-- Unescaped HTML (use with caution) -->
<div class="content">
{!! $post->htmlContent !!}
</div>Control Structures
PhenixPHP provides convenient directives for common control structures:
Conditionals:
php
@if($user->isAdmin())
<p>Welcome, administrator</p>
@elseif($user->isModerator())
<p>Welcome, moderator</p>
@else
<p>Welcome, user</p>
@endifLoops:
php
@foreach($items as $item)
<li>{{ $item->name }}</li>
@endforeachComplete Example:
php
<!-- resources/views/users/index.php -->
<h1>User List</h1>
@if(count($users) > 0)
<ul>
@foreach($users as $user)
<li>
{{ $user->name }} - {{ $user->email }}
@if($user->isActive())
<span class="badge">Active</span>
@endif
</li>
@endforeach
</ul>
@else
<p>No registered users.</p>
@endifLayout Inheritance
The layout system allows you to create a base structure that can be reused by multiple views, avoiding code duplication.
Defining a Layout
First, create a base layout that defines the general structure of your pages:
php
<!-- resources/views/layouts/app.php -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@yield('title') - My Application</title>
<link rel="stylesheet" href="/css/app.css">
</head>
<body>
<header>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
</header>
<main>
@yield('content')
</main>
<footer>
<p>© 2026 My Application</p>
</footer>
@yield('scripts')
</body>
</html>The @yield('name') directive marks the positions where child views can inject content.
Extending a Layout
To create a view that extends the layout, use the @extends directive:
php
<!-- resources/views/home.php -->
@extends('layouts.app')
@section('title')
Home
@endsection
@section('content')
<h1>Welcome to the home page</h1>
<p>This is the main content of the page.</p>
@endsection
@section('scripts')
<script src="/js/home.js"></script>
@endsectionYou can also pass additional data to the layout:
php
@extends('layouts.app', ['pageClass' => 'home-page'])
@section('content')
<!-- content -->
@endsectionSections
Sections allow you to define content blocks that will be inserted into the parent layout:
php
<!-- Child view -->
@extends('layouts.app')
@section('title')
Dashboard
@endsection
@section('content')
<div class="dashboard">
<h1>Dashboard</h1>
@include('partials.widgets')
</div>
@endsection
@section('sidebar')
<aside>
<h3>Side Menu</h3>
<ul>
<li>Option 1</li>
<li>Option 2</li>
</ul>
</aside>
@endsectionIn the parent layout, use @yield('name') to render each section:
php
<!-- Parent layout -->
<div class="container">
<div class="sidebar">
@yield('sidebar')
</div>
<div class="main">
@yield('content')
</div>
</div>Custom Directives
You can create your own custom directives to extend the template engine's functionality:
php
use Phenix\Facades\View;
// Register a custom directive
View::directive('can', function (string $action): string {
return "<?php if({$action} === 'create'): ?>";
});
View::directive('endcan', function (): string {
return "<?php endif; ?>";
});Then you can use the directive in your views:
php
@can('create')
<button>Create new element</button>
@endcanMore Complex Example:
php
use Phenix\Facades\View;
// Directive for formatting dates
View::directive('datetime', function (string $expression): string {
return "<?php echo {$expression}->format('d/m/Y H:i'); ?>";
});Usage in views:
php
<p>Registration date: @datetime($user->createdAt)</p>For language files, pluralization, fallback locale, and runtime locale switching, see the Translation guide.
Including Sub-Views
The @include directive allows you to include other views within a view:
php
<!-- resources/views/dashboard.php -->
<h1>Dashboard</h1>
@include('partials.alerts')
<div class="content">
@include('partials.stats', ['stats' => $statistics])
</div>Sub-views have access to all variables from the parent view, in addition to the extra variables you explicitly pass to them:
php
<!-- resources/views/partials/stats.php -->
<div class="stats-widget">
<h3>Statistics</h3>
<p>Total users: {{ $stats['users'] }}</p>
<p>Total sales: {{ $stats['sales'] }}</p>
</div>Naming Conventions
PhenixPHP uses dot notation to reference views, which maps to the directory structure:
| View Name | File Path |
|---|---|
welcome | resources/views/welcome.php |
users.index | resources/views/users/index.php |
users.create | resources/views/users/create.php |
layouts.app | resources/views/layouts/app.php |
partials.form | resources/views/partials/form.php |
emails.welcome | resources/views/emails/welcome.php |
The .php extension is automatically added if you don't specify it.
Name Security:
The system normalizes and sanitizes view names automatically to prevent directory traversal attacks:
php
// This is safe
View::view('../../etc/passwd'); // Will be normalized and fail safelyView Caching
PhenixPHP compiles views into pure PHP files and stores them in cache to improve performance. Compiled views are saved in storage/framework/views/ by default.
Compiling Views
The view:cache command pre-compiles all views in your application, which is useful in production:
sh
php phenix view:cacheThis command:
- Clears the existing cache
- Finds all views in
resources/views/ - Compiles them in parallel using WorkerPool for faster speed
- Stores the compiled versions in cache
Recommendation: Run this command after each production deployment to ensure all views are pre-compiled.
Clearing Cache
If you modify views during development or need to force recompilation, use the view:clear command:
sh
php phenix view:clearThis command removes all compiled files from the cache directory.
During development: You don't need to manually clear the cache. The system automatically detects when a view has been modified (by comparing timestamps) and recompiles it if necessary.
Configuration
The views configuration is located in config/view.php:
php
<?php
declare(strict_types=1);
return [
// Directory where views are located
'path' => env('VIEW_PATH', static fn () => base_path('resources/views')),
// Directory where compiled views are stored
'compiled_path' => env('VIEW_COMPILED_PATH', static fn () => base_path('storage/framework/views')),
];Available Environment Variables:
text
# .env
VIEW_PATH=/custom/path/views
VIEW_COMPILED_PATH=/custom/path/cacheCLI Commands
PhenixPHP provides CLI commands to manage views:
view:cache
Compiles all application views:
sh
php phenix view:cacheFeatures:
- Pre-compiles all views found in
resources/views/ - Uses parallel processing for faster speed
- Clears existing cache before compiling
- Ideal for production environments
view:clear
Removes all compiled views from cache:
sh
php phenix view:clearWhen to use:
- After updating the framework
- When experiencing issues with outdated views
- During development if you need to force recompilation
Complete Example
Below is a complete example showing most features of the view system:
Base Layout:
php
<!-- resources/views/layouts/dashboard.php -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>@yield('title') - Dashboard</title>
<link rel="stylesheet" href="/css/dashboard.css">
</head>
<body class="{{ $pageClass ?? 'default' }}">
<nav>
@include('partials.navigation')
</nav>
<main>
@yield('content')
</main>
<footer>
@include('partials.footer')
</footer>
@yield('scripts')
</body>
</html>View that extends the layout:
php
<!-- resources/views/users/index.php -->
@extends('layouts.dashboard', ['pageClass' => 'users-page'])
@section('title')
Users
@endsection
@section('content')
<h1>User List</h1>
@if($users->count() > 0)
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Status</th>
</tr>
</thead>
<tbody>
@foreach($users as $user)
<tr>
<td>{{ $user->name }}</td>
<td>{{ $user->email }}</td>
<td>
@if($user->isActive())
<span class="badge success">Active</span>
@else
<span class="badge danger">Inactive</span>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
@else
<p>No users to display.</p>
@endif
@endsection
@section('scripts')
<script src="/js/users.js"></script>
@endsectionIncluded Partial:
php
<!-- resources/views/partials/navigation.php -->
<ul class="nav">
<li><a href="/" class="{{ $currentRoute === 'home' ? 'active' : '' }}">Home</a></li>
<li><a href="/users" class="{{ $currentRoute === 'users' ? 'active' : '' }}">Users</a></li>
<li><a href="/settings" class="{{ $currentRoute === 'settings' ? 'active' : '' }}">Settings</a></li>
</ul>