Components · v1.x

View

The View component renders templates with a built-in PHP engine or with Twig when it is installed. View helpers written once work in both engines and are discovered automatically in every Helper/View/ directory.

Engines

Templates live in templates/. The extension selects the engine:

Extension Engine
.php built-in PHP engine
.html.twig, .twig Twig, only when twig/twig is installed (composer require twig/twig)

render('user/show') looks for user/show.php, then user/show.html.twig, then user/show.twig. A name with its extension (user/show.html.twig) is used as is. Rendering a .twig template without Twig installed throws an explicit ViewException; a missing template throws a TemplateNotFoundException listing the searched files.

Rendering

In a controller:

<?php

declare(strict_types=1);

namespace App\Controller;

use NeoPHP\Component\Controller\Contract\AbstractController;
use NeoPHP\Component\Http\Response\Response;

final class UserController extends AbstractController
{
    public function show(int $id): Response
    {
        return $this->render('user/show', ['id' => $id]);
    }
}
Method Description
render(string $template, array $parameters = [], int $status = 200, array $headers = []): Response renders a template into a response
renderView(string $template, array $parameters = []): string renders a template into a string

Outside a controller, inject NeoPHP\Component\View\Contract\ViewInterface:

$html = $this->view->render('emails/welcome', ['name' => $name]);

PHP templates

Inside a PHP template, the parameters are variables and $this gives access to:

Method Description
$this->extend('base', [...]) renders the template inside the base layout (layouts can be nested)
$this->start('name') / $this->stop() captures a section; start('name', true) appends to it
$this->section('name', 'default') outputs a section in a layout
$this->hasSection('name') whether a section is defined
$this->include('partials/menu', [...]) renders a partial
$this->e($value) escapes a value for HTML
$this->filter('name', $value, ...) applies a view filter
$this->name(...) calls a view function (path(), asset(), config()...)

templates/base.php

<!DOCTYPE html>
<html>
<head><title><?= $this->e($title ?? 'NeoPHP') ?></title></head>
<body>
<?= $this->section('content') ?>
</body>
</html>

templates/user/show.php

<?php $this->extend('base', ['title' => 'User']) ?>

<?php $this->start('content') ?>
<h1>User #<?= $this->e($id) ?></h1>
<a href="<?= $this->path('home') ?>">Home</a>
<?php $this->stop() ?>

Twig templates

templates/base.html.twig

<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}{{ config('framework.app.name') }}{% endblock %}</title>
    <link rel="stylesheet" href="{{ asset('css/app.css') }}">
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>

templates/user/show.html.twig

{% extends 'base.html.twig' %}

{% block body %}
<h1>User #{{ id }}</h1>
<a href="{{ path('home') }}">Home</a>
{% endblock %}

Twig errors are wrapped in a ViewException.

View helpers

A view helper works with every engine and never imports Twig. Its interfaces define what it provides:

Interface Result
ViewFunctionInterface function: {{ name(...) }} / $this->name(...)
ViewFilterInterface filter: {{ value|name }} / $this->filter('name', $value)
ViewGlobalInterface global variable (getValue()): {{ name }} / $name
ViewSafeHtmlInterface the output is not escaped

getName() gives the name used in templates. Functions and filters are called through __invoke().

Writing a helper

src/Shop/Helper/View/PriceViewHelper.php

<?php

declare(strict_types=1);

namespace App\Shop\Helper\View;

use NeoPHP\Component\View\Contract\ViewFilterInterface;
use NeoPHP\Component\View\Contract\ViewFunctionInterface;

class PriceViewHelper implements ViewFunctionInterface, ViewFilterInterface
{
    public function getName(): string
    {
        return 'price';
    }

    public function __invoke(float $amount, string $currency = '€'): string
    {
        return number_format($amount, 2, ',', ' ') . ' ' . $currency;
    }
}
{{ price(12.5) }}  {{ amount|price('$') }}
<?= $this->price(12.5) ?>  <?= $this->filter('price', $amount, '$') ?>

