PageRenderTime 24ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/veriler/f3/lib/template.php

https://gitlab.com/fbi/razyo-api-server
PHP | 467 lines | 345 code | 16 blank | 106 comment | 62 complexity | 181628ca0699c45303f31fc547a0f12a MD5 | raw file
  1. <?php
  2. /**
  3. Template engine for the PHP Fat-Free Framework
  4. The contents of this file are subject to the terms of the GNU General
  5. Public License Version 3.0. You may not use this file except in
  6. compliance with the license. Any of the license terms and conditions
  7. can be waived if you get permission from the copyright holder.
  8. Copyright (c) 2009-2011 F3::Factory
  9. Bong Cosca <bong.cosca@yahoo.com>
  10. @package Template
  11. @version 2.0.4
  12. **/
  13. //! Template engine
  14. class Template extends Base {
  15. //@{ Locale-specific error/exception messages
  16. const
  17. TEXT_Render='Template %s cannot be rendered';
  18. //@}
  19. /**
  20. Render template
  21. @return string
  22. @param $file string
  23. @param $mime string
  24. @param $globals boolean
  25. @public
  26. **/
  27. static function serve($file,$mime='text/html',$globals=TRUE) {
  28. $file=self::resolve($file);
  29. $found=FALSE;
  30. foreach (preg_split('/[\|;,]/',self::$vars['GUI'],0,
  31. PREG_SPLIT_NO_EMPTY) as $gui) {
  32. if (is_file($view=self::fixslashes($gui.$file))) {
  33. $found=TRUE;
  34. break;
  35. }
  36. }
  37. if (!$found) {
  38. trigger_error(sprintf(self::TEXT_Render,$file));
  39. return '';
  40. }
  41. if (PHP_SAPI!='cli' && !headers_sent())
  42. // Send HTTP header with appropriate character set
  43. header(self::HTTP_Content.': '.$mime.'; '.
  44. 'charset='.self::$vars['ENCODING']);
  45. $hash='tpl.'.self::hash($view);
  46. $cached=Cache::cached($hash);
  47. if ($cached && filemtime($view)<$cached) {
  48. if (self::$vars['CACHE'])
  49. // Retrieve PHP-compiled template from cache
  50. $text=Cache::get($hash);
  51. }
  52. else {
  53. // Parse raw template
  54. $doc=new F3markup($mime,$globals);
  55. $text=$doc->load(self::getfile($view));
  56. if (self::$vars['CACHE'] && $doc->cache)
  57. // Save PHP-compiled template to cache
  58. Cache::set($hash,$text);
  59. }
  60. // Render in a sandbox
  61. $instance=new F3instance;
  62. ob_start();
  63. if (ini_get('allow_url_fopen') && ini_get('allow_url_include'))
  64. // Stream wrap
  65. $instance->sandbox('data:text/plain,'.urlencode($text));
  66. else {
  67. // Save PHP-equivalent file in temporary folder
  68. if (!is_dir(self::$vars['TEMP']))
  69. self::mkdir(self::$vars['TEMP']);
  70. $temp=self::$vars['TEMP'].$_SERVER['SERVER_NAME'].'.'.$hash;
  71. if (!$cached || !is_file($temp) ||
  72. filemtime($temp)<Cache::cached($view)) {
  73. // Create semaphore
  74. $hash='sem.'.self::hash($view);
  75. $cached=Cache::cached($hash);
  76. while ($cached)
  77. // Locked by another process
  78. usleep(mt_rand(0,100));
  79. Cache::set($hash,TRUE);
  80. self::putfile($temp,$text);
  81. // Remove semaphore
  82. Cache::clear($hash);
  83. }
  84. $instance->sandbox($temp);
  85. }
  86. $out=ob_get_clean();
  87. unset($instance);
  88. return self::$vars['TIDY']?self::tidy($out):$out;
  89. }
  90. }
  91. //! Markup loader/parser/builder
  92. class F3markup extends Base {
  93. //@{ Locale-specific error/exception messages
  94. const
  95. TEXT_AttribMissing='Missing attribute: %s',
  96. TEXT_AttribInvalid='Invalid attribute: %s',
  97. TEXT_Global='Use of global variable %s is not allowed';
  98. //@}
  99. public
  100. //! MIME type
  101. $mime,
  102. //! Enable/disable PHP globals
  103. $globals=TRUE,
  104. //! Enable/disable template caching
  105. $cache=TRUE;
  106. private
  107. //! Parsed markup string
  108. $tree=array(),
  109. //! Symbol table for repeat/loop blocks
  110. $syms=array();
  111. /**
  112. Convert template expression to PHP code
  113. @return string
  114. @param $str string
  115. @param $echo boolean
  116. @public
  117. **/
  118. function expr($str,$echo=FALSE) {
  119. $self=$this;
  120. $syms=&$this->syms;
  121. return preg_replace_callback(
  122. '/{{(.+?)}}/s',
  123. function($expr) use(&$syms,$self,$echo) {
  124. $out=preg_replace_callback('/(?!\w)'.
  125. '@(\w+(?:\[[^@\]]+\]|\.\w+(?![\w\(]))*'.
  126. '(?:\s*->\s*\w+)?)(\s*\([^\)]*\))?(?:\s*\\\(.+))?/',
  127. function($var) use(&$syms,$self) {
  128. //Return stringified framework variable
  129. preg_match('/^(\w+)\b(.*)/',$var[1],$match);
  130. if (!$self->globals &&
  131. preg_match('/'.$self::PHP_Globals.'/',
  132. $match[1])) {
  133. trigger_error(
  134. sprintf($self::TEXT_Global,$match[1])
  135. );
  136. return FALSE;
  137. }
  138. $isfunc=isset($var[2]) && $var[2];
  139. if (in_array($match[1],$syms))
  140. return '$_'.$self::remix($var[1]).
  141. ($isfunc?$var[2]:'');
  142. $str='F3::get('.var_export($var[1],TRUE).')';
  143. if ($isfunc)
  144. $str='call_user_func_array('.$str.','.
  145. 'array'.$var[2].')';
  146. elseif (isset($var[3]))
  147. $str=str_replace(')',
  148. ',array('.$var[3].'))',$str);
  149. if (!$match[2] &&
  150. !preg_match('/('.$self::PHP_Globals.')\b/',
  151. $match[1])) {
  152. $syms[]=$match[1];
  153. $str='($_'.$match[1].'='.$str.')';
  154. }
  155. return trim($str);
  156. },
  157. $expr[1]
  158. );
  159. return $echo?('<?php echo '.trim($out).'; ?>'):$out;
  160. },
  161. $str
  162. );
  163. }
  164. /**
  165. Return TRUE if all mandatory attributes are present
  166. @return boolean
  167. @param $key string
  168. @param $tags array
  169. @param $attrs array
  170. @public
  171. **/
  172. function isdef($key,array $tags,array $attrs) {
  173. $ok=TRUE;
  174. foreach ($attrs as $attr)
  175. if (!isset($tags['@attrib'][$attr])) {
  176. $ok=FALSE;
  177. break;
  178. }
  179. if ($ok)
  180. return TRUE;
  181. $out='<'.$key;
  182. if (isset($tags['@attrib']))
  183. foreach ($tags['@attrib'] as $akey=>$aval)
  184. $out.=' '.$akey.'="'.$aval.'"';
  185. $out.='>';
  186. trigger_error(sprintf(self::TEXT_AttribMissing,$out));
  187. return FALSE;
  188. }
  189. /**
  190. Reassemble markup string and insert appropriate PHP code
  191. @return string
  192. @param $node mixed
  193. @public
  194. **/
  195. function build($node) {
  196. $out='';
  197. if (is_array($node)) {
  198. foreach ($node as $nkey=>$nval)
  199. if (is_int($nkey))
  200. $out.=$this->build($nval);
  201. else {
  202. $count=count($this->syms);
  203. switch ($nkey) {
  204. case 'include':
  205. // <include> directive
  206. if (!$this->isdef($nkey,$nval,array('href')))
  207. return;
  208. $hvar=$nval['@attrib']['href'];
  209. if (isset($nval['@attrib']['if'])) {
  210. $ival=$nval['@attrib']['if'];
  211. $cond=$this->expr($ival);
  212. // Syntax check
  213. if ($cond==$ival) {
  214. trigger_error(sprintf(
  215. self::TEXT_AttribInvalid,
  216. 'if="'.addcslashes($ival,'"').'"'));
  217. return;
  218. }
  219. }
  220. $doc=new F3markup($this->mime,$this->globals);
  221. $file=self::resolve($hvar);
  222. if ($hvar!=$file)
  223. $this->cache=FALSE;
  224. $file=self::$vars['GUI'].$file;
  225. if (is_file($file)) {
  226. $text=$doc->load(
  227. self::getfile($file),$this->syms
  228. );
  229. $out.=isset($ival)?
  230. ('<?php if ('.trim($cond).'): ?>'.
  231. $text.'<?php endif; ?>'):$text;
  232. }
  233. break;
  234. case 'loop':
  235. // <loop> directive
  236. if (!$this->isdef($nkey,$nval,
  237. array('counter','from','to')))
  238. return;
  239. $cvar=self::remix(
  240. preg_replace('/{{\s*@(.+?)\s*}}/','\1',
  241. $nval['@attrib']['counter']));
  242. foreach ($nval['@attrib'] as $akey=>$aval) {
  243. ${$akey[0].'att'}=$aval;
  244. ${$akey[0].'str'}=$this->expr($aval);
  245. // Syntax check
  246. if (${$akey[0].'str'}==$aval) {
  247. trigger_error(sprintf(
  248. self::TEXT_AttribInvalid,$akey.'="'.
  249. addcslashes($aval,'"').'"'));
  250. return;
  251. }
  252. }
  253. unset($nval['@attrib']);
  254. $this->syms[]=$cvar;
  255. $out.='<?php for ('.
  256. '$_'.$cvar.'='.$fstr.';'.
  257. '$_'.$cvar.'<='.$tstr.';'.
  258. '$_'.$cvar.'+='.
  259. // step attribute
  260. (isset($satt)?$sstr:'1').'): ?>'.
  261. $this->build($nval).
  262. '<?php endfor; ?>';
  263. break;
  264. case 'repeat':
  265. // <repeat> directive
  266. if (!$this->isdef($nkey,$nval,array('group')) &&
  267. (!$this->isdef($nkey,$nval,array('key')) ||
  268. !$this->isdef($nkey,$nval,array('value'))))
  269. return;
  270. $gval=$nval['@attrib']['group'];
  271. $gstr=$this->expr($gval);
  272. // Syntax check
  273. if ($gstr==$gval) {
  274. trigger_error(sprintf(
  275. self::TEXT_AttribInvalid,
  276. 'group="'.addcslashes($gval,'"').'"'));
  277. return;
  278. }
  279. foreach ($nval['@attrib'] as $akey=>$aval) {
  280. ${$akey[0].'var'}=self::remix(
  281. preg_replace('/{{\s*@(.+?)\s*}}/',
  282. '\1',$aval));
  283. // Syntax check
  284. if (${$akey[0].'var'}==$aval) {
  285. trigger_error(sprintf(
  286. self::TEXT_AttribInvalid,
  287. $akey.'='.
  288. '"'.addcslashes($aval,'"').'"'));
  289. return;
  290. }
  291. }
  292. unset($nval['@attrib']);
  293. if (isset($vvar))
  294. $this->syms[]=$vvar;
  295. else
  296. $vvar=self::hash($gvar);
  297. if (isset($kvar))
  298. $this->syms[]=$kvar;
  299. if (isset($cvar))
  300. $this->syms[]=$cvar;
  301. $out.='<?php '.
  302. (isset($cvar)?('$_'.$cvar.'=0; '):'').
  303. 'foreach (('.trim($gstr).'?:array()) as '.
  304. (isset($kvar)?('$_'.$kvar.'=>'):'').
  305. '$_'.$vvar.'): '.
  306. (isset($cvar)?('$_'.$cvar.'++; '):'').'?>'.
  307. $this->build($nval).
  308. '<?php endforeach; ?>';
  309. break;
  310. case 'check':
  311. // <check> directive
  312. if (!$this->isdef($nkey,$nval,array('if')))
  313. return;
  314. $ival=$nval['@attrib']['if'];
  315. $cond=$this->expr($ival);
  316. // Syntax check
  317. if ($cond==$ival) {
  318. trigger_error(sprintf(
  319. self::TEXT_AttribInvalid,
  320. 'if="'.addcslashes($ival,'"').'"'));
  321. return;
  322. }
  323. // Is <true> is defined ahead of <false>?
  324. foreach ($nval as $pos=>$blk)
  325. if (is_array($blk))
  326. foreach ($blk as $ckey=>$cval)
  327. if (preg_match('/(?:F3:)?'.
  328. '(?:true|false)/i',$ckey))
  329. ${$ckey[0].'block'}=
  330. array($pos,$blk);
  331. if (isset($tblock) && isset($fblock) &&
  332. $tblock[0]>$fblock[0])
  333. // Swap <true> and <false> blocks
  334. // <false> is defined ahead of <true>
  335. list($nval[$tblock[0]],$nval[$fblock[0]])=
  336. array($fblock[1],$tblock[1]);
  337. $out.='<?php if ('.trim($cond).'): ?>'.
  338. $this->build($nval).
  339. '<?php endif; ?>';
  340. break;
  341. case 'true':
  342. // <true> block of <check> directive
  343. $out.=$this->build($nval);
  344. break;
  345. case 'false':
  346. // <false> block of <check> directive
  347. $out.='<?php else: ?>'.$this->build($nval);
  348. break;
  349. }
  350. // Reset scope
  351. $this->syms=array_slice($this->syms,0,$count);
  352. }
  353. }
  354. else
  355. $out.=preg_match('/<\?php/',$node)?$node:$this->expr($node,TRUE);
  356. return $out;
  357. }
  358. /**
  359. Load markup from string
  360. @return string
  361. @param $text string
  362. @param $syms array
  363. @public
  364. **/
  365. function load($text,array $syms=NULL) {
  366. if ($syms)
  367. $this->syms=$syms;
  368. // Remove PHP code and alternative exclude-tokens
  369. $text=preg_replace(
  370. '/<\?(?:php)?.+?\?>|{{\*.+?\*}}/is','',trim($text));
  371. // Define root node
  372. $node=&$this->tree;
  373. // Define stack and depth variables
  374. $stack=array();
  375. $depth=0;
  376. // Define string parser variables
  377. $len=strlen($text);
  378. $ptr=0;
  379. $temp='';
  380. while ($ptr<$len)
  381. if (preg_match('/^<(\/?)'.
  382. '(?:F3:)?(include|exclude|loop|repeat|check|true|false)\b'.
  383. '((?:\s+\w+s*=\s*(?:"(?:.+?)"|\'(?:.+?)\'))*)\s*(\/?)>/is',
  384. substr($text,$ptr),$match)) {
  385. if (strlen($temp))
  386. $node[]=$temp;
  387. // Element node
  388. if ($match[1]) {
  389. // Find matching start tag
  390. $save=$depth;
  391. $found=FALSE;
  392. while ($depth>0) {
  393. $depth--;
  394. foreach ($stack[$depth] as $item)
  395. if (is_array($item) && isset($item[$match[2]])) {
  396. // Start tag found
  397. $found=TRUE;
  398. break 2;
  399. }
  400. }
  401. if (!$found)
  402. // Unbalanced tag
  403. $depth=$save;
  404. $node=&$stack[$depth];
  405. }
  406. else {
  407. // Start tag
  408. $stack[$depth]=&$node;
  409. $node=&$node[][$match[2]];
  410. if ($match[3]) {
  411. // Process attributes
  412. preg_match_all('/\s+(\w+)\s*=\s*'.
  413. '(?:"(.+?)"|\'(.+?)\')/s',$match[3],$attr,
  414. PREG_SET_ORDER);
  415. foreach ($attr as $kv)
  416. $node['@attrib'][$kv[1]]=$kv[2]?:$kv[3];
  417. }
  418. if ($match[4])
  419. // Empty tag
  420. $node=&$stack[$depth];
  421. else
  422. $depth++;
  423. }
  424. $temp='';
  425. $ptr+=strlen($match[0]);
  426. }
  427. else {
  428. // Text node
  429. $temp.=$text[$ptr];
  430. $ptr++;
  431. }
  432. if (strlen($temp))
  433. $node[]=$temp;
  434. unset($node);
  435. unset($stack);
  436. return $this->build($this->tree);
  437. }
  438. /**
  439. Override base constructor
  440. @param $mime string
  441. @param $globals boolean
  442. @public
  443. **/
  444. function __construct($mime,$globals) {
  445. $this->mime=$mime;
  446. $this->globals=$globals;
  447. }
  448. }