Tạo Một Template Engine Class PHP Gọn Nhẹ Load Nhanh

ECShop Lab3 tuần trước38 lượt xem

Template engine class siêu nhẹ, linh hoạt, load nhanh và an toàn, nó biên dịch các mẫu thành mã PHP được tối ưu hóa. ECShop Lab sẽ hướng dẫn cho bạn làm thế nào để tạo ra một Class như vậy.

Template engine Class PHP

Template Engine  Là Gì ?

Hiểu một cách khá đơn giản, Template Engine giúp bạn tách biệt phần Code PHP và Html ra làm 2 phần có thể hiểu là PHP đóng vai trò là Controler (C) và Template là View (V) trong mô hình đầy đủ MVC. Một cách code khá quen thuộc trong hầu hết các mã nguồn mỡ hiện tại như Laravel. Giúp Code của bạn nhìn trong gọn và chuyên nghiệp hơn, dễ bảo trì và sửa lỗi hơn.

Nếu bạn có ý định sử dụng mô hình MVC cho một dự án PHP thì Template Engine chắc chắn bạn sẽ cần đến. Hiện nay có rất nhiều Template Engine khá nổi tiếng như Smarty, Twig với hiệu suất tốt, bảo mật cao... Nhưng trong bài viết này ECShop Lab muốn chia sẽ đến bạn một Class Template Engine kiểu nhà trồng đơn giản và hiệu quả.

Tạo một Template Engine bằng PHP

Full code Class Template như sau: cách bạn đọc comment trong code để hiểu về các hàm nhé.

class Template {

    static $blocks = array();
    static $cache_path = 'cache/';
    static $cache_enabled = FALSE;

 /* Khai báo thư mục lưu cache */

static function Init($cache_path = 'cache/', cache_enabled= false) {
       $this->cache_path = $cache_path;
       $this->cache_enabled = $cache_enabled ;
    }

    /* Render Template để hiển thị */

static function view($file, $data = array()) {
        $cached_file = self::cache($file);
        extract($data, EXTR_SKIP);
           require $cached_file;
    }

    /* Tạo Cache lưu lại kết quả  code đã biên dịch thành PHP */

static function cache($file) {
        if (!file_exists(self::$cache_path)) {
              mkdir(self::$cache_path, 0744);
        }
        $cached_file = self::$cache_path . str_replace(array('/', '.html'), array('_', ''), $file . '.php');
        if (!self::$cache_enabled || !file_exists($cached_file) || filemtime($cached_file) < filemtime($file)) {
            $code = self::includeFiles($file);
            $code = self::compileCode($code);
            file_put_contents($cached_file, '' . PHP_EOL . $code);
        }
        return $cached_file;
    }

    /* Xóa cache */

static function clearCache() {
        foreach(glob(self::$cache_path . '*') as $file) {
            unlink($file);
        }
    }

     /*  Biên dịch các thành phần trong Template thành PHP */

static function compileCode($code) {
        $code = self::compileBlock($code);
        $code = self::compileYield($code);
        $code = self::compileEscapedEchos($code);
        $code = self::compileEchos($code);
        $code = self::compilePHP($code);
        return $code;
    }

    /* biên dịch Include  Tag */

static function includeFiles($file) {
        $code = file_get_contents($file);
        preg_match_all('/{% ?(extends|include) ?\'?(.*?)\'? ?%}/i', $code, $matches, PREG_SET_ORDER);
        foreach ($matches as $value) {
            $code = str_replace($value[0], self::includeFiles($value[2]), $code);
        }
        $code = preg_replace('/{% ?(extends|include) ?\'?(.*?)\'? ?%}/i', '', $code);
        return $code;
    }

    /* biên dịch PHP Syntax */

static function compilePHP($code) {
        return preg_replace('~\{%\s*(.+?)\s*\%}~is', '', $code);
    }

      /* biên dịch PHP Echo */

static function compileEchos($code) {
        return preg_replace('~\{{\s*(.+?)\s*\}}~is', '', $code);
    }

    static function compileEscapedEchos($code) {
        return preg_replace('~\{{{\s*(.+?)\s*\}}}~is', '', $code);
    }

    static function compileBlock($code) {
        preg_match_all('/{% ?block ?(.*?) ?%}(.*?){% ?endblock ?%}/is', $code, $matches, PREG_SET_ORDER);
        foreach ($matches as $value) {
            if (!array_key_exists($value[1], self::$blocks)) self::$blocks[$value[1]] = '';
            if (strpos($value[2], '@parent') === false) {
                self::$blocks[$value[1]] = $value[2];
            } else {
                self::$blocks[$value[1]] = str_replace('@parent', self::$blocks[$value[1]], $value[2]);
            }
            $code = str_replace($value[0], '', $code);
        }
        return $code;
    }

