1: <?php
2: /**
3: * @filesource Kotchasan/Template.php
4: *
5: * @copyright 2016 Goragod.com
6: * @license http://www.kotchasan.com/license/
7: *
8: * @see http://www.kotchasan.com/
9: */
10:
11: namespace Kotchasan;
12:
13: /**
14: * Template engine.
15: *
16: * @author Goragod Wiriya <admin@goragod.com>
17: *
18: * @since 1.0
19: */
20: class Template
21: {
22: /**
23: * จำนวนคอลัมน์ สำหรับการแสดงผลด้วย Grid.
24: *
25: * @var int
26: */
27: protected $cols = 0;
28: /**
29: * แอเรย์ของข้อมูล.
30: *
31: * @var array
32: */
33: protected $items;
34: /**
35: * ตัวแปรสำหรับการขึ้นแถวใหม่ (Grid).
36: *
37: * @var int
38: */
39: protected $num;
40: /**
41: * ชื่อ template ที่กำลังใช้งานอยู่ รวมโฟลเดอร์ที่เก็บ template ด้วย
42: * นับแต่ DOCUMENT_ROOT เช่น skin/default/.
43: *
44: * @var string
45: */
46: protected static $src;
47: /**
48: * ข้อมูล template.
49: *
50: * @var string
51: */
52: private $skin;
53:
54: /**
55: * ฟังก์ชั่นกำหนดค่าตัวแปรของ template
56: * ฟังก์ชั่นนี้จะแทนที่ตัวแปรที่ส่งทั้งหมดลงใน template ทันที.
57: *
58: * @param array $array ชื่อที่ปรากฏใน template รูปแบบ array(key1=>val1,key2=>val2)
59: *
60: * @return \static
61: */
62: public function add($array)
63: {
64: if ($this->cols > 0 && $this->num == 0) {
65: $this->items[] = "</div>\n<div class=row>";
66: $this->num = $this->cols;
67: }
68: $this->items[] = self::pregReplace(array_keys($array), array_values($array), $this->skin);
69: --$this->num;
70:
71: return $this;
72: }
73:
74: /**
75: * โหลด template
76: * ครั้งแรกจะตรวจสอบไฟล์จาก module ถ้าไม่พบ จะใช้ไฟล์จาก owner.
77: *
78: * @assert ('', '', 'FileNotFound')->isEmpty() [==] true
79: *
80: * @param string $owner ชื่อโมดูลที่ติดตั้ง
81: * @param string $module ชื่อโมดูล
82: * @param string $name ชื่อ template ไม่ต้องระบุนามสกุลของไฟล์
83: *
84: * @return \static
85: */
86: public static function create($owner, $module, $name)
87: {
88: return self::createFromHTML(self::load($owner, $module, $name));
89: }
90:
91: /**
92: * โหลด template จากไฟล์.
93: *
94: * @assert ('FileNotFound') [throws] InvalidArgumentException
95: *
96: * @param string $filename
97: *
98: * @throws \InvalidArgumentException ถ้าไม่พบไฟล์
99: *
100: * @return \static
101: */
102: public static function createFromFile($filename)
103: {
104: if (is_file($filename)) {
105: return self::createFromHTML(file_get_contents($filename));
106: } else {
107: throw new \InvalidArgumentException('Template file not found');
108: }
109: }
110:
111: /**
112: * สร้าง template จาก HTML.
113: *
114: * @param string $html
115: *
116: * @return \static
117: */
118: public static function createFromHTML($html)
119: {
120: $obj = new static();
121: $obj->skin = $html;
122: $obj->items = array();
123: $obj->num = -1;
124:
125: return $obj;
126: }
127:
128: /**
129: * คืนค่าไดเร็คทอรี่ของ template ตั้งแต่ DOCUMENT_ROOT เช่น skin/default/.
130: *
131: * @return string
132: */
133: public static function get()
134: {
135: return self::$src;
136: }
137:
138: /**
139: * ฟังก์ชั่นตรวจสอบว่ามีการ add ข้อมูลมาหรือเปล่า
140: * คืนค่า true ถ้ามีการเรียกใช้คำสั่ง add มาก่อนหน้า, หรือ false ถ้าไม่ใช่.
141: *
142: * @return bool
143: */
144: public function hasItem()
145: {
146: return empty($this->items) ? false : true;
147: }
148:
149: /**
150: * กำหนด template ที่ต้องการ.
151: *
152: * @param string $skin ไดเร็คทอรี่ของ template ตั้งแต่ DOCUMENT_ROOT ไม่ต้องมี / ปิดท้าย เช่น skin/default
153: */
154: public static function init($skin)
155: {
156: self::$src = $skin == '' ? '' : $skin.'/';
157: }
158:
159: /**
160: * ฟังก์ชั่นใส่ HTML ลงใน template ตรงๆ
161: * ใช้สำหรับแทรก HTML ลงระหว่างแต่ละรายการ.
162: *
163: * @param string $html โค้ด HTML
164: *
165: * @return \static
166: */
167: public function insertHTML($html)
168: {
169: $this->items[] = $html;
170:
171: return $this;
172: }
173:
174: /**
175: * ตรวจสอบว่ามีไฟล์ Template ถูกเลือกหรือไม่
176: * คืนค่า true ถ้าไม่พบไฟล์ Template หรือ Template ว่างเปล่า, อื่นๆคืนค่า False.
177: *
178: * @return bool
179: */
180: public function isEmpty()
181: {
182: return $this->skin == '';
183: }
184:
185: /**
186: * โหลด template
187: * ครั้งแรกจะตรวจสอบไฟล์จาก $module ถ้าไม่พบ จะใช้ไฟล์จาก $owner
188: * ถ้าไม่พบคืนค่าว่าง.
189: *
190: * @param string $owner ชื่อโมดูลที่ติดตั้ง
191: * @param string $module ชื่อโมดูลที่ลงทะเบียน
192: * @param string $name ชื่อ template ไม่ต้องระบุนามสกุลของไฟล์
193: *
194: * @return string
195: */
196: public static function load($owner, $module, $name)
197: {
198: $src = APP_PATH.self::$src;
199: if ($module != '' && is_file($src.$module.'/'.$name.'.html')) {
200: return file_get_contents($src.$module.'/'.$name.'.html');
201: } elseif ($owner != '' && is_file($src.$owner.'/'.$name.'.html')) {
202: return file_get_contents($src.$owner.'/'.$name.'.html');
203: } elseif (is_file($src.$name.'.html')) {
204: return file_get_contents($src.$name.'.html');
205: }
206:
207: return '';
208: }
209:
210: /**
211: * ฟังก์ชั่น preg_replace รองรับถึง PHP7.
212: *
213: * @assert ('/{TITLE}/', 'Title', '<b>{TITLE}</b>') [==] '<b>Title</b>'
214: * @assert ('/{LNG_([\w\s\.\-\'\(\),%\/:&\#;]+)}/e', '\Kotchasan\Language::parse(array(1=>"$1"))', '<b>{LNG_Language test}</b>') [==] '<b>Language test</b>'
215: *
216: * @param array $patt คีย์ใน template
217: * @param array $replace ข้อความที่จะถูกแทนที่ลงในคีย์
218: * @param string $skin template
219: *
220: * @return string
221: */
222: public static function pregReplace($patt, $replace, $skin)
223: {
224: if (!is_array($patt)) {
225: $patt = array($patt);
226: }
227: if (!is_array($replace)) {
228: $replace = array($replace);
229: }
230: foreach ($patt as $i => $item) {
231: if (preg_match('/(.*\/(.*?))[e](.*?)$/', $item, $patt) && preg_match('/^([\\\\a-z0-9]+)::([a-z0-9_\\\\]+).*/i', $replace[$i], $func)) {
232: $skin = preg_replace_callback($patt[1].$patt[3], array($func[1], $func[2]), $skin);
233: } else {
234: $skin = preg_replace($item, $replace[$i], $skin);
235: }
236: }
237:
238: return $skin;
239: }
240:
241: /**
242: * คืนค่า HTML ถ้าไม่พบ template คืนค่าว่าง.
243: *
244: * @return string
245: */
246: public function render()
247: {
248: if ($this->cols === 0) {
249: // template
250: return empty($this->items) ? $this->skin : implode("\n", $this->items);
251: } elseif (!empty($this->items)) {
252: // grid
253: return "<div class=row>\n".implode("\n", $this->items)."\n</div>";
254: }
255:
256: return '';
257: }
258: }
259: