public
Last active

PHP Templating Engine with bindable $this support in 53 LOC

  • Download Gist
.gitignore
1 2
vendor/
composer.lock
README.md
Markdown

Template Class, with support for binding object contexts to the template.

Requires PHP 5.4 for Closure $this support

Install:

Put this in a file named composer.json:

{
    "repositories": [
        {"type": "vcs", "url": "git://gist.github.com/1121233.git"}
    ],
    "require": {
        "chh/simple-template": "*"
    }
}

Get Composer and install:

wget http://getcomposer.org/composer.phar
php composer.phar install

Usage:

hello_world.phtml

Hello World <?= $name ?>
Here is the output of some method: <?= $this->foo() ?>
Here is the value of a context property: <?= $this->bar ?>

Template Controller

<?php

require "Template.php";

use CHH\Template;

class Context
{
  var $bar = "Some Context Property";

  function foo()
  {
    return "Context::foo()";
  }
}

$template = new Template('/hello_world.phtml');
echo $template->render(new Context, ['name' => 'Jim']);

This yields:

Hello World Jim
Here is the output of some method: Context::foo()
Here is the value of a context property: Some Context Property
SimpleTemplate.php
PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
<?php
 
namespace CHH;
 
class SimpleTemplate
{
protected $file;
 
function __construct($file)
{
if (!is_file($file) or !is_readable($file)) {
throw new \InvalidArgumentException(
"File $file does not exist or is not readable"
);
}
 
$this->file = $file;
}
 
# Renders the template
#
# context - Is the object to which $this inside
# the template refers to.
# vars - Is an array of key-value pairs, which are
# available within the template as local variables.
#
# Returns the rendered template as String.
function render($context = null, array $locals = [])
{
if (null === $context) {
$context = new \StdClass;
} else if (is_object($context)) {
$context = clone $context;
} else {
throw new \InvalidArgumentException("Invalid Context given. Context must be an object");
}
 
# Create the sandbox for the template to execute in
$template = function($__file__, array $__locals__ = array()) {
// Set all keys in $__locals__ as local variables
foreach ((array) $__locals__ as $var => $value) {
$$var = $value;
}
unset($var, $value, $__locals__);
 
ob_start();
include $__file__;
return ob_get_clean();
};
 
# Bind $this inside the closure to the context object
$template = $template->bindTo($context);
return $template($this->file, $locals);
}
}
composer.json
JSON
1 2 3 4 5 6 7 8 9 10 11 12 13
{
"name": "chh/simple-template",
"description": "Minimal template class, using Closure Object Binding.",
"authors": [
{ "name": "Christoph Hochstrasser", "email": "christoph.hochstrasser@gmail.com" }
],
"require": {
"php": ">=5.4.0"
},
"autoload": {
"classmap": ["."]
}
}

Any thoughts on how you prevent views from accessing the private vars and methods?

Which private vars?

Specifically the protected $bar of the context. I love the idea of transferring the entire context object instead of manually assigning context vars, but re-assigning $this and giving access to protected vars and methods seems like it should be avoided.

You're right, that should be avoided.

Actually it doesn't have any access to the $bar property. I've messed up with the example code.

I've checked it with 5.4.0beta2 and they now changed the default scoping behaviour of bindTo, so closures don't have access to protected and private class members by default. Actually if you try the example it throws an fatal error, with the reason that the protected property $bar could not be accessed.

So I've fixed the example code by making the bar property public.

Thanks for pointing this out!

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.