Php – How to structure template system using plain PHP

PHPtemplates

I was reading this question over on stackoverflow:

https://stackoverflow.com/questions/104516/calling-php-functions-within-heredoc-strings

and the accepted answer says to do plain PHP templates like this:

template.php:

<html>
    <head>
        <title><?=$title?></title>
    </head>
    <body>
        <?=getContent()?>
    </body>
</html>

index.php:

<?php

$title = 'Demo Title';

function getContent() {
    return '<p>Hello World!</p>';
}

include('template.php');

?>

To me the above isn't well structured in the sense that template.php depends on variables that are defined in other scripts. And you're using an include() to execute code when you do include('template.php')(as opposed to using include() to include a class or a function which isn't immediately executed).

I feel like a better approach is to wrap your template inside a function:

template.php:

<?php

function template($title, $content) {
    ob_start(); ?>

<html>
    <head>
        <title><?=$title?></title>
    </head>
    <body>
        <?=$content?>
    </body>
</html>

    <?php return ob_get_clean();
}

?>

index.php:

<?php

require_once('template.php');

print template('Demo Title', '<p>Hello World!</p>');

?>

Is the second approach better? Is there an even better way to do it?

Best Answer

I wouldn't do the second approach as the parameters aren't named.

A well-written essay on describing how a template system should work is coming from Parr, it's often quoted by people writing template systems and/or web-mvc frameworks.

Personally, what I usually prefer is to implement an ArrayObject class with a properties array, and the template would refer to $this->propertyName, which in fact would be the template object's $this->property['name']. This could be also achieved simply by using __set and __get, so:

class Template {
  private $_scriptPath=TEMPLATE_PATH;//comes from config.php
  public $properties;
  public function setScriptPath($scriptPath){
    $this->_scriptPath=$scriptPath;
  }
  public function __construct(){
      $this->properties = array();
  }
  public function render($filename){

   ob_start();
   if(file_exists($this->_scriptPath.$filename)){
     include($this->_scriptPath.$filename);
    } else throw new TemplateNotFoundException();
    return ob_get_clean();
  }
  public function __set($k, $v){
      $this->properties[$k] = $v;
  }
  public function __get($k){
      return $this->properties[$k];
  }
}

and a template would look like:

<html>
      <head>
         <title><?=$this->title?></title>
      </head>
      <body>Hey <?=$this->name?></body>
</html>

and invoking it would look like:

$view = new Template();
$view->title="Hello World app";
$view->properties['name'] = "Jude";
echo $view->render('hello.inc');

As far as I remember, this is how the old Symfony 1.x and the Zend_View template engines look like, and for me it's fine.

Related Topic