As far as I know, template inheritance appeared for the first time in the Python framework, Django. The idea is taken from the object oriented programming, where we can extend classes by adding new methods, fields and overwriting the existing ones. Here it looks similar. We have a main template where we mark some characteristic places and fill them with a default content. Another template extends the main one and is able to overwrite that places with its own content. There can go the next template, and next...

I was quite surprised that a lot of required functionality already exists in OPT 1.x - the bind and insert instructions allowed to move whole template parts somewhere else (as well as their functionality, not only the result, like in Smarty's capture), even if the destination was in the other template. Unfortunately, there was no proper detection, whether one of the files was modified, so unsually the parser decided that the main part needs to be compiled, but the template with snippet definitions - does not. As a result, the places remained empty, although they should not. The development of such system for OPT 2.0 was quite complicated. You do not have to check just the template you've called, whether it is modified. You must also look at all the templates it inherits. Yesterday, I decided to solve this problem definietly and a couple of hours ago everything seemed to work properly:

The base template (test_inherited_a.tpl):

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
	   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en_US" xml:lang="en_US">
 <head>
  <title>Instruction test: snippet and insert</title>
 </head>
 <body>
  <opt:insert snippet="header">
   <h1>I'm a standard header</h1>
   <p>Foo bar joe</p>  
  </opt:insert>
  
  <hr/>
  
  <opt:insert snippet="content">
  	<p>Well, i'm also a standard content.</p>
  
  </opt:insert>
  
  <hr/>
  
  <opt:insert snippet="footer">
  	<p>And I'm a footer.</p>  
  </opt:insert>
  
  <p>&copy; Pasteright 2008 by LMAO, It seems to work!</p>
 </body>
</html>

The extending template (test_inherit_1.tpl):

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<opt:extend file="test_inherited_a.tpl">
	<opt:snippet name="header">
		<h1>Webmaster Of Puppets</h1>
	</opt:snippet>

	<opt:snippet name="content">
		<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Phasellus ut tellus id nulla adipiscing eleifend. Sed dictum accumsan ante. Nullam at nisl vitae elit aliquet fringilla. Praesent egestas eros eget tellus. Praesent id odio a sapien rhoncus vehicula. Nunc fringilla, diam eget euismod tempor, tortor metus tincidunt sapien, eu cursus magna tellus at risus. Praesent non tellus eget magna facilisis pulvinar. Praesent libero mi, adipiscing a, pharetra eget, condimentum sodales, mi. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Donec ac elit. Duis iaculis tortor a metus. Aliquam id purus et eros faucibus fringilla. Praesent quis quam. In lectus urna, fringilla sit amet, iaculis eget, aliquet ac, quam. Donec vulputate dui sit amet lectus. Aenean tempor, orci at pretium ornare, tortor tortor venenatis ligula, eget blandit nisi risus eget dolor. Duis nunc neque, sodales porta, viverra non, tristique eu, sem. Curabitur magna neque, blandit ullamcorper, congue quis, tristique ut, felis.</p>
	</opt:snippet>

	<opt:snippet name="footer">
		<p>Bye!!!</p>
	</opt:snippet>
</opt:extend>

Some PHP code:

<?php

	define('OPT_DIR', '../lib/');
	require(OPT_DIR.'opt.class.php');

	try
	{
		$tpl = new optClass;
		$tpl -> sourceDir = './templates/';
		$tpl -> compileDir = './templates_c/';
		$tpl -> stripWhitespaces = false;
		$tpl -> printComments = false;
		$tpl -> setup();
		
		$tpl -> parse('test_inherit_1.tpl');
	}
	catch(optException $e)
	{
		optErrorHandler($e);
	}
?>

And the result can be admired below:

Template inheritance

Some facts:

  1. If we modify any of the templates in the inheritance chain, the parser will notice it and recompile proper files.
  2. If we remove any of "opt:snippet" instructions, the default content of "opt:insert" will be displayed.
  3. There is a possibility to overwrite an existing snippet by the more important template. You will be also able to call the parent content, however it is not implemented yet (some kind of "opt:parent" instruction).
  4. It can be mixed with "opt:include" and "opt:sequence" (the sequence system briefly mentioned in the introduction).

What is more, it's quite possible that the template inheritance will work faster than traditional way, although it will consume more disk space. Simply, if we have an inheritance like this: "A extends B extends C", the compiler puts all their code in the file belonging to the "A" template. It reduces the amount of disk operations (the execution of one bigger template is faster than the same content in five files). On the other hand, by default OPT still checks the file modification times, but fortunately, the number of tests is also smaller. The exact results will be available, when some benchmarks are done. Before we end, I'll say that two days ago I made a small test (a simple list with 15 items). The result:

  1. OPT 1.x: 1850 req/s
  2. OPT 2.0: 2100 req/s

Well, it looks promising, but we have to remember that the new opt.class.php file still does not have half of the options, which obviously will increase its size. Moreover, some optimizations are not implemented yet. But in fact, you can assume that the new features should not destroy the performance, and there is a chance that it will be even better.