High performance view templating API for PHP applications using tags & expressions inspired by Java JSTL and C compiler

Overview

View Language API

Table of contents:

About

This API is the PHP compiler for ViewLanguage templating engine, a markup language inspired by JSP&JSTL @ Java that acts like an extension of HTML standard, designed to completely eliminate the need for scripting in views by:

  • interfacing variables through expressions.
  • interfacing logics (control structures, repeating html segments) through tags

diagram

In order to achieve its goals, following steps need to be observed:

API is fully PSR-4 compliant, only requiring PHP8.1+ interpreter and SimpleXML extension. To quickly see how it works, check:

  • installation: describes how to install API on your computer, in light of steps above
  • unit tests: API has 100% Unit Test coverage, using UnitTest API instead of PHPUnit for greater flexibility
  • examples: shows an example how to template with ViewLanguage, including explanations for each step
  • reference guide: shows list of tags that come with API

Expressions

An expression is a ViewLanguage representation of a scripting variable. Syntax for an expression is:

${variableName}

where variableName can be:

Description ViewLanguage Example PHP Translation
a scalar variable ${foo} $foo
an array variable, where hierarchy is represented by dots ${foo.bar} $foo["bar"]
a back-end helper function (native or user-defined) ${htmlspecialchars($ {foo.bar})} htmlspecialchars($foo["bar"])
a short if using ternary operators ${($ {foo.bar}!=3?"Y":"N")} ($foo["bar"]!=3?"Y":"N")

A very powerful feature is the ability to nest expressions: writing expressions whose key(s) are expressions themselves. This can go at any depth and it is very useful when iterating through more than one list and linking a one to another's key/value association:

ViewLanguage Example PHP Translation
${foo.bar.$ {baz}} $foo["bar"][$baz]

Tags

A tag is a ViewLanguage representation of a scripting logic. All tags act like an extension of HTML standard and as such they have names and optionally attributes and bodies. There are two types of tags known by ViewLanguage:

  • macro tags: api-defined tags to be processed before content is compiled.
  • library tags: api/user-defined tags subject to compilation that belong to a library and have an unique name within that library.

A very powerful feature is the ability of tags to be recursive: it is allowed to put View Language tags inside View Language tags. Whenever that happens, compiler goes progressively deeper until no tags are left!

Macro Tags

Macro tags work in a way similar to C macros: before code is compiled, they are read and "expanded" so that compilation will run on a full source. Syntax is identical to that of normal HTML tags:

<NAME ATTRIBUTE="value" .../>

Where:

  • NAME: name of tag that performs a single logical operation.
  • ATTRIBUTE: configures tag behavior

API defines following macro tags:

  • escape: tag whose body will be ignored by compiler. This is necessary to mark content inside as not subject to parsing.
  • import: tag whose declaration will be replaced by compiler with the body of file pointed by its "file" attribute. This is crucial for layouting/templating.
  • namespace: tag whose declaration will inform compiler where to look for tag libraries not found in default folder.

At the moment, it is not possible for users to define their own macro tags!

Library Tags

Library tags are compilable api/user-defined HTML snippets expected to implement scripting logic in a View Language application. They are non-static repeating snippets of template (html) code that depend on variables and thus can't be loaded using .

Their syntax extends HTML standard:

<LIBRARY:TAG ATTRIBUTE="value" ...>...</LIBRARY:TAG>

or, if they have no body:

<LIBRARY:TAG ATTRIBUTE="value" .../>

Where:

  • LIBRARY: namespace that contains related logical operations to perform on a template. Rules:
    • Value must be lowercase and alphanumeric.
    • "-" sign is allowed as well to replace spaces in multi-word values
  • TAG: name of tag that performs a single logical operation.Rules:
    • Value must be lowercase and alphanumeric.
    • sign is allowed as well to replace spaces in multi-word values
  • ATTRIBUTE: configures tag behavior (can be zero or more). Rules:
    • Name must be lowercase and alphanumeric.
    • "_" sign is allowed as well to replace spaces in multi-word names
    • Value can only be primitive (string or number) or ViewLanguage expressions.
    • Unlike standard HTML, attributes cannot be multilined currently.

Standard Tags

