PHP Simple Template Engine

โดย SONGCHAI SAETERN
สำหรับคลาส Template Engine จะประกอบด้วย 3 ส่วนด้วยกัน คอ

1) ส่วนของ PHP ( Controller & Model )
2) ส่วนของ HTML ( Views )
3) คลาสจัดการ Template









HTML Views

<html>
<head>
<title>{page_title}</title>
</head>
<body>
<h3>{table_heading}</h3>

<table border="1">
<tr>
<th>รายการ</th>
<th>จำนวน</th>
<th>ราคาต่อหน่วย</th>
<th>ราคารวม</th>
</tr>
<tr parser-repeat="[data_lists]">
<td>{title}</td>
<td>{qty}</td>
<td>{unit_price}</td>
<td>{subtotal}</td>
</tr>
</table>
</body>
</html>


PHP Controller & Model  

<?php
include('libraries/PHP_Parser.php');
$template = new PHP_Parser();
// Query from Database
$data_lists = array(
array('title' => 'กระดาษ A4', 'qty' => 2, 'unit_price'=>100, 'subtotal'=>200),
array('title' => 'ดินสอ', 'qty' => 15, 'unit_price'=>2, 'subtotal'=>30),
array('title' => 'ยางลบ', 'qty' => 8, 'unit_price'=>5, 'subtotal'=>40),
array('title' => 'ปากกา', 'qty' => 10, 'unit_price'=>12, 'subtotal'=>120),
array('title' => 'ไม้บรรทัด', 'qty' => 3, 'unit_price'=>20, 'subtotal'=>60)
);
$data = array(
'page_title'   => 'ใบส่งของ',
'table_heading' => 'ใบส่งของ เลขที่ 64/00001',
'data_lists' => $data_lists
);

$template->parse('views/home.html', $data);
?>




Class Template engine PHP_Parser.php

<?php
/**
 * PHP HTML Template Engine
 * http://www.phpcodemania.com
 * @2021 for PHP Beginner
 */
class PHP_Parser{
/**
* Left delimiter character for pseudo vars
*
* @var string
*/
public $left_delimiter = '{';

/**
* Right delimiter character for pseudo vars
*
* @var string
*/
public $right_delimiter = '}';
public function parse($file_path, $data, $return = FALSE)
{
$html = $this->_load_view($file_path);
$template = $this->_parse_attribute($html, $data);
if($return == TRUE){
return $template;
}else{
echo $template;
}
}
private function _load_view($file_path)
{
$base_path = getcwd();
$file_path = $base_path.'/'. $file_path;
if(file_exists($file_path)){
ob_start();
include($file_path);
$template = ob_get_clean();
}else{
$template = 'View not found : '. $file_path;
}
return $template;
}
/**
* Parse array data list with attribute
*
* @param string
* @param array
* @param boolean
* @return array
*/  
protected function _parse_attribute($template, $data)
{
if ($template === '')
{
return FALSE;
}

$replace = array();
foreach ($data as $key => $val)
{
$replace = array_merge(
$replace,
is_array($val)
? $this->_parse_attribute_pair($key, $val, $template)
: $this->_parse_variable($key, (string) $val, $template)
);
}

unset($data);
$template = strtr($template, $replace);
return $template;
}
private function _parse_variable($key, $val, $string)
{
return array($this->left_delimiter.$key.$this->right_delimiter => (string) $val);
}
/**
* Parse data to pseudo-variables between attribute parser-repeat="[*]"
*
* @param string
* @param array
* @param string
* @return array
*/
protected function _parse_attribute_pair( $attr_value, $data, $string)
{
$replace = array();
$matches = $this->_get_html_between('parser-repeat', $attr_value, $string);

if(!empty($matches)){
foreach ($matches as $match)
{
$str = '';
foreach ($data as $row)
{
$temp = array();
foreach ($row as $key => $val)
{
if (is_array($val))
{
$pair = $this->_parse_attribute_pair($key, $val, $match[0]);
if ( ! empty($pair))
{
$temp = array_merge($temp, $pair);
}
continue;
}
$temp[$this->left_delimiter.$key.$this->right_delimiter] = $val;
}
$temp[' parser-repeat="['.$attr_value.']"'] = '';//Clear repeat attribute
$str .= strtr($match[0], $temp);
}
$replace[$match[0]] = $str;
}
}
return $replace;
}
/**
* Match html element between attribute parser-repeat="[*]"
* @param string
* @param string
* @param string
* @param string
* @return array
*/
private function _get_html_between( $attr, $value, $xml, $tag=null ) 
{
if( is_null($tag) ){
$tag = '\w+';
}else{
$tag = preg_quote($tag);
}
$attr = preg_quote($attr);
$value = preg_quote($value);

//SEARCH TAG TARGET (IT WILL BREAK WITH FIRST CLOSE TAG)
$tag_regex = "/<(".$tag.")[^>]*".$attr."\s*=\s*(['\"])\[$value\]\\2[^>]*>(.*?)(<\/\\1>)/si";
preg_match_all($tag_regex,
$xml,
$results,
PREG_SET_ORDER);
 
if(isset($results[0][1])){
$check = count($results); // IF HAS MORE THAN ONE TAG MUST USE \+w
if($check == 1){
$tag = $results[0][1]; // SET IF ONE TAG NAME
}
//GET CONTENT AGAIN WITH MULTI CLOSE TAG
$multi_regex = '{<'.$tag.'[^>]*'.$attr.'="\['.$value.'\]"[^>]*>((?:(?:(?!<'.$tag.'[^>]*>|<\/'.$tag.'>).)++|<'.$tag.'[^>]*>(?1)<\/'.$tag.'>)*)<\/'.$tag.'>}si';
preg_match_all($multi_regex,
$xml,
$matches_result,
PREG_SET_ORDER);
$results = $matches_result;
}

return $results;
}
}

