Until now, when using the framework, I really like the inheritance of view engine. It helps me a lot in the layout division, the code feels coherent and convenient. Curious and explored, today I would like to share with everyone how to create a super “smart” template engine that is simple for you guys. Let’s go
Initialization
First, let’s create a project with the following structure:
Well, the project is simply a template.php
file.
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">class</span> <span class="token class-name">Template</span> <span class="token punctuation">{</span> <span class="token comment">/** * * Thư mục views. * */</span> <span class="token keyword">private</span> <span class="token variable">$__directory</span> <span class="token punctuation">;</span> <span class="token comment">/** * * Layout của views. * Thuộc tính này sẽ có giá trị là view parent - view cần kế thừa của view đang được gọi. * @default null * */</span> <span class="token keyword">private</span> <span class="token variable">$__layout</span> <span class="token punctuation">;</span> <span class="token comment">/** * * Các section của layout. VD: content, sidebar, header, footer,... Nói chung ai dùng fw rồi đều sẽ biết * */</span> <span class="token keyword">private</span> <span class="token variable">$__sections</span> <span class="token punctuation">;</span> <span class="token comment">/** * * section hiện tại đang xét. * @default null * */</span> <span class="token keyword">private</span> <span class="token variable">$__current_section</span> <span class="token punctuation">;</span> <span class="token comment">/** * * Ham khoi tao * */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">__construct</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// code</span> <span class="token punctuation">}</span> <span class="token comment">/** * * Hàm load view * * @param string $view_name Tên view cần load * @param array $args Các tham số cần truyền qua view * * @return string * */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">render</span> <span class="token punctuation">(</span> string <span class="token variable">$view_name</span> <span class="token punctuation">,</span> <span class="token keyword">array</span> <span class="token variable">$args</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// code</span> <span class="token punctuation">}</span> <span class="token comment">/** * * Include một view trong một view * * @param string $view_name Tên view cần include (giống include file php trong php đó) * * */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token keyword">include</span> <span class="token punctuation">(</span> string <span class="token variable">$view_name</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// code</span> <span class="token punctuation">}</span> <span class="token comment">/** * * Hàm bắt đầu một section * * @param string $name Tên của section. * */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">section</span> <span class="token punctuation">(</span> string <span class="token variable">$name</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// code</span> <span class="token punctuation">}</span> <span class="token comment">/** * * Hàm kết thúc một section * */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">end</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// code</span> <span class="token punctuation">}</span> <span class="token comment">/** * * Hàm kế thùa layout trong views * * @param string $layout Layout cần kế thừa * */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">layout</span> <span class="token punctuation">(</span> string <span class="token variable">$layout</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// code</span> <span class="token punctuation">}</span> <span class="token comment">/** * * Hàm xác định vị trí section sẽ được render trong file layout view * * @param string $name Tên section cần render * * */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">renderSection</span> <span class="token punctuation">(</span> string <span class="token variable">$name</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// code</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </span> |
Well the above code is just initializing the template.php
class file, I have carefully explained the variables and methods in the Template class. The following will code in detail each function.
Code
The constructor
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 | <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">__construct</span> <span class="token punctuation">(</span> <span class="token variable">$directory</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">__setDirectory</span> <span class="token punctuation">(</span> <span class="token variable">$directory</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">__sections</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">__layout</span> <span class="token operator">=</span> <span class="token constant">null</span> <span class="token punctuation">;</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">__current_section</span> <span class="token operator">=</span> <span class="token constant">null</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/** * * Hàm set thư mục views * * @param string $directory Thư mục views * */</span> <span class="token keyword">private</span> <span class="token keyword">function</span> <span class="token function">__setDirectory</span> <span class="token punctuation">(</span> string <span class="token variable">$directory</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span> <span class="token function">is_dir</span> <span class="token punctuation">(</span> <span class="token variable">$directory</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Exception</span> <span class="token punctuation">(</span> <span class="token double-quoted-string string">" <span class="token interpolation"><span class="token variable">$directory</span></span> is not exist"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">__directory</span> <span class="token operator">=</span> <span class="token variable">$directory</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/** * * Hàm kiểm tra đường dẫn của file view * * @param string $path Đường dẫn của file. Đuôi file sẽ là .php * * @return string * */</span> <span class="token keyword">private</span> <span class="token keyword">function</span> <span class="token function">__resolvePath</span> <span class="token punctuation">(</span> string <span class="token variable">$path</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$file</span> <span class="token operator">=</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">__directory</span> <span class="token punctuation">.</span> <span class="token single-quoted-string string">'/'</span> <span class="token punctuation">.</span> <span class="token variable">$path</span> <span class="token punctuation">.</span> <span class="token single-quoted-string string">'.php'</span> <span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span> <span class="token function">file_exists</span> <span class="token punctuation">(</span> <span class="token variable">$file</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Exception</span> <span class="token punctuation">(</span> <span class="token double-quoted-string string">" <span class="token interpolation"><span class="token variable">$file</span></span> is not exist"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token variable">$file</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Next comes the include function . This function is also quite simple, but you need to have a good understanding of output buffering
. You can consult about buffers at php.net
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <span class="token comment">/** * * Include một view trong một view * * @param string $view_name Tên view cần include (giống include file php trong php đó) * * */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token keyword">include</span> <span class="token punctuation">(</span> string <span class="token variable">$view_name</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">ob_start</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">include_once</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">__resolvePath</span> <span class="token punctuation">(</span> <span class="token variable">$view_name</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$content</span> <span class="token operator">=</span> <span class="token function">ob_get_contents</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">ob_end_clean</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">echo</span> <span class="token variable">$content</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Section and end functions. We will get the HTML (XML) code between the section and the end function
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 | <span class="token comment">/** * * Hàm bắt đầu một section * * @param string $name Tên của section. * */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">section</span> <span class="token punctuation">(</span> string <span class="token variable">$name</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">__current_section</span> <span class="token operator">=</span> <span class="token variable">$name</span> <span class="token punctuation">;</span> <span class="token function">ob_start</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/** * * Hàm kết thúc một section * */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">end</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token keyword">empty</span> <span class="token punctuation">(</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">__current_section</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Exception</span> <span class="token punctuation">(</span> <span class="token double-quoted-string string">"There is not a section start"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token variable">$content</span> <span class="token operator">=</span> <span class="token function">ob_get_contents</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">ob_end_clean</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">__sections</span> <span class="token punctuation">[</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">__current_section</span> <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token variable">$content</span> <span class="token punctuation">;</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">__current_section</span> <span class="token operator">=</span> <span class="token constant">null</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
The layout and renderSection functions are even simpler.
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 | <span class="token comment">/** * * Hàm kế thùa layout trong views * * @param string $layout Layout cần kế thừa * */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">layout</span> <span class="token punctuation">(</span> string <span class="token variable">$layout</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">__layout</span> <span class="token operator">=</span> <span class="token variable">$layout</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/** * * Hàm xác định vị trí section sẽ được render trong file layout view * * @param string $name Tên section cần render * * */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">renderSection</span> <span class="token punctuation">(</span> string <span class="token variable">$name</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">echo</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">__sections</span> <span class="token punctuation">[</span> <span class="token variable">$name</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Finally the function renders a view. This function will call the section and end functions first, then inherit the layout, and finally return a string of complete HTML (XML) file.
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 | <span class="token comment">/** * * Hàm load view * * @param string $view_name Tên view cần load * @param array $args Các tham số cần truyền qua view * * @return string * */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">render</span> <span class="token punctuation">(</span> string <span class="token variable">$view_name</span> <span class="token punctuation">,</span> <span class="token keyword">array</span> <span class="token variable">$args</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token function">is_array</span> <span class="token punctuation">(</span> <span class="token variable">$args</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">extract</span> <span class="token punctuation">(</span> <span class="token variable">$args</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">ob_start</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">include_once</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">__resolvePath</span> <span class="token punctuation">(</span> <span class="token variable">$view_name</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$content</span> <span class="token operator">=</span> <span class="token function">ob_get_clean</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token keyword">empty</span> <span class="token punctuation">(</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">__layout</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token variable">$content</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">ob_clean</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">include_once</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">__resolvePath</span> <span class="token punctuation">(</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">__layout</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$output</span> <span class="token operator">=</span> <span class="token function">ob_get_contents</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">ob_end_clean</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token variable">$output</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
ok. that’s done with the code template engine. Now try it out.
Call Template Engine
First we create a file ~/views/layout.php
.
Note: in this template engine, I use the view file extension .php . You can customize this file extension in the __resolvePath function
template.php
file above
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> html</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> head</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> title</span> <span class="token punctuation">></span></span> Demo <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> title</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> head</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> body</span> <span class="token punctuation">></span></span> <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">renderSection</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'sidebar'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token delimiter important">?></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> br</span> <span class="token punctuation">/></span></span> <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">renderSection</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'content'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token delimiter important">?></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> body</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> html</span> <span class="token punctuation">></span></span> |
Next we create a file view ~/views/profile.php
1 2 3 4 5 6 7 8 9 10 11 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">layout</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'template'</span> <span class="token punctuation">)</span> <span class="token delimiter important">?></span></span> <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">section</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'content'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token delimiter important">?></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> h1</span> <span class="token punctuation">></span></span> User Profile <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> h1</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> p</span> <span class="token punctuation">></span></span> Hello, <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">echo</span> <span class="token variable">$name</span> <span class="token punctuation">;</span> <span class="token delimiter important">?></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> p</span> <span class="token punctuation">></span></span> <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">end</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token delimiter important">?></span></span> <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">section</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'sidebar'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token delimiter important">?></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> h2</span> <span class="token punctuation">></span></span> Sidebar <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> h2</span> <span class="token punctuation">></span></span> <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">end</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token delimiter important">?></span></span> |
In the file index.php
(the file you need to call that view).
1 2 3 4 5 6 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token comment">// set thư mục views là ~/views</span> <span class="token variable">$template</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Template</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'/views'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">echo</span> <span class="token variable">$template</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">render</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'profile'</span> <span class="token punctuation">,</span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'name'</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token single-quoted-string string">'Jocelyn'</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> </span> |
Ok. You try the ~/index.php
file and enjoy the results.
summary
Creating a template engine by yourself is not difficult, it is important that you understand the output buffering
in PHP. https://www.php.net/manual/en/ref.outcontrol.php