DEV Community

Cover image for Controllable Templating in PHP
neoan
neoan

Posted on

Controllable Templating in PHP

It's been a while

I have been using my own templating engine for a while now as I wasn't happy with how blade & co handle certain things. Additionally, any kind of logic within the view is a violation of separation of concerns in my view.

However, after living with a few issues for a while, I finally decided to rewrite it completely:

GitHub logo sroehrl / neoan3-template

neoan3 minimal template engine

neoan3-apps/template

PHP template engine

Test Coverage Maintainability Build Status

As of version 2, we dropped PHP 7.4 support You require at least PHP 8.0 or use v1.2.0 of this package.

Installation / Quick start

composer require neoan3-apps/template

use Neoan3\Apps\Template\Constants;
use Neoan3\Apps\Template\Template;

require_once 'vendor/autoload.php';

// optional, if set, path defines the relative starting point to templates
Constants::setPath(__DIR__ . '/templates');

echo Template::embrace('<h1>{{test}}</h1>',['test'=>'Hello World']);
Enter fullscreen mode Exit fullscreen mode

Contents

Templating

neoan3-template is not a full blown template engine, but rather what a template engine should be: With modern JavaScript solutions creating a dynamic approach, neoan3-template focuses on the necessities of static rendering.

profile.html

<h1>{{user}}</h1>
<p>{{profile.name}}</p>
<p n-for="items as
Enter fullscreen mode Exit fullscreen mode

Happy accidents

I have had a way of adding project-specific functionality to the engine and wanted to keep it open for special needs.
By simplifying the hook-process, I noticed something interesting:

In theory, one can now use this package to create specific behavior utilizing two capabilities:

customFunctions

We are usually under full control of the data we are sending to the rendering mechanism when dealing with SSR sites, but sometimes we want to leave e.g. a model as is. So let's build a salutation template function:

Template\Constants::addCustomFunction('salutation', function($gender)
{
   $salutation = 'Mrs.';
   if($gender === 1){
      $salutation = 'Mr.';
   } elseif($gender > 1){
      $salutation = 'Mx.';
   }
});

Enter fullscreen mode Exit fullscreen mode

In our template, we can now use this function:

<!-- profile.html -->
<div>
   <p>Hello, {{salutation(user.gender)}} {{user.lastName}}</p>
</div>
Enter fullscreen mode Exit fullscreen mode
$user = [
   'gender' => 0,
   'firstName' => 'Samantha',
   'lastName' => 'Smith',
   'birthDay' => '1990-06-06',
   'website' => 'https://sam-smith-1990.com'
];
echo Template\Template::embraceFromFile('/profile.html', ['user' =>$user]);
Enter fullscreen mode Exit fullscreen mode

output

<div>
   <p>Hello, Mrs. Smith</p>
</div>
Enter fullscreen mode Exit fullscreen mode

This is, of course, a very constructed example, but you get the idea. Another way of implementing additional functionality is via the method

customAttributes

With this we can hook into attribute-interpretation of the template engine. Let's have a look:

Template\Constants::addCustomAttribute('secure-link', function($attr)
{
   $href = $attr->parentNode->getAttribute('href')->nodeValue;
   // external?
   if(
      !str_contains($href, $_SERVER['HTTP_HOST'] && 
      str_starts_with($href, 'http'))
     )
   {
      $attr->parentNode->setAttribute('target', '_blank');
      $attr->parentNode->setAttribute('rel', 'noopener noreferrer');
   }
});
Enter fullscreen mode Exit fullscreen mode
<!-- profile.html -->
<div>
   <p>Hello, {{salutation(user.gender)}} {{user.lastName}}</p>
   <a href="{{user.website}}" secure-link>{{user.website}}</a>
</div>
Enter fullscreen mode Exit fullscreen mode

output

<div>
   <p>Hello, Mrs. Smith</p>
   <a href="https://sam-smith-1990.com" target="_blank" rel="noopener noreferrer">https://sam-smith-1990.com</a>
</div>
Enter fullscreen mode Exit fullscreen mode

I know these examples are rather strange, but due to the reusability one could easily build together a templating mechanism suited to personal needs.
As usual, leaving a star when playing around with the repo is appreciated.

Discussion

Now to the fun part: What has always bothered you when having to deal with passing data to your templating mechanism? What do you wish was less of a hassle?

Top comments (0)