/* End of file PHP_Parser.php */







PHP CI MANIA - PHP Code Generator 

โปรแกรมช่วยสร้างโค้ด "ลดเวลาการเขียนโปรแกรม"
ราคาสุดคุ้ม  
http://fastcoding.phpcodemania.com



[PHP] มาปรับแต่ง Template Parser Class ใน CodeIgniter Framework กันเถอะ

โดย SONGCHAI SAETERN

PHP : CodeIgniter Framework - Template Parser Class



การทำงานปกติของ Template Parser Class ใน CodeIgniter Framework มีดังนี้


สำหรับผลลัพธ์เมื่อสั่ง
$this->parser->parse('test_parser_view', $data);

ก็จะเกิดการแทรกข้อมูลในอาร์เรย์ $data ลงไปยังไฟล์ View ที่ระบุ และแสดงผลแบบ Loop แถวของตารางตามจำนวนอาร์เรย์ blog_entries และ data_list โดยอัตโนมัติ


จะเห็นว่าการใช้ Template Engine ก็ช่วยให้การเขียนโค้ดง่ายขึ้นได้เช่นกัน (แต่ก็จะต้องจัดรูปแบบข้อมูลให้เรียบร้อยใน Controller ก่อนจะนำมาแทรกลงใน Template ซึ่งใน PHP CI MANIA จะมีการสร้างฟังก์ชั่นเหล่านี้โดยอัตโนมัติ เช่น จัดรูปแบบวันที่ภาษาไทย จัดรูปแบบตัวเลข เป็นต้น)


ทุกอย่างก็ดูสะดวกสบายใช้ง่ายดีนี่นา แล้วทำไมถึงอยากจะปรับแต่งฟังก์ชั่นให้กับ Template Parser Class ด้วยล่ะ??

ซึ่งถ้าได้ติดตามบล็อกของผมมาโดยตลอด จะเห็นว่าก่อนหน้านี้ก็มีการปรับแต่งไปแล้วครั้งหนึ่งในบทความ PHP Codeigniter กับการใช้งาน Template Parser Class แล้วไม่สามารถเรียกใช้ตัวแปรด้านนอก Variable Pairs ที่กำหนดไว้ได้  ซึ่งมีปัญหาว่าในลูปไม่สามารถดึงค่าตัวแปร url ของหน้าเว็บจากอารเรย์ชั้นข้างนอกได้



หลังจากปรับแต่งก็ใช้วิธีใส่วงเล็บปีกกาซ้อนกัน 2 ชั้นเพื่อเรียกใช้ตัวแปรด้านนอกแทน