    /* biên dịch kế thừa như trong Blade của Laravel */

 static function compileYield($code) {
        foreach(self::$blocks as $block => $value) {
            $code = preg_replace('/{% ?yield ?' . $block . ' ?%}/', $value, $code);
        }
        $code = preg_replace('/{% ?yield ?(.*?) ?%}/i', '', $code);
        return $code;
    }

}
?>

 

Cách Sử Dụng PHP Template Engine Class

Để giúp bạn hiểu hơn về Template Engine chúng ta bắt đầu với một Dự án Say Hello World huyền thoại như sau:

Bước 1:Tạo một Project với thư mục có tên template chứa các file với cấu trúc như sau

| -- cache
| -- views |
               | -- index.html 
               | -- layout.html
               | -- header.html
               | -- footer.html
|-- Template.php
| -- index.php

Bước 2:Code cho từng file đã tạo

- Bạn tạo một File index.php (được hiểu là lớp Controller - C) như cấu trúc trên với nội dung như sau:

include 'Template.php'; 

/* Bạn có thể đổi tên thư mục khác, và cấu hình có bật Cache hay không */

Template::Init('cache/', false);

/* Hiển thị nội dung ra trang chủ được lấy từ file index.html  là Template trong thư mục views */
Template::view('views/index.html', [
    'name' => 'Home Page',
    'colors' => ['red','blue','green']
]);


?>

- Tạo một file layout.html trong thư mục views (toàn bộ file trong views được xem là lớp view - V ) với nội dung như sau:

 

File này đóng vai trò làm master, kiểu là tất cả các trang khác sau này sẽ cùng chung một bố cục html của thằng layout.html này. Hay còn gọi là kế thừa Template.

 

- Tạo tiếp template cho trang chủ có tên index.html đặt vào thư mục views:

{% extends layout.html %}
{% block title %} Home Page  {% endblock %}
{% include header.html %}
{% block content %}

Hello World !

Welcome to the {% $name %} !

{% endblock %}
{% include footer.html %}

Giải thích cú pháp trong Template Engine

+ {%  code %}  : ký tự % báo hiệu cho Class Template biết chổ này cần biên dịch từ dấu ngoặc nhọn mở, đến dấu ngoặc nhọn đóng, bạn thấy quá quen khi dùng Laravel. Phần code ở giữa sẽ được biên dịch qua PHP code.

yield content --> kế thừa nội dung từ block content, của template kế thừa.

extends layout.html -> có nghĩa khi View nạp index.html ở file index.php để render kết quả ra màn hình thì index.html sẽ kế thừa bố cục html chuẩn từ layout.html.

block -> thẻ này dùng khai báo nội dung hiển thị ra template cha, được đặt tên là title trong phần khai báo {% yield title %} file layout.html

có nghĩa là nội dung giữa thẻ block này sẽ hiện thị ra thẻ meta title.

include header.html --> đơn giản như trong PHP là include (nhúng) file header.html, footer.html vào thôi.

Khai báo biến trong Template Engine

Biến trong Template này được sử dụng thông qua mảng, được truyền vào tham số thứ 2 trong hàm function view ở index.php.

Có thể sửa đoạn code ở index.php thành một cách dễ hiểu hơn :

...

$data =[

'name' => 'Home Page',
    'colors' => ['red','blue','green']
]);

Template::view('views/index.html', $data);

 

Ở template index.html ta sử dụng biến đã khai báo như sau

Welcome to the {% $name %}, list of colors:

{% foreach($colors as $color): %}
   - This is {{ $color }}
{% endforeach; %}

/* kết quả hiển thị thành html */

Welcome to the Home Page, list of colors:

- This is red
- This is blue
- This is green

 

Qua đây có thể hiểu {% và %} đại điện cho php đóng mở.

Xóa Cache Template

Cache trong trường hợp này không phải là cache html tĩnh nhưng trong các Framework chuyên nghiệp mà thực ra là Complie Code PHP, tức là phần code được biên dịch từ template thành PHP sau đó lưu thành file để không phải biên dịch nhiều lần.

Trong thực tế ta cache là kết quả hiển thị ra màn hình được lưu lại dưới dạng file tạm.

Để xóa cache ta chỉ cần gọi hàm

Template::clearCache();

Kết Luận

Template engine rất hữu ích khi bạn làm các dự án lớn lao hơn, nhiều người cùng làm với ưu điểm là làm cho dự án của bạn sạch sẽ và trông chuyên nghiệp hơn. Nếu bạn thấy các Framework phức tạp khó dùng thì với bài viết này bạn có thể phát triển cho mình một mô hình MVC của riêng mình với cách code của riêng mình, tùy biến linh hoạt tùy thích.

Nguồn tham khảo: codeshack.io