PageRenderTime 27ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/core/gw_includes/class.template3.php

http://glossword.googlecode.com/
PHP | 631 lines | 432 code | 41 blank | 158 comment | 22 complexity | 0d89c26321dd09f2ddf10438d0e90b03 MD5 | raw file
  1. <?php
  2. /*
  3. Usage:
  4. -- HTML (e.tpl.html):
  5. <ul>
  6. {d START BLOCK}
  7. <li> I would say that {n} times.</li>
  8. <li> Here - {global} </li>
  9. {/d}
  10. ??. {global}
  11. </ul>
  12. -- PHP (e.php):
  13. include('class.site_template.php');
  14. $oTpl = new site_class_templates;
  15. $oTpl->new_template('e.tpl.html');
  16. $oTpl->assign( 'ctime', @date("Y-M-d") );
  17. for ($i = 1; $i < 5; $i++)
  18. {
  19. $oTpl->assign('n', $i);
  20. $oTpl->parse_block('number');
  21. }
  22. $oTpl->assign_global('global', '?????????? ??????????');
  23. echo $oTpl->get_html();
  24. // $oTpl->display();
  25. */
  26. class site_class_templates
  27. {
  28. /* All variables for the template */
  29. public $ar_variables = array();
  30. public $ar_variables_global = array();
  31. /* */
  32. public $phrase_not_found = 'Not found';
  33. /* */
  34. public $cfg;
  35. /* */
  36. public $ar_compiled = array();
  37. public $ar_run = array();
  38. public $ar_block_v = array();
  39. public $ar_block_i = array();
  40. public $ar_block_c = array();
  41. public $ar_file_events = array();
  42. /* */
  43. public $ar_d = array();
  44. /* */
  45. public function __construct($ar_cfg = array())
  46. {
  47. $ar_cfg_default = array(
  48. 'template_extension' => '', /* Extensions for HTML-files */
  49. 'path_source' => '.', /* Path to HTML-files */
  50. 'path_cache' => 'cache' /* Path to store compiled PHP-code */
  51. );
  52. /* */
  53. foreach ($ar_cfg as $k => $v)
  54. {
  55. $ar_cfg_default[$k] = $v;
  56. }
  57. /* */
  58. $this->cfg =& $ar_cfg_default;
  59. /* Start new parser object */
  60. $this->oParser = new site_class_templates_parser();
  61. }
  62. /**
  63. * Assigns a local variable.
  64. *
  65. * Usage:
  66. * $oTpl->assign( array('varname' => 'value') );
  67. * $oTpl->assign( 'varname', 'value' );
  68. *
  69. * @param $varname String or Array
  70. * @param $value String
  71. * @access public
  72. */
  73. public function assign($varname, $value = '')
  74. {
  75. $str = '';
  76. if (is_array($varname))
  77. {
  78. foreach ($varname as $k => $v)
  79. {
  80. /* $k is variable, $variable => crc32($variable) */
  81. $this->ar_variables[$this->_parse_var($k)] = strval($v);
  82. }
  83. }
  84. else
  85. {
  86. $this->ar_variables[$this->_parse_var($varname)] = strval($value);
  87. }
  88. }
  89. /**
  90. * Assigns a global variable.
  91. *
  92. * Usage:
  93. * $oTpl->assign( array('varname' => 'value') );
  94. * $oTpl->assign( 'varname', 'value' );
  95. *
  96. * @param $varname String or Array
  97. * @param $value String
  98. * @access public
  99. */
  100. public function assign_global($varname, $value = '')
  101. {
  102. if (is_array($varname))
  103. {
  104. foreach ($ar as $k => $v)
  105. {
  106. /* $k is variable, $variable => crc32($variable) */
  107. $this->ar_variables_global[$this->_parse_var($k)] = strval($v);
  108. }
  109. }
  110. else
  111. {
  112. $this->ar_variables_global[$this->_parse_var($varname)] = strval($value);
  113. }
  114. }
  115. /* Shorthand for new_template_file() */
  116. public function new_template($template)
  117. {
  118. return $this->new_template_file($template);
  119. }
  120. /**
  121. * Reads HTML-template and compiles it into PHP-code. File-based version.
  122. */
  123. public function new_template_file($template)
  124. {
  125. $k = 0;
  126. $ar[$k]['settings_key'] =& $template;
  127. $ar[$k]['date_modified'] = $ar[$k]['date_compiled'] = 0;
  128. $ar[$k]['code'] = $ar[$k]['code_i'] = $ar[$k]['settings_value'] = '';
  129. /* if a compiled PHP-code exists */
  130. if (file_exists($this->cfg['path_cache'].'/code-'.$template.'.php'))
  131. {
  132. $ar[$k]['date_compiled'] = filemtime($this->cfg['path_cache'].'/code-'.$template.'.php');
  133. $ar[$k]['code'] = implode('', file($this->cfg['path_cache'].'/code-'.$template.'.php'));
  134. $ar[$k]['code_i'] = implode('', file($this->cfg['path_cache'].'/code_i-'.$template.'.php'));
  135. }
  136. /* Read the contents of file */
  137. if (file_exists($this->cfg['path_source'].'/'.$template.$this->cfg['template_extension']))
  138. {
  139. $ar[$k]['date_modified'] = filemtime($this->cfg['path_source'].'/'.$template.$this->cfg['template_extension']);
  140. /* Load a already compiled contents */
  141. if ($ar[$k]['date_compiled'] <= $ar[$k]['date_modified'])
  142. {
  143. $ar[$k]['settings_value'] = implode('', file($this->cfg['path_source'].'/'.$template.$this->cfg['template_extension']));
  144. $ar[$k]['settings_value'] = str_replace(array('{%', '%}'), array('{', '}'), $ar[$k]['settings_value']);
  145. }
  146. }
  147. else
  148. {
  149. print '<div>'.$this->phrase_not_found.': '.$this->cfg['path_source'].'/'.$template.$this->cfg['template_extension'].'</div>';
  150. }
  151. /* For each loaded template */
  152. foreach ($ar as $k => $ar_v)
  153. {
  154. /* Iterations in blocks */
  155. $ar_block_i = array();
  156. /* Create new template key to use in arrays with compiled code */
  157. $template_key = sprintf("%u", crc32($ar_v['settings_key']));
  158. /* Should be empty on first run */
  159. if (isset($this->ar_compiled[$template_key]))
  160. {
  161. /* Do not load HTML-file second time */
  162. continue;
  163. }
  164. /* Put template into array with compiled code */
  165. $this->ar_compiled[$template_key] = array(
  166. 'filename' => $ar_v['settings_key'],
  167. 'code' => $ar_v['code'],
  168. 'html' => $ar_v['settings_value']
  169. );
  170. /* Load compiled contents */
  171. if ($ar_v['date_modified'] < $ar_v['date_compiled'])
  172. {
  173. /* Runs before $this->_parse() */
  174. eval(' ?'.'>' . $ar_v['code_i'] . '<?php ');
  175. }
  176. else
  177. {
  178. /* Compile new PHP-code */
  179. eval( $this->_compile($template_key) );
  180. }
  181. /* Merge local iterations with global iteration */
  182. foreach ($ar_block_i as $k => $v)
  183. {
  184. $this->ar_block_i[$k] = $v;
  185. }
  186. #$this->ar_block_i = array_merge($this->ar_block_i, $ar_block_i);
  187. }
  188. }
  189. /**
  190. * Compiles PHP-code. Runs iterations ($ar_block_i).
  191. */
  192. public function _compile($template_key)
  193. {
  194. $str_return = '';
  195. /* Reset settings */
  196. $this->oParser->_reset();
  197. /* */
  198. $tmp = array();
  199. $tmp['filename_c'] = '';
  200. $tmp['str_i'] = $tmp['str'] = '';
  201. /* Search for the template and source HTML-code for it. */
  202. if (!isset($this->ar_compiled[$template_key]) && !isset($this->ar_compiled[$template_key]['html']))
  203. {
  204. return false;
  205. }
  206. /* */
  207. $ar_html = array();
  208. /* */
  209. $ar_php = array();
  210. /* HTML-code to compile */
  211. $tmp['tpl_content'] =& $this->ar_compiled[$template_key]['html'];
  212. /* */
  213. $tmp['filename_c'] =& $this->ar_compiled[$template_key]['filename'];
  214. /* */
  215. $tmp['filename_i'] =& $this->ar_compiled[$template_key]['filename'];
  216. /* Prepare replacements pairs */
  217. $ar_html = $ar_php = array();
  218. /* Fix for XML prolog in HTML-file */
  219. $ar_html[] = '<'.'?xml';
  220. $ar_php[] = '<'.'?php echo "<","?xml"; ?'.'>';
  221. /* Search for blocks and variables */
  222. $preg = "/(<!--[ ]?(START|END) BLOCK: (.+)-->|{([A-Za-z0-9\.:\/\-_]+)})/i";
  223. # $preg = "/({)([ A-Za-z0-9:\/_-]+)(})/i";
  224. if ( preg_match_all($preg, $tmp['tpl_content'], $tmp['tpl_matches']) )
  225. {
  226. foreach ($tmp['tpl_matches'][1] as $k => $varblock_name)
  227. {
  228. $ar_html[] = $tmp['tpl_matches'][1][$k];
  229. $tmp['variable'] = trim($tmp['tpl_matches'][4][$k]);
  230. $tmp['block_name'] = trim($tmp['tpl_matches'][3][$k]);
  231. if (strstr($varblock_name, 'START'))
  232. {
  233. /* Block starts */
  234. $ar_php[] = $this->oParser->start_block($tmp['block_name']);
  235. }
  236. elseif (strstr($varblock_name, 'END'))
  237. {
  238. /* Block ends */
  239. $ar_php[] = $this->oParser->end_block($tmp['block_name']);
  240. }
  241. else
  242. {
  243. /* The variable appears */
  244. $ar_php[] = $this->oParser->_var($tmp['variable']);
  245. }
  246. }
  247. }
  248. $tmp['str'] = str_replace($ar_html, $ar_php, $tmp['tpl_content']);
  249. /* Save PHP-code */
  250. $this->_file_save($tmp['filename_c'], $tmp['str'], 'code');
  251. /* Display code on the first run */
  252. $this->ar_compiled[$template_key]['code'] = $tmp['str'];
  253. /* save new iteration */
  254. $str_internal = $this->oParser->make_iterations();
  255. $tmp['str_i'] = '<'.'?php'. "\n" . $str_internal . '?'.'>';
  256. /* Save iterations */
  257. $this->_file_save($tmp['filename_i'], $tmp['str_i'], 'code_i');
  258. return $str_internal;
  259. }
  260. /* */
  261. public function display()
  262. {
  263. $this->_parse();
  264. echo $this->ar_variables[$this->last_parsed];
  265. }
  266. /* */
  267. public function get_html()
  268. {
  269. $this->_parse();
  270. return $this->ar_variables[$this->last_parsed];
  271. }
  272. /**
  273. * Executes compiled PHP-code and assigned variables.
  274. */
  275. private function _parse( $varname = '')
  276. {
  277. $this->last_parsed = $varname.'_last';
  278. if ( isset( $this->ar_variables[$this->last_parsed] ) )
  279. {
  280. return $this->ar_variables[$this->last_parsed];
  281. }
  282. /* */
  283. ob_start();
  284. foreach ($this->ar_compiled as $k => $ar_v)
  285. {
  286. @eval (' ?'.'>' . $ar_v['code'] . '<?'.'php ');
  287. /* Clean up */
  288. unset( $this->ar_compiled[$k] );
  289. }
  290. $this->ar_variables[$this->last_parsed] = ob_get_clean();
  291. }
  292. /* Saves PHP-code */
  293. private function _file_save($filename, $contents, $prefix)
  294. {
  295. $filename = $this->cfg['path_cache'].'/'.$prefix.'-'.$filename.'.php';
  296. /* Debug events */
  297. $this->ar_file_events[] = $filename;
  298. /* */
  299. $this->file_put_contents($filename, $contents, 'w');
  300. }
  301. /* Alias for parse_block() */
  302. public function new_block($blockname)
  303. {
  304. $this->parse_block($blockname);
  305. }
  306. /**
  307. * Assigns variables from $this->ar_variables to $this->ar_block_v.
  308. * Called on each step inside the loop.
  309. *
  310. * @access public
  311. */
  312. public function parse_block($blockname)
  313. {
  314. /* Optimize block name */
  315. $blockname = $this->_parse_var($blockname);
  316. /* Link to all variables in the current iteration */
  317. $vars =& $this->ar_block_i[$blockname]['var'];
  318. /* Link to an array */
  319. $linked_v =& $this->ar_block_v[$blockname][];
  320. /* Search for variable names in the block */
  321. if (is_array($vars))
  322. {
  323. foreach ($vars as $k => $varname)
  324. {
  325. @$linked_v[$varname] = $this->ar_variables[$varname];
  326. }
  327. }
  328. /* Search for childs */
  329. if (isset($this->ar_block_i[$blockname]['childs']) && is_array($this->ar_block_i[$blockname]['childs']))
  330. {
  331. foreach ($this->ar_block_i[$blockname]['childs'] as $k => $child)
  332. {
  333. $this->ar_block_v[$child][] = 'end';
  334. }
  335. }
  336. }
  337. /**
  338. * Function used inside PHP-code.
  339. * Start block. Used in while(_bStart('blockname'))
  340. */
  341. public function _bStart($blockname)
  342. {
  343. static $block;
  344. /* while the last element of compiled blocks is not the given block */
  345. if (@end($this->ar_block_c) != $blockname)
  346. {
  347. $this->ar_block_c[] = $blockname;
  348. }
  349. /* See parse_block() for 'end' */
  350. if (!(list($k, $this->ar_run[$blockname]) = @each($this->ar_block_v[$blockname])) ||
  351. $this->ar_run[$blockname] == 'end')
  352. {
  353. /* remove the last element from array */
  354. array_pop($this->ar_block_c);
  355. return false;
  356. }
  357. return true;
  358. }
  359. /**
  360. * Reserved function. Can be executed at the end of block.
  361. */
  362. public function _bEnd($blockname)
  363. {
  364. return true;
  365. }
  366. /**
  367. * Retrieves value from the variable.
  368. * $this->_v('varname') --> 123
  369. */
  370. public function _v($varname)
  371. {
  372. $str = '';
  373. /* search for the variable in block */
  374. if ( $cur_block = @end($this->ar_block_c) )
  375. {
  376. /* a dynamic block */
  377. $str =& $this->ar_run[$cur_block][$varname];
  378. }
  379. else
  380. {
  381. /* a static variable */
  382. $str =& $this->ar_variables[$varname];
  383. }
  384. /* Global variable overwrites a local. */
  385. if (isset($this->ar_variables_global[$varname]))
  386. {
  387. $str =& $this->ar_variables_global[$varname];
  388. }
  389. echo $str;
  390. }
  391. /**
  392. * Optimizes variable or block name. Used on parsing source HTML-code.
  393. */
  394. public static function _parse_var($varname)
  395. {
  396. #return $varname;
  397. return sprintf("%u", crc32($varname));
  398. }
  399. /**
  400. * Put contents into a file. Binary and fail safe.
  401. *
  402. * @param string $filename Full path to filename
  403. * @param string $contents File contents
  404. * @param string $mode [ w = write new file (default) | a = append ]
  405. * @return TRUE if success, FALSE otherwise
  406. */
  407. static public function file_put_contents($filename, $contents, $mode = "w")
  408. {
  409. /* Correct Windows path */
  410. $filename = str_replace('\\', '/', $filename);
  411. /* Write a new file */
  412. if (!file_exists($filename))
  413. {
  414. /* Check & create directories first */
  415. $ar_parts = explode('/', $filename);
  416. $int_parts = (sizeof($ar_parts) - 1);
  417. $folder = '';
  418. for ($i = 0; $i < $int_parts; $i++)
  419. {
  420. $folder .= $ar_parts[$i].'/';
  421. if (is_dir($folder))
  422. {
  423. continue;
  424. }
  425. else
  426. {
  427. $oldumask = umask(0);
  428. @mkdir($folder, 0777);
  429. @chmod($folder, 0777);
  430. umask($oldumask);
  431. }
  432. }
  433. /* Nothing to write */
  434. if ($contents == '')
  435. {
  436. return true;
  437. }
  438. /* Write to file */
  439. $fp = @fopen($filename, 'wb');
  440. @chmod($filename, 0777);
  441. if ($fp)
  442. {
  443. fputs($fp, $contents);
  444. }
  445. else
  446. {
  447. return false;
  448. }
  449. fclose($fp);
  450. }
  451. else
  452. {
  453. /* Append to file, binary mode is transparent */
  454. if ($fp = @fopen($filename, $mode.'b'))
  455. {
  456. $is_allow = flock($fp, 2); /* lock for writing & reading */
  457. if ($is_allow)
  458. {
  459. fputs($fp, $contents, strlen($contents));
  460. }
  461. flock($fp, 3); /* unlock */
  462. fclose($fp);
  463. }
  464. else
  465. {
  466. return false;
  467. }
  468. }
  469. return true;
  470. }
  471. /* Debug purposes */
  472. public function get_variables()
  473. {
  474. return array(
  475. 'ar_block_i' => $this->ar_block_i,
  476. 'ar_block_c' => $this->ar_block_c,
  477. 'ar_block_v' => $this->ar_block_v,
  478. 'ar_compiled' => $this->ar_compiled,
  479. 'ar_variables' => $this->ar_variables,
  480. 'ar_variables_global' => $this->ar_variables_global,
  481. );
  482. }
  483. }
  484. /**
  485. *
  486. */
  487. class site_class_templates_parser extends site_class_templates
  488. {
  489. /* */
  490. public function __construct()
  491. {
  492. $this->_reset();
  493. }
  494. /**
  495. *
  496. */
  497. public function _reset()
  498. {
  499. $this->ar_block_v = array();
  500. $this->ar_block_c = array();
  501. $this->ar_block_i = array();
  502. $this->ar_childs = array();
  503. $this->cur_level = 0;
  504. }
  505. /**
  506. * Construct iteration array.
  507. */
  508. public function make_iterations($is_delete = 1)
  509. {
  510. $ar_str = array();
  511. foreach ($this->ar_block_i as $block => $info)
  512. {
  513. if (isset($info['var']) && is_array($info['var']))
  514. {
  515. foreach ($info['var'] as $k => $v)
  516. {
  517. $ar_str[] = '$ar_block_i[\''.$block.'\'][\'var\'][] = \''.$v.'\';';
  518. }
  519. }
  520. if (isset($info['childs']) && is_array($info['childs']))
  521. {
  522. foreach ($info['childs'] as $k => $child)
  523. {
  524. $ar_str[] = '$ar_block_i[\''.$block.'\'][\'childs\'][] = \''.$child.'\';';
  525. }
  526. }
  527. }
  528. if ($is_delete)
  529. {
  530. $this->ar_blocks_c = $this->ar_block_i = array();
  531. }
  532. return implode("\n", $ar_str);
  533. }
  534. /**
  535. * Used to compile the variable defined in HTML-code into PHP-code.
  536. */
  537. public function _var($variable)
  538. {
  539. $variable = $this->_parse_var($variable);
  540. if ($cur_block = @end($this->ar_block_c))
  541. {
  542. $this->ar_block_i[$cur_block]['var'][] = $variable;
  543. }
  544. return '<'.'?php $this->_v(\''.$variable.'\');?>';
  545. }
  546. /**
  547. * Starts the block. Called on parsing source HTML-code.
  548. */
  549. public function start_block($block_name)
  550. {
  551. /* Optimize block name */
  552. $block_name = $this->_parse_var($block_name);
  553. if (@end($this->ar_block_c))
  554. {
  555. $this->ar_block_i[current($this->ar_block_c)]['childs'][] = $block_name;
  556. }
  557. else
  558. {
  559. $this->ar_block_i[$block_name]['childs'] = array();
  560. }
  561. $this->ar_block_c[] = $block_name;
  562. return '<'.'?php while ($this->_bStart(\''.$block_name.'\')) : ?>';
  563. }
  564. /**
  565. * Ends the block. Called on parsing source HTML-code.
  566. */
  567. public function end_block($block_name)
  568. {
  569. array_pop($this->ar_block_c);
  570. return '<'.'?php endwhile; ?'.'>';
  571. }
  572. }
  573. ?>