มาที่เรื่องของบทความนี้กันต่อ

เนื่องจากในโปรแกรม PHP CI MANIA ที่ผมกำลังพัฒนาอยู่นั้น จะมีส่วนของการแสดงตัวอย่างของ VIEW แต่ละตัวในขั้นตอนสร้างโค้ดได้ด้วย จึงทำให้เกิดบทความนี้ขึ้นมา


จะเห็นว่าเวลาในโหมด Preview เพื่อดูไฟล์ View ในตัวอย่างโค้ดด้านบนนั้น จะมีข้อความส่วนที่ผมไม่อยากให้แสดงออกมาโผล่ออกมาด้วย


ซึ่งในส่วนของการรับค่าอาร์เรย์ data_list ยังพอทำใจได้เพราะมันครอบ <li> เอาไว้
แต่ในส่วนของตาราง <table> จะมีวงเล็บ {blog_entries} ครอบแถวเอาไว้ พอมันครอบ <tr> แต่มันไม่ใช่แท็ก HTML ของ <table> มันก็เลยถูกเขี่ยกระเด็นออกไปด้านบนซะงั้น

นอกจากจะรกเกะกะสายตาแล้ว ยังทำให้ตำแหน่งตารางผิดไปจากตำแหน่งจริงอีกด้วย



และในภาพด้านบนนี้ ก็คือตัวอย่างการแสดงผลในโหมด Preview ที่กระผมต้องการนั่นเอง จะเห็นว่าส่วนของบล็อกที่ใช้รับค่า {data_list} และ {blog_entries} ได้หายไปแล้ว เพราะผมได้เอาไปซ่อนไว้ใน Attribute ของอีเลเมนต์ที่ต้องการวนลูปไปแล้วนั่นเอง


จากภาพด้านบนนี้ ผมได้สร้างแอตทริบิวต์สำหรับรับค่าอาร์เรย์จาก Controller ไว้ชื่อว่า parser-repeat ซึ่งจะมีด้วยกัน 2 จุด คือ
1) <li parser-repeat="data_list">{title}</li>
    จะสร้างแท็ก <li>  ที่นำข้อมูลจากอาร์เรย์ title ใน data_list มาแสดง จนครบทุกตัว
2) <tr parser-repeat="blog_entries">
    จะสร้างแท็ก <tr> ที่นำข้อมูลจากอาร์เรย์ title และ body ใน blog_entries มาแสดง จนครบทุกตัว



และเมื่อใช้คำสั่ง
$this->parser->parse_repeat('test_parser_repeat_view', $data);

ก็จะเรียกฟังก์ชั่นใหม่ที่ผมได้เพิ่มเข้าไป นั่นคือ parse_repeat() ซึ่งจะทำหน้าที่ค้นหา HTML ทุกตัวที่มี Attribute กำหนดไว้ว่า parser-repeat="{ชื่อ Key ของอาร์เรย์}" มาแทนที่ด้วยข้อมูลอาร์เรย์ในคีย์ชุดนั้นๆ




สำหรับซอร์สโค้ดหลังปรับแต่งเรียบร้อยมีดังนี้

application/libraries/MY_parser.php
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * Overrides the CI Template Parser to allow for multiple occurrences of the
 * same variable pair
 *
 */