API includes a standard library containing tags for programming language instructions where LIBRARY is empty:

  • :for: iterates numerically through a list
  • :foreach: iterates through a dictionary by key and value
  • :if: evaluates body fitting condition
  • :elseif: evaluates body fitting condition that did not met previous if/elseif.
  • :else: evaluates body that did not met previous if/else if
  • :set: creates a variable and/or sets a value for it.
  • :unset: unsets a variable.
  • :while: performs a loop on condition.
  • :break: ends loop.
  • :continue: skips evaluating the rest of current loop and moves to next iteration.

Standard tags work with ATTRIBUTE values of following types:

  • scalars: strings or integers
  • EXPRESSION: ViewLanguage expressions. If helper functions are used as attribute values, they must be left undecorated: count(${asd}) instead of ${count(${asd})}.
  • CONDITION: syntax is expected to be C-like, but ultimately matches that of language code is compiled into (in our case, PHP). Example: ${x}==true means in PHP $x==true.

User Defined Tags

In order to break up HTML response into discrete units, developers must create their own libraries & tags. User defined tags are found on disk according to these rules:

  • library name must be a folder inside tags folder supplied on compilation
  • tag code muse be a HTML file inside library folder whose name equals name
ViewLanguage Example PHP Translation
<foo:baz attr="1"/> $contents = file_get_contents($tagsFolder."/foo/baz.html");
// replaces all occurrences of $[attr] with 1

Configuration

To configure this API you must have a XML with a templating tag inside:

<templating compilations_path="..." tags_path="..." templates_path="..." templates_extension="..." />

Where:

  • compilations_path: (mandatory) path into which PHP compilations of ViewLanguage templates are saved
  • tags_path: (optional) path into which ViewLanguage tag libraries are located
  • templates_path: (optional) path into which templates are located
  • templates_extension: (optional) template files extension. If not set, "html" is assumed!

Example:

<templating compilations_path="compilations" tags_path="application/taglib" templates_path="application/views" templates_extension="html"/>

Compilation

Once you have completed step above, you need to instantiate Lucinda\Templating\Wrapper in order to be able to compile templates later on:

$wrapper = new Lucinda\SQL\Wrapper(simplexml_load_file(XML_FILE_NAME));

Object has following method that can be used to compile one or more templates:

Method Arguments Returns Description
compile string $template, array $data string Compiles ViewLanguage template into HTML and returns result

How are templates compiled?

As in any other templating language, compilation first traverses the tree of dependencies ever deeper and assembles result into a PHP file then produces an HTML by binding it to data received by user. It thus involves following steps:

  • if a PHP compilation for $template argument exists, checks if elements referenced inside have changed since it was last updated. If it doesn't exist or it changed:
    • parses tags recursively (backing-up tag bodies in compilation file to be excluded from parsing) and appends results to compilation file
    • parses tags defined in templates, to know where to locate user-defined tag libraries not defined in default taglib folder
    • parses library tags recursively (backing-up tag bodies in compilation file to be excluded from parsing) and replaces them with relevant PHP/HTML code in compilation file.
    • parses expressions and replaces them with relevant PHP code in compilation file.
    • restores backed up tags bodies (if any) in compilation file
    • caches new compilation on disk along with a checksum of its parts (templates, tags) for future validations
  • in output buffer, loads compilation file, binds it to $data supplied by user and produces a final HTML out of it

Since the whole process is somewhat performance hungry, PHP compilation files will be cached on disk and returned directly on next requests unless one of its components (template or tag) has changed. This makes API able to compile in around 0.001 sec amortised time, thus bringing no performance taxation whatsoever but all the advantages of an elegant view!

Installation

First choose a folder where API will be installed then write this command there using console:

composer require lucinda/templating

Then create a configuration.xml file holding configuration settings (see configuration above) and a index.php file in project root with following code:

require(__DIR__."/vendor/autoload.php");
$wrapper = new Lucinda\SQL\Wrapper(simplexml_load_file("configuration.xml"));

To compile a template:

$html = $wrapper->compile(TEMPLATE_NAME, USER_DATA);

Where:

  • TEMPLATE_NAME is the base template that must obey following rules:
    • must be a path to a file located in templates_path (see configuration)
    • file it points to must have templates_extension (see configuration)
    • because of above, must not include extension
  • USER_DATA is a list of values from back-end to be accessed in template, obeying following rules:
    • must be an array
    • entry keys must be strings or integers (the usual PHP requirements)
    • entry values must be scalars or arrays
    • if array is multidimensional, keys and values in siblings must obey same rules as above

To display results:

header("Content-Type: text/html; charset=UTF-8");
echo $html;

Unit Tests