Discovery

The View component contains no helper: it discovers every Helper/View/ directory of the framework and of the application (src/**/Helper/View/). Dependencies are autowired. An application helper with the same name as a framework helper replaces it.

The file name must match the class (PSR-4): in debug, a helper file that declares another class (e.g. MardownViewHelper.php containing MarkdownViewHelper) throws an error naming the file; in production it is ignored.

Helpers stored outside a Helper/View/ directory can be listed under helpers in config/framework/view.yaml.

Framework helpers

Helper Name
Asset asset()
Config config()
Cookie cookie()
Flash flashes()
Routing path(), url()
Session session()
Csrf csrf_token(), csrf_field()
Form form_*()
Security (package) app_user(), is_granted(), logout_path(), last_username(), last_authentication_error()
Debug (package) dump()

See the documentation of each feature.

Configuration

config/framework/view.yaml

paths:
  - '%kernel.templates_path%'

namespaces:
  admin: '%kernel.root_path%/templates/admin'

helpers: []

twig:
  enabled: true
  cache: '%kernel.root_path%/var/cache/twig'
  auto_reload: true
  strict_variables: '%kernel.debug%'
Option Description
paths template directories
namespaces named directories: @admin/dashboard renders templates/admin/dashboard.*
helpers additional helper classes
twig.enabled false disables Twig even when it is installed
twig.* Twig options: cache, debug, auto_reload, strict_variables, autoescape, charset

API

ViewInterface

NeoPHP\Component\View\Contract\ViewInterface, implemented by ViewManager(array $paths = [], ?array $engines = null) (extends AbstractView, PHP engine by default):

Method Description
render(string $template, array $parameters = []): string renders a template
exists(string $template): bool whether a template exists
locate(string $template, ?string $engine = null): string absolute file of a template
addPath(string $path, ?string $namespace = null): static adds a template directory
getPaths(): array template directories
addGlobal(string $name, mixed $value): static adds a global variable
addHelper(string $name, callable $helper, bool $safe = false): static adds a function
addFilter(string $name, callable $filter, bool $safe = false): static adds a filter
addExtension(ViewHelperInterface $extension): static adds a helper object
addEngine(EngineInterface $engine): static adds an engine
getEngines(): array registered engines
$view->addGlobal('site_name', 'My shop');
$view->addHelper('year', static fn (): string => date('Y'));
$view->addFilter('shout', static fn (string $text): string => strtoupper($text));

Helper contracts

Interface Methods
ViewHelperInterface getName(): string
ViewFunctionInterface, ViewFilterInterface extend ViewHelperInterface; __invoke(...)
ViewGlobalInterface getValue(): mixed
ViewSafeHtmlInterface marker: output not escaped

EngineInterface

NeoPHP\Component\View\Engine\EngineInterface (implemented by PhpEngine and TwigEngine):

Method Description
getName(): string engine name
getExtensions(): array handled file extensions
render(string $template, string $file, array $parameters): string renders a file
addFunction(), addFilter(), addGlobal(), addPath() receive the helpers and paths of the view

TwigEngine(array $options = []) exposes getEnvironment(): Twig\Environment. PhpEngine limits nested layouts to MAX_LAYOUT_DEPTH (20).

Other classes

Class Description
Discovery\HelperDiscovery finds helper classes: addSource(string $path, string $namespace), getSources(), discover(): array, setStrict(bool) / isStrict()
Template\Template the $this of PHP templates
Provider\ViewProvider builds the view from framework.view
Exception\ViewException engine or template error
Exception\TemplateNotFoundException template not found

Changelog

  • Bugfix after v1.17.0 — In debug, a view helper file whose name does not match its class throws an error.
  • v1.4.0 — render() and renderView() provided by the ViewController trait.
  • v1.2.0 — Optional Twig engine, engine-agnostic view helpers discovered in each Helper/View/ directory, config/framework/view.yaml; the asset() helper moves to the Asset feature.
  • v1.0.0 — PHP views with layouts, sections and partials.