class MY_Parser extends CI_Parser {
/**
* Parse a template
*
* Parses pseudo-variables contained in the specified template view,
* replacing them with the data in the second param
*
* @param string
* @param array
* @param bool
* @return string
*/
public function parse($template, $data, $return = FALSE)
{
$template = $this->CI->load->view($template, $data, TRUE);
$template = $this->_parse_double($template, $data); return $this->_parse($template, $data, $return);
}
public function parse_repeat($template, $data, $return = FALSE)
{
$template = $this->CI->load->view($template, $data, TRUE);
$template = $this->_parse_double($template, $data);
$template = $this->_parse_attribute($template, $data, $return);
return $this->_parse($template, $data, $return);
}
/**
* Parse a double key/value
*
* @param string
* @param array
* @return string
*/
protected function _parse_double($template, $data)
{
$replace = array();
preg_match_all("/\{\{(.*?)\}\}/si", $template, $matches);
foreach ($matches[1] as $match)
{
$key = '{{'.$match.'}}';
$replace[$key] = isset($data[$match]) ? $data[$match] : $key;
}
unset($data);
return strtr($template, $replace);
}
/**
* Parse list with attribute
*
* @param string
* @param array
* @return string
*/

protected function _parse_attribute($template, $data, $return = FALSE)
{
if ($template === '')
{
return FALSE;
}
$replace = array();
foreach ($data as $key => $val)
{
$replace = array_merge(
$replace,
is_array($val)
? $this->_parse_attribute_pair($key, $val, $template)
: $this->_parse_single($key, (string) $val, $template)
);
}
unset($data);
$template = strtr($template, $replace);
/* return only
if ($return === FALSE)
{
$this->CI->output->append_output($template);
}
*/
return $template;
}
protected function _parse_attribute_pair( $attr_value, $data, $string)
{
$replace = array();
$matches = $this->get_tag('parser-repeat', $attr_value, $string);
foreach ($matches as $match)
//if(!empty($matches))
{
$str = '';
foreach ($data as $row)
{
$temp = array();
foreach ($row as $key => $val)
{
if (is_array($val))
{
$pair = $this->_parse_attribute_pair($key, $val, $match[0]);
if ( ! empty($pair))
{
$temp = array_merge($temp, $pair);
}
continue;
}
$temp[$this->l_delim.$key.$this->r_delim] = $val;
}
$temp[' parser-repeat="'.$attr_value.'"'] = '';//Clear repeat attribute
$str .= strtr($match[0], $temp);
}
$replace[$match[0]] = $str;
}
return $replace;
}
private function get_tag( $attr, $value, $xml, $tag=null ) {
if( is_null($tag) ){
$tag = '\w+';
}else{
$tag = preg_quote($tag);
}
$attr = preg_quote($attr);
$value = preg_quote($value);
$tag_regex = "/<(".$tag.")[^>]*$attr\s*=\s*".
"(['\"])$value\\2[^>]*>(.*?)<\/\\1>/s";
preg_match_all($tag_regex,
$xml,
$matches,
PREG_PATTERN_ORDER);

$target = array();
if(!empty($matches)){
$match1 = isset($matches[0][0]) ? $matches[0][0] : '';
$match2 = isset($matches[3][0]) ? $matches[3][0] : '';
if($match1 != ''){
$target = array(array($match1, $match2));
}
}
return $target;
}
}
// END Parser Class
/* End of file MY_Parser.php */
/* Location: ./application/libraries/MY_Parser.php */


ไฟล์ Controller สำหรับทดสอบ
application/controllers/Template_engine.php
<?php
if (!defined('BASEPATH'))
    exit('No direct script access allowed');
class Template_engine extends CI_Controller
{
    public function __construct()
    {
        parent::__construct();
}
    public function index()
    {
    $this->parser->parse('labs/test_parser_view', array());
}
    public function parser_view()
    {
    $this->parser->parse('labs/test_parser_repeat_view', array());
}
public function test_repeat()
{
$data = array(
'blog_title'   => 'My Blog Title',
'blog_heading' => 'My Blog Heading',
'blog_entries' => array(
array('title' => 'Title 1', 'body' => 'Body 1'),
array('title' => 'Title 2', 'body' => 'Body 2'),
array('title' => 'Title 3', 'body' => 'Body 3'),
array('title' => 'Title 4', 'body' => 'Body 4'),
array('title' => 'Title 5', 'body' => 'Body 5')
),'data_list' => array(
array('title' => 'Title 1', 'body' => 'Body 1'),
array('title' => 'Title 2', 'body' => 'Body 2'),
array('title' => 'Title 3', 'body' => 'Body 3'),
),
);
$this->parser->parse_repeat('labs/test_parser_repeat_view', $data);
}
}
// ---------------------------- END Controller Class --------------------------------


