PHPackages                             demeve/template - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Templating &amp; Views](/categories/templating)
4. /
5. demeve/template

ActiveLibrary[Templating &amp; Views](/categories/templating)

demeve/template
===============

A PHP library to load and render view files split into named sections (output, style, script, etc.).

v0.10.0(1w ago)061↓75%MITPHPPHP ^8.1

Since Apr 28Pushed 1w agoCompare

[ Source](https://github.com/demve/template)[ Packagist](https://packagist.org/packages/demeve/template)[ Docs](https://github.com/demve/template)[ RSS](/packages/demeve-template/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (1)Versions (20)Used By (0)

Demeve Template
===============

[](#demeve-template)

[![PHP 8.1+](https://camo.githubusercontent.com/83dd395020c37276225039739320f6c8e7e99963ab21ee3d09282cb48dad2a60/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e312532422d626c7565)](https://www.php.net/)[![License: MIT](https://camo.githubusercontent.com/5caa455d8debc46fb23abbadb45a733a937f3910a73fc875c2f7820468e1bb54/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e)](LICENSE)[![Packagist](https://camo.githubusercontent.com/6ecbb42c39e316b4fbfabe9efd7208349a2026d55fd3cf93876a528fd6d413bd/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f64656d6576652f74656d706c617465)](https://packagist.org/packages/demeve/template)

A lightweight PHP template engine built around **Single File Components** — HTML, CSS, and JS in one file, split into named sections.

---

What is this?
-------------

[](#what-is-this)

Demeve Template lets you build PHP views as self-contained component files. Each file is divided into named sections (`output`, `style`, `script`, etc.) using HTML comment markers. The engine parses these files once and writes per-section cache files that are plain PHP — eligible for OPcache and easy to inspect.

Components can extend layouts, include other components, and accumulate `style`/`script` sections from every component on the page into a single emission point. There is no template syntax to learn beyond a handful of comment directives; everything else is native PHP.

---

Features
--------

[](#features)

- **Single File Components**: HTML, CSS, and JS in one `.php` or `.html` file
- **Layout inheritance**: `` with named slots and accumulated blocks
- **Comment directives**: IDE-friendly syntax that compiles to PHP calls
- **CSS scoping**: `__self` placeholder replaced with the component name — no collisions
- **Smart caching**: per-section `.cache.php` files, invalidated by mtime
- **Modifiers**: post-process sections (minify, annotate, deduplicate)
- **File modifiers**: bundle CSS/JS to disk with cache-busting URLs
- **Auto-load**: dependencies discovered from `render()` calls, no manual declaration needed
- **Autoload folder**: drop components in `autoload/` to inject global CSS/JS without any `` declaration
- **Asset management**: copy assets to public directory with mtime-based versioning
- **Preview mode**: built-in interactive component browser with live controls

---

Installation
------------

[](#installation)

```
composer require demeve/template
```

Requires **PHP 8.1 or higher**. No other dependencies.

---

Quick Start
-----------

[](#quick-start)

**1. Create a layout** (`components/layout.php`):

```

>

    slot('title', 'My App') ?>
    block('style') ?>

    slot('content') ?>
    block('script') ?>

```

**2. Create a component** (`components/home.php`):

```

Welcome

.__self { padding: 2rem; }
.__self h1 { font-size: 2rem; }

    Hello, e($name) ?>!

```

**3. Render it**:

```
use Demeve\Template\Template;

$t = new Template(
    path:  __DIR__ . '/components',
    cache: __DIR__ . '/.cache',
);

echo $t->render('Home', ['name' => 'World']);
```

---

Core Concepts
-------------

[](#core-concepts)

### Components &amp; Sections

[](#components--sections)

A component is a single `.php` or `.html` file divided into named sections using `\` markers. The `output` section is the rendered HTML. Any other name (`style`, `script`, `meta`, etc.) is collected globally and emitted elsewhere.

```

    .__self { color: red; }

Hello

    console.log('loaded');

```

Sections accumulate across **all loaded components**. Every `style` block from every widget on the page can be emitted together with a single `$this->block('style')` call in the layout.

---

### Comment Directives

[](#comment-directives)

Directives are HTML comments that compile to PHP calls. They keep files syntax-highlighted in any editor.

SyntaxCompiles toPurpose``section boundaryDeclare a named section``layout declarationMust appear before any section``pre-load callRegister sections without rendering output````Side-effect call````Echo call``multi-argument callPipe-separated arguments``strippedNever reaches cacheCommon shorthand examples:

```
             renders a sub-component
       emits a slot with fallback
                    emits accumulated style sections
                emits with modifier applied
                emits via file modifier
                      echoes $this->e($this->get('name'))
```

---

### CSS Scoping with `__self`

[](#css-scoping-with-__self)

Use `__self` as the root class in every component's `style` section and HTML markup. The parser replaces it with the **component name verbatim** (as passed to `render()`/`load()`):

Component`__self` becomes`WidgetsCard``WidgetsCard``PageHeader``PageHeader``Button``Button`> **Note:** Use PascalCase component names (no dots). `WidgetsCard` → `.__self` becomes `.WidgetsCard` — a single valid CSS class. Dots produce compound selectors that don't work as class names.

```

.__self          { display: flex; gap: 1rem; }
.__self .title   { font-size: 1.25rem; font-weight: bold; }

    e($title) ?>

```

Do **not** use `__self` for global reset styles or intentionally shared utility classes.

---

### Layouts &amp; Inheritance

[](#layouts--inheritance)

A component declares its parent with `\` on the first line. The layout wraps the child's output automatically.

**Child** (`components/pages/home.php`):

```

Home Page

    render('WidgetsCard', ['title' => 'Hello']) ?>

```

**Layout** (`components/layouts/app.php`):

```

>

    slot('title', 'App') ?>
    block('style') ?>

    slot('content') ?>
    block('script') ?>

```

Layouts can themselves extend other layouts. The chain resolves automatically.

Circular extends (A extends B, B extends A) are detected at load time: the engine logs an error via `errors()` and breaks the cycle rather than looping forever.

---

### Slots vs Blocks

[](#slots-vs-blocks)

`slot()``block()`**Purpose**Single value from one componentAccumulated from all loaded components**Typical use**Page title, main contentCSS, JS, meta tags**Template call**`$this->slot('title', 'Default')``$this->block('style')`**Directive**`````block()` returns a placeholder string at call time. After the full render tree completes, the engine replaces all placeholders with the accumulated section content. This means components loaded *after* the block directive fires still contribute their sections correctly.

---

### Modifiers

[](#modifiers)

A modifier post-processes all sections of a given name before emission. Implement `ModifierInterface`:

```
use Demeve\Template\ModifierInterface;

class CssMinifier implements ModifierInterface
{
    public function process(array $sections): string
    {
        $combined = implode("\n", $sections);
        $combined = preg_replace('/\/\*.*?\*\//s', '', $combined);
        return trim(preg_replace('/\s+/', ' ', $combined));
    }
}
```

`$sections` is an associative array keyed by component name → raw section content.

Register and use:

```
$t->addModifier('css', new CssMinifier());
```

```

```

Built-in modifiers in `src/Modifier/`:

ClassTypeEffect`CssModifier``ModifierInterface`Minifies CSS`JsModifier``ModifierInterface`Minifies JS (handles strings, template literals, regex literals)`HtmlModifier``ModifierInterface`Prepends HTML comment with component name`CssFileModifier``FileModifierInterface`Bundles CSS to a single file, returns `` tag or URL`JsFileModifier``FileModifierInterface`Bundles JS to a single file, returns `` tag or URL---

### File Modifiers (Bundling)

[](#file-modifiers-bundling)

`FileModifierInterface` receives **file paths** instead of content, enabling memory-efficient bundling. The modifier reads files only when regeneration is needed.

```
use Demeve\Template\Modifier\CssFileModifier;
use Demeve\Template\Modifier\JsFileModifier;

$t->addModifier('css', new CssFileModifier([
    'output_dir'        => __DIR__ . '/public/dist',
    'public_url_prefix' => '/dist',
    'read_file'         => false,  // return URL; true returns file contents
]));

$t->addModifier('js', new JsFileModifier([
    'output_dir'        => __DIR__ . '/public/dist',
    'public_url_prefix' => '/dist',
    'read_file'         => false,
]));
```

In the layout:

```