For tests and examples, check following files/folders in API sources:

  • unit-tests.sql: SQL commands you need to run ONCE on server (assuming MySQL) before unit tests execution
  • test.php: runs unit tests in console
  • unit-tests.xml: sets up unit tests and mocks "sql" tag
  • tests: unit tests for classes from src folder

Examples

Assuming configuration.xml (see configuration and installation) is:

<xml>
   <templating compilations_path="compilations" tags_path="application/taglib" templates_path="application/views" templates_extension="html"/>
</xml>

Let's create a application/views/index.html template with following body:

<import file="header"/>
Hello, dear ${data.author}! Author of:
<ul>
    <:foreach var="${data.apis}" key="name" val="url">
    <my:api id="${name}" link="${url}"/>
    </:foreach>
</ul>
<import file="footer"/>

What above does is:

  • loads body of a application/views/header.html template
  • echoes value of author array key of $data via expressions
  • loops through value of apis array key of $data via <:foreach>
  • on every iteration, loads body of application/taglib/my/api.html template (user tag), binding attributes to values
  • loads body of a application/views/footer.html template

Contents of application/views/header.html file:

<html>
    <head>
        <title>View Language API Tutorial</title>
    </head>
    <body>

Contents of application/taglib/my/api.html file:

<li><a href="$[id]">$[link]</a></li>

Contents of application/views/footer.html:

    </body>
</html/>

As one can see above, templates depend on variables to be received from back-end (author & apis), both keys of $data array. Assuming value of latter is:

$data = [
	"author" => "Lucian Popescu", 
	"apis" => ["View Language API" => "https://www.lucinda-framework.com/view-language", "STDOUT MVC API" => "https://www.lucinda-framework.com/stdout-mvc"]
];

Now let's compile application/views/index.html template (see compilation) and bind it to $data:

require(__DIR__."/vendor/autoload.php");
$wrapper = new Lucinda\SQL\Wrapper(simplexml_load_file("configuration.xml"));
$html = $wrapper->compile("index", $data);

First, ViewLanguage base template is compiled into PHP, results saved in a compilations/index.php file (in case it doesn't exist already) with following body:

<html>
    <head>
        <title>View Language API Tutorial</title>
    </head>
    <body>
        Hello, <?php echo $data["author"]; ?>, author of:
        <ul>
            <?php foreach($data["apis"] as $name=>$url) { ?>
            <li><a href="<?php echo $url; ?>"><?php echo $name; ?></a></li>
            <?php } ?>
        </ul>
    </body>
</html/>

Then above file is loaded in output buffer and bound with $data, so the final HTML returned will be:

<html>
    <head>
        <title>View Language API Tutorial</title>
    </head>
    <body>
        Hello, Lucian Popescu, author of:
        <ul>
            <li><a href="https://www.lucinda-framework.com/view-language">View Language API</a></li>
            <li><a href="https://www.lucinda-framework.com/stdout-mvc">STDOUT MVC API</a></li>
        </ul>
    </body>
</html/>

Reference Guide

tag escape

Marks tag body to be ignored by ViewLanguage compiler.Syntax:

<escape>
...
</escape>

Examples how this tag is compiled into PHP:

ViewLanguage Example PHP Translation
<escape>
${foo.bar}
</escape>
${foo.bar}

tag import

Includes another view language template into current one. Syntax:

<import file="..."/>

Attributes:

Name Mandatory Data Type Description
file Y string Location of file whose sources should replace tag declaration relative to views folder supplied to compiler

Examples how this tag is compiled into PHP:

ViewLanguage Example PHP Translation
<import file="header"/> require_once($viewsFolder."/header.html")

tag namespace

Marks custom location of user defined tag library (must be placed BEFORE latter declaration). Syntax:

<namespace taglib="..." folder="..."/>

Attributes:

Name Mandatory Data Type Description
taglib Y string Name of tag library to look for.
folder Y string Location of folder tag library should be looked for relative to tags folder supplied to compiler

Examples how this tag is compiled into PHP:

ViewLanguage Example PHP Translation
<namespace taglib="foo" folder="bar"/>
...
<foo:baz attr="1"/>
...
$contents = file_get_contents($tagsFolder."/bar/foo/baz.html");
// replaces all instances of $[attr] with 1

tag :for

Creates a FOR loop. Syntax:

<:for var="..." start="..." end="..." (step="...")>
    ...
</:for>

Attributes:

Name Mandatory Data Type Description
var Y string Name of counter variable.
start Y integer Value of begin counter.
end Y integer Value of end counter.
step N integer Value of increment/decrement step (default: 1).

Examples how this tag is compiled into PHP:

ViewLanguage Example PHP Translation
<:for var="i" start="0" end="10">
...
</:for>
for($i=0; $i<=10; $i=$i+1){
...
}
<:for var="i" start="10" end="0" step="-1">
...
</:for>
for($i=10; $i>=0; $i=$i-1){
...
}

tag :foreach

Creates a FOR EACH loop. Syntax:

<:foreach var="..." (key="...") val="...">
    ...
</:foreach>

Attributes:

Name Mandatory Data Type Description
var Y EXPRESSION Variable to iterate.
key N string Name of key variable.
val Y string Name of value variable.

Examples how this tag is compiled into PHP:

ViewLanguage Example PHP Translation
<:foreach var="${a}" key="k" val="v">
...
</:foreach>
foreach($a as $k=>$v) {
...
}
<:foreach var="${a}" val="v">
...
</:foreach>
foreach($a as $v) {
...
}

tag :if

Creates an IF condition. Syntax:

<:if test="...">
    ...
</:if>

Tag must not be closed if folowed by a :else or :elseif!

Attributes:

Name Mandatory Data Type Description
test Y CONDITION Condition when body is executed.

Examples how this tag is compiled into PHP:

ViewLanguage Example PHP Translation
<:if test="${x}==2">
...
</:if>
if($x==2) {
...
}

You can also run simple IF/ELSE statements from expressions using ternary operators!

tag :elseif

Creates an ELSE IF condition. Syntax:

<:elseif test="...">
    ...
</:if>

Tag must not be closed if folowed by a :else or :elseif!

Attributes:

Name Mandatory Data Type Description
test Y CONDITION Condition when body is executed.

Examples how this tag is compiled into PHP:

ViewLanguage Example PHP Translation
<:elseif test="${x}==2">
...
</:if>
} elseif($x==2) {
...
}

