
PHP template inheritance without using third-party libraries
When developing Web applications, we are sure to encounter problems with rendering HTML pages. Typically, these problems are solved by the template engine - actually PHP or some kind of template parser. If the application is large and the pages contain many blocks, then the complexity of the templates can increase dramatically, and developers have a desire to simplify the work with them. Different techniques are used, but usually this is the allocation of repeating blocks in the templates and their correct decomposition - including template inheritance.
I like how template inheritance is done in Django. The idea is simple - there is a basic template, content blocks are highlighted in it, which will vary depending on the page. When rendering a page, you can specify that the base template is taken as the basis and redefine only the necessary blocks. If the project has a lot of pages of the same type, then you can make an intermediate template that inherits from the main one, and then redefine its data. With skillful design, the amount of repeated code can be nullified, as well as make life easier when changing the design.
It seems that this approach is liked not only by me and the Django developers, but also by Fabien Potencier, the author of the Symfony framework and the Twig template engine. Twig has many interesting features, including compiling templates into native PHP classes, data filtering, built-in loops, etc. - In general, all that is supposed to have a modern template engine. But the most interesting is the very template inheritance that I mentioned above. Here is an example from the official documentation:
Basic template:
Child template:
In the basic template, the main blocks are defined, and in the child, an example of their redefinition. Digging in the source code showed that this inheritance is also implemented natively - by inheriting the “compiled” classes. Great idea! Everything is fine, but I was somewhat confused by the need to study the template syntax, albeit simple, but still different from PHP. And my dislike for non-native templates began with Smarty (which also has inheritance) and until today has not undergone significant changes. Adam Shaw, a
developer from San Francisco, came very close to the idea . Obviously, he also, like me, doesn’t like experimenting with template syntax and came up with the uncomplicatedly named Template Inheritance libraryThe site says in bold yellow that "There is no need to learn another template language", they say you do not need to learn another template language. I agree with that. What does he offer? We look at the example again from the official documentation:
Basic template:
Child template:
The syntax is natural, the blocks are highlighted explicitly, connected the library to the base template and forgot. All. The author says that he did this with buffers and a stack (nested blocks are possible). The code is really interesting, but replete with the presence of global variables. What else remains to be desired?
This is where we come to the main theme of our story. But can PHP itself override the blocks of the base template? I think quite! See:
Here is the basic template:
Here, in 3 blocks of the template, the presence of the corresponding variable that stores some content is checked and, if it is present in the scope, then it can be output to the template, and if not, then the content is displayed by default. We can only redefine these variables in the child template. And here he is:
This example overrides the $ content variable if it has not been set before. This is done in order to be able to inherit this template and redefine the content block. I think the idea is clear. No libraries are required - just write templates in this style and you will be happy.
Of course, there were some shortcomings here. Firstly, this is not a very concise syntax for defining and redefining blocks: retribution for nativeness. Secondly, the parent block code cannot be obtained in the child template. Thirdly, with this method of including templates in front of the actual HTML code, a certain number of spaces may form due to indentation between blocks. Here I would advise connecting the template also using buffers and filtering the content. This is done in many frameworks:
This function returns the template output from the $ pathToTemplate file with variable substitution obtained from the $ data array. extract - not for everybody - you don’t have to do it, but refer directly to $ data. Before output from the content, leading and trailing spaces are removed. In the template, you can do everything that your conscience allows you 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’m sure that there is something else. I would be glad if this article helps someone make their code a little better.
I like how template inheritance is done in Django. The idea is simple - there is a basic template, content blocks are highlighted in it, which will vary depending on the page. When rendering a page, you can specify that the base template is taken as the basis and redefine only the necessary blocks. If the project has a lot of pages of the same type, then you can make an intermediate template that inherits from the main one, and then redefine its data. With skillful design, the amount of repeated code can be nullified, as well as make life easier when changing the design.
It seems that this approach is liked not only by me and the Django developers, but also by Fabien Potencier, the author of the Symfony framework and the Twig template engine. Twig has many interesting features, including compiling templates into native PHP classes, data filtering, built-in loops, etc. - In general, all that is supposed to have a modern template engine. But the most interesting is the very template inheritance that I mentioned above. Here is an example from the official documentation:
Basic template:
{% block head %}
{% block title %}{% endblock %} - My Webpage
{% endblock %}
{% block content %}{% endblock %}
Child template:
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ parent() }}
{% endblock %}
{% block content %}
Index
Welcome on my awesome homepage.
{% endblock %}
In the basic template, the main blocks are defined, and in the child, an example of their redefinition. Digging in the source code showed that this inheritance is also implemented natively - by inheriting the “compiled” classes. Great idea! Everything is fine, but I was somewhat confused by the need to study the template syntax, albeit simple, but still different from PHP. And my dislike for non-native templates began with Smarty (which also has inheritance) and until today has not undergone significant changes. Adam Shaw, a
developer from San Francisco, came very close to the idea . Obviously, he also, like me, doesn’t like experimenting with template syntax and came up with the uncomplicatedly named Template Inheritance libraryThe site says in bold yellow that "There is no need to learn another template language", they say you do not need to learn another template language. I agree with that. What does he offer? We look at the example again from the official documentation:
Basic template:
Child template:
This is the title
This is the article
The syntax is natural, the blocks are highlighted explicitly, connected the library to the base template and forgot. All. The author says that he did this with buffers and a stack (nested blocks are possible). The code is really interesting, but replete with the presence of global variables. What else remains to be desired?
This is where we come to the main theme of our story. But can PHP itself override the blocks of the base template? I think quite! See:
Here is the basic template:
<?php echo isset($title) ? $title : ''; ?>
Default content
Here, in 3 blocks of the template, the presence of the corresponding variable that stores some content is checked and, if it is present in the scope, then it can be output to the template, and if not, then the content is displayed by default. We can only redefine these variables in the child template. And here he is:
Переопределенный контент
This example overrides the $ content variable if it has not been set before. This is done in order to be able to inherit this template and redefine the content block. I think the idea is clear. No libraries are required - just write templates in this style and you will be happy.
Of course, there were some shortcomings here. Firstly, this is not a very concise syntax for defining and redefining blocks: retribution for nativeness. Secondly, the parent block code cannot be obtained in the child template. Thirdly, with this method of including templates in front of the actual HTML code, a certain number of spaces may form due to indentation between blocks. Here I would advise connecting the template also using buffers and filtering 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 variable substitution obtained from the $ data array. extract - not for everybody - you don’t have to do it, but refer directly to $ data. Before output from the content, leading and trailing spaces are removed. In the template, you can do everything that your conscience allows you 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’m sure that there is something else. I would be glad if this article helps someone make their code a little better.