📜 ⬆️ ⬇️

Inheritance of templates in PHP without using third-party libraries

When developing web applications, we will definitely encounter problems with rendering HTML pages. Typically, these problems are solved by the template engine - actually PHP or any template parser. If the application is large and the pages contain many blocks, then the complexity of the templates may increase dramatically, and developers will have a desire to simplify working with them. Different techniques are used, but usually this is the selection of duplicate blocks in templates and their correct decomposition - including the inheritance of templates.

I like how template inheritance is done in Django . The idea is simple - there is a basic template, it highlights the content blocks, which will vary depending on the page. When rendering a page, you can specify that the base template is taken as a basis and override only the blocks you need. If there are a lot of pages of the same type in a project, then you can create an intermediate template that inherits from the main one and then redefine its data. With skillful design, the amount of repetitive code can be nullified, as well as make life easier when you change the design.

It looks like this approach is liked not only by me and the Django developers, but also by Fabien Potencier to the author of the Symfony framework and the Twig template engine. Twig has many interesting features, including compiling templates into native PHP classes, filtering data, built-in loops, and so on. - in general, all that is supposed to have a modern template engine. But the most interesting thing is the same template inheritance I mentioned above. Here is an example from the official documentation:
')
Basic pattern:

<!DOCTYPE html> <html> <head> {% block head %} <link rel="stylesheet" href="style.css" /> <title>{% block title %}{% endblock %} - My Webpage</title> {% endblock %} </head> <body> <div id="content">{% block content %}{% endblock %}</div> <div id="footer"> {% block footer %} © Copyright 2011 by <a href="http://domain.invalid/">you</a>. {% endblock %} </div> </body> </html> 


Child template:

 {% extends "base.html" %} {% block title %}Index{% endblock %} {% block head %} {{ parent() }} <style type="text/css"> .important { color: #336699; } </style> {% endblock %} {% block content %} <h1>Index</h1> <p class="important"> Welcome on my awesome homepage. </p> {% endblock %} 


The basic blocks are defined in the basic template, and an example of their overriding is defined in the child. Digging in the source code showed that this inheritance is also implemented natively - by inheriting the “compiled” classes. Great idea! Everything is good, but I was somewhat confused by the need to study a simple, but still different from PHP syntax template. And my dislike for the non-native patterns began with Smarty (in which there is also inheritance) and to this day has not undergone significant changes.

Adam Shaw, a San Francisco developer, came very close to the idea. Obviously, he also, like me, doesn’t like experimenting with the syntax of templating and came up with a simple library called Template Inheritance. The site says in bold yellow that "There is no need to learn another template language." I agree with that. What does he offer? See an example again from the official documentation:

Basic pattern:

 <?php require_once 'ti.php' ?> <html> <body> <h1> <?php startblock('title') ?> <?php endblock() ?> </h1> <div id='article'> <?php startblock('article') ?> <?php endblock() ?> </div> </body> </html> 


Child template:

 <?php include 'base.php' ?> <?php startblock('title') ?> This is the title <?php endblock() ?> <?php startblock('article') ?> This is the article <?php endblock() ?> 


The syntax is natural, the blocks are allocated explicitly, the library is connected to the basic template and forgot. Everything. The author says that he did this with the help of buffers and a stack (nested blocks are possible). The code is really interesting, but replete with the presence of global variables. What is left to desire?

This is where we come to the main topic of our story. Could PHP itself override the blocks of the base template? I think quite! See:

Here is the basic template:

 <!DOCTYPE HTML> <html lang="ru-RU"> <head> <title><?php echo isset($title) ? $title : ''; ?></title> <meta charset="UTF-8"> </head> <body> <div class="content"> <?php if(isset($content)){echo $content}else{ ?> Default content <?php }?> </div> <div class="sidebar"> <?php if(isset($sidebar)){echo $sidebar}else{ ?> Default sidebar <?php }?> </div> </body> </html> 


Here, in 3 blocks of the template, the presence of the corresponding variable storing some content is checked and, if it is present in the scope, then it can be output to the template, and if not, the default content is output. All that remains is to override these variables in the child template. But actually he:

 <?php if(!isset($content)){ ob_start(); ?> <h1> </h1> <?php $content = ob_get_clean();} ?> <?php require 'baseTemplate.php'; ?> 


This example overrides the $ content variable if it has not been set in advance. This is done in order to be able to inherit this template and redefine the block of content. I think the idea is clear. No libraries are required - just write templates in this style and you will be happy.

Of course, this was not without flaws. First, it is not a very concise syntax for defining and redefining blocks: retribution for nativeness. Secondly, it is impossible to get the code of the parent block in the child template. Thirdly, with this method of including templates, a certain number of spaces may appear in front of the actual HTML code due to indents between the blocks. Here I would advise you to connect the template also with the help of buffers and filter the content. This is done in many frameworks:

 function render($pathToTemplate, $data) { extract($data); ob_start(); require $pathToTemplate; return trim(ob_get_clean()); } 


This function returns the template output from the $ pathToTemplate file with the substitution of variables obtained from the $ data array. extract - to an amateur - you can not do, and contact $ data directly. Before outputting, the leading and trailing spaces are removed from the content. In the template, you can do everything that allows your conscience to do, without violating the principles of separation of logic and presentation, and PHP. For example, depending on the situation, connect one or another base file.

That's all. To implement inheritance, you can use any of the methods described above, I am sure that there is something else. I would be glad if this article will help someone to make their code a little better.

Source: https://habr.com/ru/post/134421/


All Articles