本文實例講述了PHP模板解析類。分享給大家供大家參考。具體如下:
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
|
<?php class template { private $vars = array (); private $conf = '' ; private $tpl_name = 'index' ; //如果模板不存在 會查找當前 controller默認index模板 private $tpl_suffix = '.html' ; //如果CONFIG沒配置默認后綴 則顯示 private $tpl_compile_suffix = '.tpl.php' ; //編譯模板路徑 private $template_tag_left = '<{' ; //模板左標簽 private $template_tag_right = '}>' ; //模板右標簽 private $template_c = '' ; //編譯目錄 private $template_path = '' ; //模板完整路徑 private $template_name = '' ; //模板名稱 index.html //定義每個模板的標簽的元素 private $tag_foreach = array ( 'from' , 'item' , 'key' ); private $tag_include = array ( 'file' ); //目前只支持讀取模板默認路徑 public function __construct( $conf ) { $this ->conf = & $conf ; $this ->template_c = $this ->conf[ 'template_config' ][ 'template_c' ]; //編譯目錄 $this ->_tpl_suffix = $this ->tpl_suffix(); } private function str_replace ( $search , $replace , $content ) { if ( empty ( $search ) || empty ( $replace ) || empty ( $content )) return false; return str_replace ( $search , $replace , $content ); } /** * preg_match_all * @param $pattern 正則 * @param $content 內(nèi)容 * @return array */ private function preg_match_all( $pattern , $content ) { if ( empty ( $pattern ) || empty ( $content )) core::show_error( '查找模板標簽失敗!' ); preg_match_all( "/" . $this ->template_tag_left. $pattern . $this ->template_tag_right. "/is" , $content , $match ); return $match ; } /** * 模板文件后綴 */ public function tpl_suffix() { $tpl_suffix = empty ( $this ->conf[ 'template_config' ][ 'template_suffix' ]) ? $this ->tpl_suffix : $this ->conf[ 'template_config' ][ 'template_suffix' ] ; return $tpl_suffix ; } /** * 此處不解釋了 * @return */ public function assign( $key , $value ) { $this ->vars[ $key ] = $value ; } /** * 渲染頁面 * @param * 使用方法 1 * $this->view->display('error', 'comm/'); * 默認是指向TPL模版的跟目錄,所以comm/就是 tpl/comm/error.html * 使用方法 2 * $this->view->display('errorfile'); * 默認指向控制器固定的文件夾 * 例如你的域名是 http://heartphp/admin/index, 那么正確路徑就是tpl/admin/index/errorfile.html * @return */ public function display( $filename = '' , $view_path = '' ) { $tpl_path_arr = $this ->get_tpl( $filename , $view_path ); //獲取TPL完整路徑 并且向指針傳送路徑以及名稱 if (! $tpl_path_arr ) core::show_error( $filename . $this ->_tpl_suffix. '模板不存在' ); //編譯開始 $this ->view_path_param = $view_path ; //用戶傳遞過來的模版跟目錄 $this ->compile(); } /** * 編譯控制器 * @param * @return */ private function compile() { $filepath = $this ->template_path. $this ->template_name; $compile_dirpath = $this ->check_temp_compile(); $vars_template_c_name = str_replace ( $this ->_tpl_suffix, '' , $this ->template_name); $include_file = $this ->template_replace( $this ->read_file( $filepath ), $compile_dirpath , $vars_template_c_name ); //解析 if ( $include_file ) { $this ->read_config() && $config = $this ->read_config(); extract( $this ->vars, EXTR_SKIP); [url=home.php?mod=space&uid=48608]@ include [/url] $include_file ; } } /** * 讀取當前項目配置文件 */ protected function read_config() { if ( file_exists (SYSTEM_PATH. 'conf/config.php' )) { @ include SYSTEM_PATH. 'conf/config.php' ; return $config ; } return false; } /** * 解析模板語法 * @param $str 內(nèi)容 * @param $compile_dirpath 模版編譯目錄 * @param $vars_template_c_name 模版編譯文件名 * @return 編譯過的PHP模板文件名 */ private function template_replace( $str , $compile_dirpath , $vars_template_c_name ) { if ( empty ( $str )) core::show_error( '模板內(nèi)容為空!' ); //處理編譯頭部 $compile_path = $compile_dirpath . $vars_template_c_name . $this ->tpl_compile_suffix; //編譯文件 if ( is_file ( $compile_path )) { //$header_content = $this->get_compile_header($compile_path); //$compile_date = $this->get_compile_header_comment($header_content); $tpl_filemtime = filemtime ( $this ->template_path. $this ->template_name); $compile_filemtime = filemtime ( $compile_path ); //echo $tpl_filemtime.'=='.date('Y-m-d H:i:s', $tpl_filemtime).'<br/>'; //echo $compile_filemtime.'=='.date('Y-m-d H:i:s', $compile_filemtime); //如果文件過期編譯 當模板標簽有include并且有修改時 也重新編譯 //<{include file="public/left.html"}> 當修改include里的文件,非DEBUG模式時 如果不更改主文件 目前是不重新編譯include里的文件,我在考慮是否也要更改,沒想好,暫時這樣,所以在開發(fā)階段一定要開啟DEBUG=1模式 要不然修改include文件無效 。 有點羅嗦,不知道表述清楚沒 if ( $tpl_filemtime > $compile_filemtime || DEBUG) { $ret_file = $this ->compile_file( $vars_template_c_name , $str , $compile_dirpath ); } else { $ret_file = $compile_path ; } } else { //編譯文件不存在 創(chuàng)建他 $ret_file = $this ->compile_file( $vars_template_c_name , $str , $compile_dirpath ); } return $ret_file ; } /** * 模板文件主體 * @param string $str 內(nèi)容 * @return html */ private function body_content( $str ) { //解析 $str = $this ->parse( $str ); $header_comment = "Create On##" .time(). "|Compiled from##" . $this ->template_path. $this ->template_name; $content = "<? if(!defined('IS_HEARTPHP')) exit('Access Denied');/*{$header_comment}*/?>\r\n$str" ; return $content ; } /** * 開始解析相關(guān)模板標簽 * @param $content 模板內(nèi)容 */ private function parse( $content ) { //foreach $content = $this ->parse_foreach( $content ); //include $content = $this ->parse_include( $content ); //if $content = $this ->parse_if( $content ); //elseif $content = $this ->parse_elseif( $content ); //模板標簽公用部分 $content = $this ->parse_comm( $content ); //轉(zhuǎn)為PHP代碼 $content = $this ->parse_php( $content ); return $content ; } /** * echo 如果默認直接<{$config['domain']}> 轉(zhuǎn)成 <?php echo $config['domain']?> */ private function parse_echo( $content ) { } /** * 轉(zhuǎn)換為PHP * @param $content html 模板內(nèi)容 * @return html 替換好的HTML */ private function parse_php( $content ){ if ( empty ( $content )) return false; $content = preg_replace( "/" . $this ->template_tag_left. "(.+?)" . $this ->template_tag_right. "/is" , "<?php $1 ?>" , $content ); return $content ; } /** * if判斷語句 * <{if empty($zhang)}> * zhang * <{elseif empty($liang)}> * liang * <{else}> * zhangliang * <{/if}> */ private function parse_if( $content ) { if ( empty ( $content )) return false; //preg_match_all("/".$this->template_tag_left."if\s+(.*?)".$this->template_tag_right."/is", $content, $match); $match = $this ->preg_match_all( "if\s+(.*?)" , $content ); if (!isset( $match [1]) || ! is_array ( $match [1])) return $content ; foreach ( $match [1] as $k => $v ) { //$s = preg_split("/\s+/is", $v); //$s = array_filter($s); $content = str_replace ( $match [0][ $k ], "<?php if({$v}) { ?>" , $content ); } return $content ; } private function parse_elseif( $content ) { if ( empty ( $content )) return false; //preg_match_all("/".$this->template_tag_left."elseif\s+(.*?)".$this->template_tag_right."/is", $content, $match); $match = $this ->preg_match_all( "elseif\s+(.*?)" , $content ); if (!isset( $match [1]) || ! is_array ( $match [1])) return $content ; foreach ( $match [1] as $k => $v ) { //$s = preg_split("/\s+/is", $v); //$s = array_filter($s); $content = str_replace ( $match [0][ $k ], "<?php } elseif ({$v}) { ?>" , $content ); } return $content ; } /** * 解析 include include標簽不是實時更新的 當主體文件更新的時候 才更新標簽內(nèi)容,所以想include生效 請修改一下主體文件 * 記錄一下 有時間開發(fā)一個當DEBUG模式的時候 每次執(zhí)行刪除模版編譯文件 * 使用方法 <{include file="www.phpddt.com"}> * @param $content 模板內(nèi)容 * @return html */ private function parse_include( $content ) { if ( empty ( $content )) return false; //preg_match_all("/".$this->template_tag_left."include\s+(.*?)".$this->template_tag_right."/is", $content, $match); $match = $this ->preg_match_all( "include\s+(.*?)" , $content ); if (!isset( $match [1]) || ! is_array ( $match [1])) return $content ; foreach ( $match [1] as $match_key => $match_value ) { $a = preg_split( "/\s+/is" , $match_value ); $new_tag = array (); //分析元素 foreach ( $a as $t ) { $b = explode ( '=' , $t ); if (in_array( $b [0], $this ->tag_include)) { if (! empty ( $b [1])) { $new_tag [ $b [0]] = str_replace ( "\"" , "" , $b [1]); } else { core::show_error( '模板路徑不存在!' ); } } } extract( $new_tag ); //查詢模板文件 foreach ( $this ->conf[ 'view_path' ] as $v ){ $conf_view_tpl = $v . $file ; //include 模板文件 if ( is_file ( $conf_view_tpl )) { $c = $this ->read_file( $conf_view_tpl ); $inc_file = str_replace ( $this ->_tpl_suffix, '' , basename ( $file )); $this ->view_path_param = dirname( $file ). '/' ; $compile_dirpath = $this ->check_temp_compile(); $include_file = $this ->template_replace( $c , $compile_dirpath , $inc_file ); //解析 break ; } else { core::show_error( '模板文件不存在,請仔細檢查 文件:' . $conf_view_tpl ); } } $content = str_replace ( $match [0][ $match_key ], '<?php include("' . $include_file . '")?>' , $content ); } return $content ; } /** * 解析 foreach * 使用方法 <{foreach from=$lists item=value key=kk}> * @param $content 模板內(nèi)容 * @return html 解析后的內(nèi)容 */ private function parse_foreach( $content ) { if ( empty ( $content )) return false; //preg_match_all("/".$this->template_tag_left."foreach\s+(.*?)".$this->template_tag_right."/is", $content, $match); $match = $this ->preg_match_all( "foreach\s+(.*?)" , $content ); if (!isset( $match [1]) || ! is_array ( $match [1])) return $content ; foreach ( $match [1] as $match_key => $value ) { $split = preg_split( "/\s+/is" , $value ); $split = array_filter ( $split ); $new_tag = array (); foreach ( $split as $v ) { $a = explode ( "=" , $v ); if (in_array( $a [0], $this ->tag_foreach)) { //此處過濾標簽 不存在過濾 $new_tag [ $a [0]] = $a [1]; } } $key = '' ; extract( $new_tag ); $key = ( $key ) ? '$' . $key . ' =>' : '' ; $s = '<?php foreach(' . $from . ' as ' . $key . ' $' . $item . ') { ?>' ; $content = $this -> str_replace ( $match [0][ $match_key ], $s , $content ); } return $content ; } /** * 匹配結(jié)束 字符串 */ private function parse_comm( $content ) { $search = array ( "/" . $this ->template_tag_left. "\/foreach" . $this ->template_tag_right. "/is" , "/" . $this ->template_tag_left. "\/if" . $this ->template_tag_right. "/is" , "/" . $this ->template_tag_left. "else" . $this ->template_tag_right. "/is" , ); $replace = array ( "<?php } ?>" , "<?php } ?>" , "<?php } else { ?>" ); $content = preg_replace( $search , $replace , $content ); return $content ; } /** * 檢查編譯目錄 如果沒有創(chuàng)建 則遞歸創(chuàng)建目錄 * @param string $path 文件完整路徑 * @return 模板內(nèi)容 */ private function check_temp_compile() { //$paht = $this->template_c. $tpl_path = ( $this ->view_path_param) ? $this ->view_path_param : $this ->get_tpl_path() ; $all_tpl_apth = $this ->template_c. $tpl_path ; if (! is_dir ( $all_tpl_apth )) { $this ->create_dir( $tpl_path ); } return $all_tpl_apth ; } /** * 讀文件 * @param string $path 文件完整路徑 * @return 模板內(nèi)容 */ private function read_file( $path ) { //$this->check_file_limits($path, 'r'); if (( $r = @ fopen ( $path , 'r' )) === false) { core::show_error( '模版文件沒有讀取或執(zhí)行權(quán)限,請檢查!' ); } $content = fread ( $r , filesize ( $path )); fclose( $r ); return $content ; } /** * 寫文件 * @param string $filename 文件名 * @param string $content 模板內(nèi)容 * @return 文件名 */ private function compile_file( $filename , $content , $dir ) { if ( empty ( $filename )) core::show_error( "{$filename} Creation failed" ); $content = $this ->body_content( $content ); //對文件內(nèi)容操作 //echo '開始編譯了====='; $f = $dir . $filename . $this ->tpl_compile_suffix; //$this->check_file_limits($f, 'w'); if (( $fp = @ fopen ( $f , 'wb' )) === false) { core::show_error( $f . '<br/>編譯文件失敗,請檢查文件權(quán)限.' ); } //開啟flock flock ( $fp , LOCK_EX + LOCK_NB); fwrite( $fp , $content , strlen ( $content )); flock ( $fp , LOCK_UN + LOCK_NB); fclose( $fp ); return $f ; } /** * 這個檢查文件權(quán)限函數(shù) 暫時廢棄了 * @param [$path] [路徑] * @param [status] [w=write, r=read] */ public function check_file_limits( $path , $status = 'rw' ) { clearstatcache(); if (! is_writable ( $path ) && $status == 'w' ) { core::show_error( "{$path}<br/>沒有寫入權(quán)限,請檢查." ); } elseif (! is_readable ( $path ) && $status == 'r' ) { core::show_error( "{$path}<br/>沒有讀取權(quán)限,請檢查." ); } elseif ( $status == 'rw' ) { //check wirte and read if (! is_writable ( $path ) || ! is_readable ( $path )) { core::show_error( "{$path}<br/>沒有寫入或讀取權(quán)限,請檢查" ); } } } /** * 讀取編譯后模板的第一行 并分析成數(shù)組 * @param string $filepath 文件路徑 * @param number $line 行數(shù) * @return 返回指定行數(shù)的字符串 */ /* private function get_compile_header($filepath, $line = 0) { if(($file_arr = @file($filepath)) === false) { core::show_error($filepath.'<br/>讀取文件失敗,請檢查文件權(quán)限!'); } return $file_arr[0]; } */ /** * 分析頭部注釋的日期 * @param string $cotnent 編譯文件頭部第一行 * @return 返回上一次日期 */ /* private function get_compile_header_comment($content) { preg_match("/\/\*(.*?)\*\//", $content, $match); if(!isset($match[1]) || empty($match[1])) core::show_error('編譯錯誤!'); $arr = explode('|', $match[1]); $arr_date = explode('##', $arr[0]); return $arr_date[1]; } */ /** * 獲取模板完整路徑 并返回已存在文件 * @param string $filename 文件名 * @param string $view_path 模板路徑 * @return */ private function get_tpl( $filename , $view_path ) { empty ( $filename ) && $filename = $this ->tpl_name; //遍歷模板路徑 foreach ( $this ->conf[ 'view_path' ] as $path ) { if ( $view_path ) { //直接從tpl跟目錄找文件 $tpl_path = $path . $view_path ; $view_file_path = $tpl_path . $filename . $this ->_tpl_suffix; } else { //根據(jù)目錄,控制器,方法開始找文件 $view_file_path = ( $tpl_path = $this ->get_tpl_path( $path )) ? $tpl_path . $filename . $this ->_tpl_suffix : exit (0); } if ( is_file ( $view_file_path )) { //向指針傳送模板路徑和模板名稱 $this ->template_path = $tpl_path ; // $this ->template_name = $filename . $this ->_tpl_suffix; return true; } else { core::show_error( $filename . $this ->_tpl_suffix. '模板不存在' ); } } } /** * 獲取模板路徑 * @param string $path 主目錄 * @return URL D和M的拼接路徑 */ private function get_tpl_path( $path = '' ) { core::get_directory_name() && $path_arr [0] = core::get_directory_name(); core::get_controller_name() && $path_arr [1] = core::get_controller_name(); ( is_array ( $path_arr )) ? $newpath = implode( '/' , $path_arr ) : core::show_error( '獲取模板路徑失敗!' ) ; return $path . $newpath . '/' ; } /** * 創(chuàng)建目錄 * @param string $path 目錄 * @return */ private function create_dir( $path , $mode = 0777){ if ( is_dir ( $path )) return false; $dir_arr = explode ( '/' , $path ); $dir_arr = array_filter ( $dir_arr ); $allpath = '' ; $newdir = $this ->template_c; foreach ( $dir_arr as $dir ) { $allpath = $newdir . '/' . $dir ; if (! is_dir ( $allpath )) { $newdir = $allpath ; if (!@ mkdir ( $allpath , $mode )) { core::show_error( $allpath . '<br/>創(chuàng)建目錄失敗,請檢查是否有可都寫權(quán)限!' ); } chmod ( $allpath , $mode ); } else { $newdir = $allpath ; } } return true; } public function __destruct(){ $this ->vars = null; $this ->view_path_param = null; } } |
希望本文所述對大家的php程序設(shè)計有所幫助。