ไฟล์ View สำหรับทดสอบ
1. สำหรับแสดงตัวอย่างแบบเดิม
application/views/test_parser_view.php
<html>
<head>
<title>การแสดงผลแบบ Parser ปกติ</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
    <body>
        <h3>{blog_heading}</h3> <ul>
{data_list}
<li>{title}</li>
{/data_list}
</ul> <table class="table table-bordered" border="1" style="width:500px">
<thead>
<tr bgcolor="#aaaaaa">
<td>Title</td>
<td>body</td>
</tr>
</thead>
<tbody>
{blog_entries}
<tr parser-repeat="blog_entries">
<td>{title}</td>
<td>{body}</td>
</tr>
{/blog_entries}
</tbody>
</table>
    </body>
</html>

2. สำหรับแสดงตัวอย่างแบบใหม่ (แทรกในแท็กของอีเลเมนต์)
application/views/test_parser_repeat_view.php
<html>
<head>
<title>การแสดงผลแบบใช้ Parser Repeat</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
    <body>
        <h3>{blog_heading}</h3>
<ul>
<li parser-repeat="data_list">{title}</li>
</ul>
<table class="table table-bordered" border="1" style="width:500px">
<thead>
<tr bgcolor="#aaaaaa">
<td>Title</td>
<td>body</td>
</tr>
</thead>
<tbody>
<tr parser-repeat="blog_entries">
<td>{title}</td>
<td>{body}</td>
</tr>
</table>
</table>
    </body>
</html>


แหล่งข้อมูลอ้างอิง

CodeIgniter - Template Parser Class
https://www.codeigniter.com/userguide3/libraries/parser.html

15 PHP regular expressions for web developers
https://www.catswhocode.com/blog/15-php-regular-expressions-for-web-developers

PHP Codeigniter กับการใช้งาน Template Parser Class แล้วไม่สามารถเรียกใช้ตัวแปรด้านนอก Variable Pairs ที่กำหนดไว้ได้

โดย SONGCHAI SAETERN

มาดัดแปลงฟังก์ชั่น Template Engine ใน CodeIgniter กัน



โค้ดด้านล่างนี้เป็นส่วนของ View ที่มาจากเว็บไซต์ CodeIgniter
https://www.codeigniter.com/userguide3/libraries/parser.html


โดยจะมี Controller ที่ทำหน้าที่รับส่งค่าดังนี้


เมื่อลองรันทดสอบบนหน้าเว็บบราวเซอร์ จะเห็นว่าข้อมูลในอาร์เรย์จะแทรกลงไปในตำแหน่งวงเล็บที่ตรงกับ Key ของข้อมูลแต่ละตัว และในส่วนของ {blog_entries} .......... {/blog_entries} จะพิเศษตรงที่ มีการวนลูปตามจำนวนอาร์เรย์ให้เราเรียบร้อย โดยที่เราไม่ต้องเขียนคำสั่งวนลูปอีกเลย



แต่ปัญหามีอยู่ว่า ในบล็อก {blog_entries} .......... {/blog_entries}  ไม่สามารถเรียกใช้ {site_url} ซึ่งเราได้สร้างอาร์เรย์รับค่าเอาไว้แล้ว

หน้า controllers/Welcome.php
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Welcome extends CI_Controller {
public function index()
{
$this->load->helper('url');
$this->load->library('parser');
$data = array(
'site_url' => site_url(),
'blog_title'   => 'My Blog Title',
'blog_heading' => 'My Blog Heading',
'blog_entries' => array(
array('title' => 'Title 1', 'body' => 'Body 1'),
array('title' => 'Title 2', 'body' => 'Body 2'),
array('title' => 'Title 3', 'body' => 'Body 3'),
array('title' => 'Title 4', 'body' => 'Body 4'),
array('title' => 'Title 5', 'body' => 'Body 5')
)
);
$this->parser->parse('welcome_message', $data);
}
}
?>

ไฟล์ views/welcome_message.php

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{blog_title}</title>
<style type="text/css">
body {
background-color: #fff;
margin: 40px;
font: 13px/20px normal Helvetica, Arial, sans-serif;
color: #4F5155;
}
</style>
</head>
<body>
        <h3>H3 : {blog_heading}</h3>
<p>ลิงค์ : {site_url}</p>
        {blog_entries}
            <h5>H5 : {title}</h5>
            <p>P : {body}</p>
<p>Link : {site_url}</p>
        {/blog_entries}
</body>
</html>

