A powerful PHP template engine with asset compilation, template inheritance, and variable processing. CommentTemplate provides a simple yet flexible way to manage templates with built-in CSS/JS minification and caching.
- Template Inheritance: Use layouts and include other templates
- Asset Compilation: Automatic CSS/JS minification and caching
- Variable Processing: Template variables with filters and commands
- Base64 Encoding: Inline assets as data URIs
- Flight Framework Integration: Optional integration with Flight PHP framework
Install via Composer:
composer require knifelemon/comment-template<?php
require_once 'vendor/autoload.php';
use KnifeLemon\CommentTemplate\Engine;
// Initialize template engine
$template = new Engine();
$template->setPublicPath(__DIR__); // Root directory (where index.php is)
$template->setSkinPath('templates'); // Relative to public path
$template->setAssetPath('assets'); // Relative to public path
$template->setFileExtension('.php');
// Render template
$template->render('homepage', [
'title' => 'Welcome',
'content' => 'Hello World!'
]);<?php
require_once 'vendor/autoload.php';
use KnifeLemon\CommentTemplate\Engine;
$app = Flight::app();
$app->register('view', Engine::class, [], function (Engine $engine) {
$engine->setPublicPath(__DIR__); // Root directory (where index.php is)
$engine->setSkinPath('views'); // Relative to public path
$engine->setAssetPath('assets'); // Relative to public path
$engine->setFileExtension('.php');
});
$app->map('render', function(string $template, array $data) use ($app): void {
echo $app->view()->render($template, $data);
});<?php
require_once 'vendor/autoload.php';
use KnifeLemon\CommentTemplate\Engine;
$app = Flight::app();
$app->register('view', Engine::class, [
__DIR__, // Public path (root directory where index.php is)
'views', // Templates path (relative to public path)
'assets', // Asset path (relative to public path)
'.php' // File extension
]);
$app->map('render', function(string $template, array $data) use ($app): void {
echo $app->view()->render($template, $data);
});CommentTemplate supports different JavaScript loading strategies:
- Normal:
<!--@js(file)-->- Standard blocking script load - Async:
<!--@jsAsync(file)-->- Non-blocking, executes immediately when loaded - Defer:
<!--@jsDefer(file)-->- Non-blocking, waits for HTML parsing to complete - Top placement: Use
jsTop*variants to load scripts in the<head>section - Single files: Use
*Single*variants to skip minification and load individual files
Use layouts to create a common structure:
layout.php:
<!DOCTYPE html>
<html>
<head>
<title>{$title}</title>
</head>
<body>
<!--@contents-->
</body>
</html>page.php:
<!--@layout(layout)-->
<h1>{$title}</h1>
<p>{$content}</p><!--@css(/css/styles.css)--> <!-- Minified and cached -->
<!--@css(/css/*.css)--> <!-- Load all CSS files in folder (sorted alphabetically) -->
<!--@cssSingle(/css/critical.css)--> <!-- Single file, not minified --><!--@js(/js/script.js)--> <!-- Minified, loaded at bottom -->
<!--@js(/js/*.js)--> <!-- Load all JS files in folder (sorted alphabetically) -->
<!--@jsAsync(/js/analytics.js)--> <!-- Minified, loaded at bottom with async -->
<!--@jsDefer(/js/utils.js)--> <!-- Minified, loaded at bottom with defer -->
<!--@jsTop(/js/critical.js)--> <!-- Minified, loaded in head -->
<!--@jsTopAsync(/js/tracking.js)--> <!-- Minified, loaded in head with async -->
<!--@jsTopDefer(/js/polyfill.js)--> <!-- Minified, loaded in head with defer -->
<!--@jsSingle(/js/widget.js)--> <!-- Single file, not minified -->
<!--@jsSingleAsync(/js/ads.js)--> <!-- Single file, not minified, async -->
<!--@jsSingleDefer(/js/social.js)--> <!-- Single file, not minified, defer -->Wildcard Support:
- Use
*to match multiple files:<!--@css(/css/*.css)--> - Files are processed in alphabetical order for consistent output
- Works with all asset directives:
@css,@js,@jsAsync,@jsDefer, etc.
<!--@base64(images/logo.png)--> <!-- Inline as data URI --><!-- Inline small images as data URIs for faster loading -->
<img src="<!--@base64(images/logo.png)-->" alt="Logo">
<div style="background-image: url('<!--@base64(icons/star.svg)-->');">
Small icon as background
</div><!--@asset(images/photo.jpg)--> <!-- Copy single asset to public directory -->
<!--@assetDir(assets)--> <!-- Copy entire directory to public directory -->CommentTemplate also processes asset directives within CSS and JavaScript files during compilation:
CSS Example:
/* In your CSS files */
@font-face {
font-family: 'CustomFont';
src: url('<!--@asset(fonts/custom.woff2)-->') format('woff2');
}
.background-image {
background: url('<!--@asset(images/bg.jpg)-->');
}
.inline-icon {
background: url('<!--@base64(icons/star.svg)-->');
}JavaScript Example:
/* In your JS files */
const fontUrl = '<!--@asset(fonts/custom.woff2)-->';
const imageData = '<!--@base64(images/icon.png)-->';Benefits:
- Asset directives are processed during CSS/JS compilation
- Files are automatically copied to the public directory
- URLs are generated with correct asset paths
- Base64 encoding works in CSS/JS files too
CommentTemplate provides intelligent path handling for both relative and absolute paths:
The Public Path is the root directory of your web application, typically where index.php resides. This is the document root that web servers serve files from.
// Example: if your index.php is at /var/www/html/myapp/index.php
$template->setPublicPath('/var/www/html/myapp'); // Root directory
// Windows example: if your index.php is at C:\xampp\htdocs\myapp\index.php
$template->setPublicPath('C:\\xampp\\htdocs\\myapp');Templates path supports both relative and absolute paths:
$template = new Engine();
$template->setPublicPath('/var/www/html/myapp'); // Root directory (where index.php is)
// Relative paths - automatically combined with public path
$template->setSkinPath('views'); // → /var/www/html/myapp/views/
$template->setSkinPath('templates/pages'); // → /var/www/html/myapp/templates/pages/
// Absolute paths - used as-is (Unix/Linux)
$template->setSkinPath('/var/www/templates'); // → /var/www/templates/
$template->setSkinPath('/full/path/to/templates'); // → /full/path/to/templates/
// Windows absolute paths
$template->setSkinPath('C:\\www\\templates'); // → C:\www\templates\
$template->setSkinPath('D:/projects/templates'); // → D:/projects/templates/
// UNC paths (Windows network shares)
$template->setSkinPath('\\\\server\\share\\templates'); // → \\server\share\templates\Asset path also supports both relative and absolute paths:
// Relative paths - automatically combined with public path
$template->setAssetPath('assets'); // → /var/www/html/myapp/assets/
$template->setAssetPath('static/files'); // → /var/www/html/myapp/static/files/
// Absolute paths - used as-is (Unix/Linux)
$template->setAssetPath('/var/www/cdn'); // → /var/www/cdn/
$template->setAssetPath('/full/path/to/assets'); // → /full/path/to/assets/
// Windows absolute paths
$template->setAssetPath('C:\\www\\static'); // → C:\www\static\
$template->setAssetPath('D:/projects/assets'); // → D:/projects/assets/
// UNC paths (Windows network shares)
$template->setAssetPath('\\\\server\\share\\assets'); // → \\server\share\assets\Smart Path Detection:
- Relative Paths: No leading separators (
/,\) or drive letters - Unix Absolute: Starts with
/(e.g.,/var/www/assets) - Windows Absolute: Starts with drive letter (e.g.,
C:\www,D:/assets) - UNC Paths: Starts with
\\(e.g.,\\server\share)
How it works:
- All paths are automatically resolved based on type (relative vs absolute)
- Relative paths are combined with the public path
@cssand@jscreate minified files in:{resolvedAssetPath}/css/or{resolvedAssetPath}/js/@assetcopies single files to:{resolvedAssetPath}/{relativePath}@assetDircopies directories to:{resolvedAssetPath}/{relativePath}- Smart caching: files only copied when source is newer than destination
<!-- Copy entire assets folder -->
<!--@assetDir(assets)-->
<!-- Copy specific subdirectory -->
<!--@assetDir(images)-->
<!--@assetDir(fonts)--><!-- Copy and reference static assets -->
<img src="<!--@asset(images/hero-banner.jpg)-->" alt="Hero Banner">
<a href="<!--@asset(documents/brochure.pdf)-->" download>Download Brochure</a>
<!-- Copy entire directory (fonts, icons, etc.) -->
<!--@assetDir(assets/fonts)-->
<!--@assetDir(assets/icons)-->Directory structure example:
templates/
├── resources/
│ ├── images/
│ │ ├── logo.png
│ │ └── banner.jpg
│ ├── css/
│ │ └── style.css
│ └── js/
│ └── app.js
└── layout.php
After <!--@assetDir(resources)--> in template:
public/
└── assets/ # (configured asset path)
└── resources/ # (copied directory)
├── images/
├── css/
└── js/
<!--@import(components/header)--> <!-- Include other templates --><!-- Include reusable components -->
<!--@import(components/header)-->
<main>
<h1>Welcome to our website</h1>
<!--@import(components/sidebar)-->
<div class="content">
<p>Main content here...</p>
</div>
</main>
<!--@import(components/footer)-->{$title|upper} <!-- Convert to uppercase -->
{$content|lower} <!-- Convert to lowercase -->
{$html|striptag} <!-- Strip HTML tags -->
{$text|escape} <!-- Escape HTML -->
{$multiline|nl2br} <!-- Convert newlines to <br> -->
{$html|br2nl} <!-- Convert <br> tags to newlines -->
{$description|trim} <!-- Trim whitespace -->
{$subject|title} <!-- Convert to title case -->{$title|default=Default Title} <!-- Set default value -->
{$name|concat= (Admin)} <!-- Concatenate text -->{$content|striptag|trim|escape} <!-- Chain multiple filters -->Example:
<h1>{$title|upper}</h1>
<p>{$description|striptag}</p>Template comments are completely removed from the output and won't appear in the final HTML:
{* This is a single-line template comment *}
{*
This is a multi-line
template comment
that spans several lines
*}
<h1>{$title}</h1>
{* Debug comment: checking if title variable works *}
<p>{$content}</p>Note: Template comments {* ... *} are different from HTML comments <!-- ... -->. Template comments are removed during processing and never reach the browser.
public function __construct(string $publicPath = "", string $skinPath = "", string $assetPath = "", string $fileExtension = "")render(string $template, array $data = []): void
- Render template and output to browser
fetch(string $template, array $data = []): string
- Render template and return as string
setPublicPath(string $path): void
- Set public path for asset compilation
setSkinPath(string $path): void
- Set template directory path (supports both relative and absolute paths)
setFileExtension(string $extension): void
- Set template file extension
setAssetPath(string $path): void
- Set asset storage path (supports both relative and absolute paths)
getPublicPath(): string
- Get current public path
getSkinPath(): string
- Get current template directory path
getFileExtension(): string
- Get current template file extension
getAssetPath(): string
- Get current asset storage path
This will:
- Minify and combine CSS files
- Minify and combine JS files
- Cache compiled assets
- Inject
<link>and<script>tags automatically
composer testcomposer phpstancomposer test-coverageCommentTemplate includes integration with Tracy Debugger for development logging and debugging.
composer require tracy/tracy<?php
use KnifeLemon\CommentTemplate\Engine;
use Tracy\Debugger;
// Enable Tracy (must be called before any output)
Debugger::enable(Debugger::DEVELOPMENT);
// Use CommentTemplate as normal - logging happens automatically
$template = new Engine();
$template->setPublicPath(__DIR__);
$template->setSkinPath('templates');
$template->setAssetPath('assets');
$template->render('homepage', ['title' => 'Hello World']);CommentTemplate adds a custom panel to Tracy's debug bar with four tabs:
- Overview: Configuration, performance metrics, and counts
- Assets: CSS/JS compilation details with compression ratios
- Variables: Original and transformed values with applied filters
- Timeline: Chronological view of all template operations
- Template rendering (start/end, duration, layouts, imports)
- Asset compilation (CSS/JS files, sizes, compression ratios)
- Variable processing (original/transformed values, filters)
- Asset operations (base64 encoding, file copying)
- Performance metrics (duration, memory usage)
Note: Zero performance impact when Tracy is not installed or disabled.
See examples/tracy/ and examples/flightphp/ for complete working examples.
MIT License. See LICENSE file for details.
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