tag :else

Creates an ELSE condition. Syntax:

<:else>
    ...
</:if>

Examples how this tag is compiled into PHP:

ViewLanguage Example PHP Translation
<:else>
...
</:if>
} else {
...
}

tag :set

Sets a value to a variable.Syntax:

<:set var="..." val="..."/>

Attributes:

Name Mandatory Data Type Description
var Y string Name of variable to be created/updated.
val Y string
EXPRESSION
Value of variable.

Examples how this tag is compiled into PHP:

ViewLanguage Example PHP Translation
<:set var="a" val="10" $a = 10;
<:set var="a" val="${x}" $a = $x;

tag :unset

Removes variable from memory. Syntax:

<:unset var="..."/>

Attributes:

Name Mandatory Data Type Description
var Y string Name of variable to be unset.

Examples how this tag is compiled into PHP:

ViewLanguage Example PHP Translation
<:unset var="a" unset($a);

tag :while

Creates a WHILE loop. Syntax:

<:while test="...">
    ...
</:while>

Attributes:

Name Mandatory Data Type Description
test Y CONDITION Condition when body is executed.

Examples how this tag is compiled into PHP:

ViewLanguage Example PHP Translation
<:while test="${x}!=2">
...
</:while>
while($x!=2) {
...
}

tag :break

Breaks a FOR/FOR EACH/WHILE statement loop. Syntax:

<:break/>

Examples how this tag is compiled into PHP:

ViewLanguage Example PHP Translation
<:break/:while> break;

tag :continue

Continues to next step within a FOR/FOR EACH/WHILE statement loop. Syntax:

<:continue/>

Examples how this tag is compiled into PHP:

ViewLanguage Example PHP Translation
<:break/:while> break;
You might also like...
A high-performance event loop library for PHP
A high-performance event loop library for PHP

🐇 A high-performance event loop library for PHP 🐇

High-performance, low-memory-footprint, single-file embedded database for key/value storage

LDBA - a fast, pure PHP, key-value database. Information LDBA is a high-performance, low-memory-footprint, single-file embedded database for key/value

High performance pocketmine 4.0 vote plugin

Voting38 This is a threaded high performance voting plugin for PocketMine-MP API 4.0 You can configure messages and voting rewards in the config. Ther

A cross-language remote procedure call(RPC) framework for rapid development of high performance distributed services.

Motan Overview Motan is a cross-language remote procedure call(RPC) framework for rapid development of high performance distributed services. Related

