Skip to content

Views

Table of Contents

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-Type header (text/html)
  • Returns a Response object 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 the View facade 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>
@endif

Loops:

php
@foreach($items as $item)
    <li>{{ $item->name }}</li>
@endforeach

Complete 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>
@endif

Layout 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>&copy; 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>
@endsection

You can also pass additional data to the layout:

php
@extends('layouts.app', ['pageClass' => 'home-page'])

@section('content')
    <!-- content -->
@endsection

Sections

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>
@endsection

In 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>
@endcan

More 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 NameFile Path
welcomeresources/views/welcome.php
users.indexresources/views/users/index.php
users.createresources/views/users/create.php
layouts.appresources/views/layouts/app.php
partials.formresources/views/partials/form.php
emails.welcomeresources/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 safely

View 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:cache

This 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:clear

This 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/cache

CLI Commands

PhenixPHP provides CLI commands to manage views:

view:cache

Compiles all application views:

sh
php phenix view:cache

Features:

  • 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:clear

When 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>
@endsection

Included 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>