Github

Pesto - Expressive Templates

Modern PHP template engine that provides an intuitive and expressive way to build web application views. It offers a clean syntax using custom HTML attributes and supports advanced templating features like view composition, slots, conditional rendering, loops, and built-in security measures.

pesto

How to use with

Pesto can be easily integrated with your favorite framework.

Slim Framework Slim CodeIgniter CodeIgniter
Coming Soon
CakePHP
Coming Soon
Symfony
Coming Soon
Laravel

Clean & Expressive Syntax

Pesto provides a clean syntax using custom HTML attributes. It understands the context of {{ variables }} and escapes them to prevent XSS.

Intuitive Attributes

Use attributes like php-foreach and php-if directly in your HTML.

<ul>
    <li php-foreach="range(1, 10) as $number"
        php-if="$number > 7">
        Item {{ $number }}
    </li>
</ul>

Clarity with <template>

For greater clarity, use the <template> tag, which will not be included in the final render.

<ul>
    <template php-foreach="range(1, 10) as $number">
       <li php-if="$number > 7">Item {{ $number }}</li>
    </template>
</ul>

Installation & Usage

PHP ^8.4 is required. Pesto is available via Composer and has no third-party dependencies.

composer require millancore/pesto
use MillanCore\Pesto\PestoFactory;

$pesto = PestoFactory::create([
    templatesPath: __DIR__ . '/views',
    cachePath: __DIR__ . '/cache',
    // [ New CustomFilters(), ... ]
]);

$pesto->make('view.php', ['user' => $user]);

View Composition

Pesto makes it easy to reuse parts of your views.

The <template> Tag

The <template> tag allows you to define php-* attributes that will be evaluated, but the tag itself will not be included in the final render.

Input

<p php-if="$user->isAdmin()">Admin</p>

Output

<p>Admin</p>

Input

<template php-if="$user->isAdmin()">Admin</template>

Output

Admin

Partials & Slots

When working with views composed of other views, you can use partials and slots to avoid repetition.

Layout: layouts/app.html

<!DOCTYPE html>
<html lang="en">
<head>
    <title>{{ $title }}</title>
</head>
<body>
    <header>{{ $header | slot }}</header>
    <main>{{ $main | slot }}</main>
</body>
</html>

View: views/home.html

<template php-partial="layouts/app.html" php-with="['title' => 'Home']">
    <!-- Named slot -->
    <nav php-slot="header">
        <a href="/">Home</a>
        <a href="/about">About</a>
    </nav>

    <!--Main Slot -->
    <section>
        <h1>Home</h1>
        <p>Lorem ipsum...</p>
    <section>
</template>

Nested Views

Pesto allows you to nest views, reusing the same layout multiple times in the same view.

<template php-partial="list.html">
    <li>Item</li>
    <li>
        <ul php-partial="list.html">
            <li>nested item</li>
            ....
        </ul>
    </li>
</template>

Control Flow

Pesto provides foreach and if directives, sufficient for building any view.

If Attribute

Conditionally render blocks. php-elseif and php-else must be siblings of php-if.

<p php-if="$user->isAdmin()">Admin</p>
<p php-elseif="$user->isModerator()">Moderator</p>
<p php-else>Guest</p>

Loops

We can use to render a list of items based on an array or iterable objects.

<li php-foreach="$list as $item">
    {{ $item }}
</li>

Inline

Combine directives in one single tag.

<ul>
  <li php-foreach="$users as $user" php-if="$user->isAdmin()">
      {{ $user->name | title }};
  </li>
</ul>

Filters

Apply transformations to variables using the pipe | operator. You can also create your own.

Usage

<p>{{ $text | upper }}</p>

Chaining

<p>{{ $text | capitalize | truncate:50,... }}</p>

Arguments

<p>{{ $createAt | date:'m-d-Y' }}</p>

Built-in Filters

  • raw: Prevents escaping.
  • String: upper, lower, capitalize, title, trim, nl2br, strip_tags, slug, join.

Add Custom Filters

Create a class with public methods and register it.

1. Create a filter class

// CustomFilter.php
#[AsFilter(name: 'truncate')]
public function truncate(
    string $value,
    int $length,
    string $end = '...'
) : string
{
    //...
}

2. Register it in the factory

$pesto = PestoFactory::create([
    templatesPath: => __DIR__ . '/views',
    cachePath: => __DIR__ . '/cache', [
        new CustomFilter(),
    ]
]);