High performance mine reset plugin for PocketMine-MP 4.0.0

MineReset38 High performance mine reset plugin for PocketMine-MP 4.0.0 Unlike other plugins, this plugin is not threaded but asynchronous. When a mine

PHP_Depend is an adaptation of the established Java development tool JDepend. This tool shows you the quality of your design in terms of extensibility, reusability and maintainability.

PHP Depend Documentation PHP Depend for enterprise Available as part of the Tidelift Subscription. The maintainers of PHP Depend and thousands of othe

Port of the Java Content Repository (JCR) to PHP.

PHP Content Repository PHPCR This repository contains interfaces for the PHPCR standard. The JSR-283 specification defines an API for a Content Reposi

An open-source Minecraft: Java Edition server implementation, written in PHP.
An open-source Minecraft: Java Edition server implementation, written in PHP.

PHPCraft An open-source Minecraft: Java Edition server implementation, written in PHP. What is PHPCraft? PHPCraft is an open-source Minecraft: Java Ed

A PHP library for all things Minecraft: Java Edition

Phpcraft A PHP library for all things Minecraft: Java Edition. This project has been abandoned! The limited subset of 1.8.x - 1.15.x that has been imp

Owner
Lucian Gabriel Popescu
Lucian Gabriel Popescu
Ip2region is a offline IP location library with accuracy rate of 99.9% and 0.0x millseconds searching performance. DB file is ONLY a few megabytes with all IP address stored. binding for Java,PHP,C,Python,Nodejs,Golang,C#,lua. Binary,B-tree,Memory searching algorithm

Ip2region是什么? ip2region - 准确率99.9%的离线IP地址定位库,0.0x毫秒级查询,ip2region.db数据库只有数MB,提供了java,php,c,python,nodejs,golang,c#等查询绑定和Binary,B树,内存三种查询算法。 Ip2region特性

Lion 12.6k Dec 30, 2022
PeachPie - the PHP compiler and runtime for .NET and .NET Core

PeachPie Compiler The open-source PHP compiler to .NET If you run into any inconsistencies, bugs or incompatibilities, kindly let us know and we'll do

PeachPie Compiler Platform 2.1k Dec 22, 2022
Pug-php adds inline PHP scripting support to the Pug template compiler

Pug-php adds inline PHP scripting support to the Pug template compiler. Since version 3, it uses Phug, a very customizable Pug template engine made by the tale-pug and pug-php developers as the new PHP Pug engine reference.

Pug PHP 376 Dec 17, 2022
MassPlugCompiler - Textpattern CMS plugin compiler

mtxpc mtxpc compiles Textpattern CMS plugin sources into installer packages. Supports multi-file structure and a JSON manifest file. Install Using Com

Jukka Svahn 5 Apr 15, 2022
Tars is a high-performance RPC framework based on name service and Tars protocol, also integrated administration platform, and implemented hosting-service via flexible schedule.

TARS - A Linux Foundation Project TARS Foundation Official Website TARS Project Official Website WeChat Group: TARS01 WeChat Offical Account: TarsClou

THE TARS FOUNDATION PROJECTS 9.6k Jan 1, 2023
A high-performance license server system service for creating and managing products, major versions, and software licenses for the purpose of selling installable software products.

A high-performance license server system service for creating and managing products, major versions, and software licenses for the purpose of selling installable software products. Comes with a SDK and command-line tool. Works anywhere that PHP runs.

CubicleSoft 32 Dec 5, 2022
Enable method chaining or fluent expressions for any value and method.

PHP Pipe Operator A (hopefully) temporary solution to implement the pipe operator in PHP. Table of contents Requirements How to install How to use The

Sebastiaan Luca 268 Dec 26, 2022
PHP Regular expressions made easy

PHPVerbalExpressions ported from VerbalExpressions VerbalExpressions is a PHP library that helps to construct hard regular expressions. Installation T

null 2.4k Jan 3, 2023
Fluent regular expressions in PHP

FLUX (Fluent Regex) 0.5.2 by Selvin Ortiz Description Fluent Regular Expressions in PHP inspired by and largely based on VerbalExpressions:JS by Jesse

Selvin Ortiz 341 Nov 20, 2022
🦉 human-readable regular expressions for PHP

RegExpBuilder integrates regular expressions into the programming language, thereby making them easy to read and maintain. Regular Expressions are created by using chained methods and variables such as arrays or strings.

Max Girkens 907 Dec 30, 2022