เมื่อรันบนหน้าเว็บบราวเซอร์ผลลัพธ์จะแสดงเฉพาะส่วนที่อยู่ด้านนอก {blog_entries} .......... {/blog_entries} เท่านั้น ส่วนที่อยู่ในบล็อก จะเป็นแค่ {site_url} ธรรมดา





ดังนั้นเพื่อเติมเต็มความต้องการที่ขาดหายไป ผมก็เลยจัดการเขียนทับฟังก์ชั่น parse() ไปซะเลย 

โดยแทนที่ข้อความในวงเล็บที่มี 2 ตัวซ้อนกันแทนการใช้วงเล็บตัวเดียว เพื่อไม่ให้ทับซ้อนกับการทำงานเดิมของโค้ดที่อยู่ในบล็อก

สร้างไฟล์ MY_Parser.php  เอาไว้ที่ application/libraries/MY_Parser.php

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * Overrides the CI Template Parser to allow for multiple occurrences of the
 * same variable pair
 *
 */
class MY_Parser extends CI_Parser {
// --------------------------------------------------------------------
/**
* Parse a template
*
* Parses pseudo-variables contained in the specified template view,
* replacing them with the data in the second param
*
* @param string
* @param array
* @param bool
* @return string
*/
public function parse($template, $data, $return = FALSE)
{
$template = $this->CI->load->view($template, $data, TRUE);
$results = $this->_parse_double($template, $data);
$results = $this->_parse($results, $data, TRUE);
if ($return === FALSE)
{
$this->CI->output->append_output($results);
}
return $results;
}
// --------------------------------------------------------------------
/**
* Parse a single key/value
*
* @param string
* @param string
* @param string
* @return string
*/
protected function _parse_double($results, $data)
{
$replace = array();
preg_match_all("/\{\{(.*?)\}\}/si", $results, $matches);
foreach ($matches[1] as $match)
{
$key = '{{'.$match.'}}';
$replace[$key] = isset($data[$match]) ? $data[$match] : $key;
}
$results = strtr($results, $replace);
return $results;
}
}
// END Parser Class
/* End of file MY_Parser.php */
/* Location: ./application/libraries/MY_Parser.php */


และแก้ไขในส่วนของ View ให้ใช้วงเล็บซ้อนกัน 2 ตัว เมื่อต้องการเรียกใช้ข้อมูลจากตัวแปรที่อยู่ด้านนอกของบล็อกที่เป็น Variable Pairs นั้นๆ

จะสังเกตว่า ผมเปลี่ยนมาใช้ {{site_url}} แทนวงเล็บชั้นเดียว

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{blog_title}</title>
<style type="text/css">
body {
background-color: #fff;
margin: 40px;
font: 13px/20px normal Helvetica, Arial, sans-serif;
color: #4F5155;
}
</style>
</head>
<body>
        <h3>H3 : {blog_heading}</h3>
<p>ลิงค์ : {site_url}</p>
        {blog_entries}
            <h5>H5 : {title}</h5>
            <p>P : {body}</p>
<p>Link : {{site_url}}</p>
        {/blog_entries}
</body>
</html>

เมื่อไปรันบนหน้าเว็บเบราเซอร์จะได้ผลลัพธ์ตามที่ต้องการดังนี้




ยังไงก็ลองเอาไปปรับใช้กันดูนะครับ

ขอให้ทุกท่านสนุกกับการฝึกเขียน PHP แบบ MVC ด้วย CodeIgniter Framework นะครับ :)


ข้อมูลอ้างอิง

สร้าง Pattern เพื่อค้นหาคำในวงเล็บ {{something1}} something2 {{something3}} something4https://stackoverflow.com/questions/5897478/how-to-get-the-shortest-rather-than-longest-possible-regex-match-with-preg-match

ฟังก์ชั่นแทนที่ข้อความ strtr()
http://php.net/manual/en/function.strtr.php



PHP CI MANIA PHP Code Generator 
โปรแกรมช่วยสร้างโค้ด ลดเวลาการเขียนโปรแกรม เขียนโปรแกรมง่ายและสะดวกขึ้น
สนใจสั่งซื้อราคาสุดคุ้ม >> http://fastcoding.phpcodemania.com/