smarty_internal_compile_section.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. <?php
  2. /**
  3. * Smarty Internal Plugin Compile Section
  4. * Compiles the {section} {sectionelse} {/section} tags
  5. *
  6. * @package Smarty
  7. * @subpackage Compiler
  8. * @author Uwe Tews
  9. */
  10. /**
  11. * Smarty Internal Plugin Compile Section Class
  12. *
  13. * @package Smarty
  14. * @subpackage Compiler
  15. */
  16. class Smarty_Internal_Compile_Section extends Smarty_Internal_Compile_Private_ForeachSection
  17. {
  18. /**
  19. * Attribute definition: Overwrites base class.
  20. *
  21. * @var array
  22. * @see Smarty_Internal_CompileBase
  23. */
  24. public $required_attributes = array('name', 'loop');
  25. /**
  26. * Attribute definition: Overwrites base class.
  27. *
  28. * @var array
  29. * @see Smarty_Internal_CompileBase
  30. */
  31. public $shorttag_order = array('name', 'loop');
  32. /**
  33. * Attribute definition: Overwrites base class.
  34. *
  35. * @var array
  36. * @see Smarty_Internal_CompileBase
  37. */
  38. public $optional_attributes = array('start', 'step', 'max', 'show', 'properties');
  39. /**
  40. * counter
  41. *
  42. * @var int
  43. */
  44. public $counter = 0;
  45. /**
  46. * Name of this tag
  47. *
  48. * @var string
  49. */
  50. public $tagName = 'section';
  51. /**
  52. * Valid properties of $smarty.section.name.xxx variable
  53. *
  54. * @var array
  55. */
  56. public $nameProperties = array('first', 'last', 'index', 'iteration', 'show', 'total', 'rownum', 'index_prev',
  57. 'index_next', 'loop');
  58. /**
  59. * {section} tag has no item properties
  60. *
  61. * @var array
  62. */
  63. public $itemProperties = null;
  64. /**
  65. * {section} tag has always name attribute
  66. *
  67. * @var bool
  68. */
  69. public $isNamed = true;
  70. /**
  71. * Compiles code for the {section} tag
  72. *
  73. * @param array $args array with attributes from parser
  74. * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object
  75. *
  76. * @return string compiled code
  77. * @throws \SmartyCompilerException
  78. */
  79. public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler)
  80. {
  81. $compiler->loopNesting ++;
  82. // check and get attributes
  83. $_attr = $this->getAttributes($compiler, $args);
  84. $attributes = array('name' => $compiler->getId($_attr[ 'name' ]));
  85. unset($_attr[ 'name' ]);
  86. foreach ($attributes as $a => $v) {
  87. if ($v === false) {
  88. $compiler->trigger_template_error("'{$a}' attribute/variable has illegal value", null, true);
  89. }
  90. }
  91. $local = "\$__section_{$attributes['name']}_" . $this->counter ++ . '_';
  92. $sectionVar = "\$_smarty_tpl->tpl_vars['__smarty_section_{$attributes['name']}']";
  93. $this->openTag($compiler, 'section', array('section', $compiler->nocache, $local, $sectionVar));
  94. // maybe nocache because of nocache variables
  95. $compiler->nocache = $compiler->nocache | $compiler->tag_nocache;
  96. $initLocal =
  97. array('saved' => "isset(\$_smarty_tpl->tpl_vars['__smarty_section_{$attributes['name']}']) ? \$_smarty_tpl->tpl_vars['__smarty_section_{$attributes['name']}'] : false",);
  98. $initNamedProperty = array();
  99. $initFor = array();
  100. $incFor = array();
  101. $cmpFor = array();
  102. $propValue = array('index' => "{$sectionVar}->value['index']", 'show' => 'true', 'step' => 1,
  103. 'iteration' => "{$local}iteration",
  104. );
  105. $propType = array('index' => 2, 'iteration' => 2, 'show' => 0, 'step' => 0,);
  106. // search for used tag attributes
  107. $this->scanForProperties($attributes, $compiler);
  108. if (!empty($this->matchResults[ 'named' ])) {
  109. $namedAttr = $this->matchResults[ 'named' ];
  110. }
  111. if (isset($_attr[ 'properties' ]) && preg_match_all("/['](.*?)[']/", $_attr[ 'properties' ], $match)) {
  112. foreach ($match[ 1 ] as $prop) {
  113. if (in_array($prop, $this->nameProperties)) {
  114. $namedAttr[ $prop ] = true;
  115. } else {
  116. $compiler->trigger_template_error("Invalid property '{$prop}'", null, true);
  117. }
  118. }
  119. }
  120. $namedAttr[ 'index' ] = true;
  121. $output = "<?php\n";
  122. foreach ($_attr as $attr_name => $attr_value) {
  123. switch ($attr_name) {
  124. case 'loop':
  125. if (is_numeric($attr_value)) {
  126. $v = (int) $attr_value;
  127. $t = 0;
  128. } else {
  129. $v = "(is_array(@\$_loop=$attr_value) ? count(\$_loop) : max(0, (int) \$_loop))";
  130. $t = 1;
  131. }
  132. if (isset($namedAttr[ 'loop' ])) {
  133. $initNamedProperty[ 'loop' ] = "'loop' => {$v}";
  134. if ($t == 1) {
  135. $v = "{$sectionVar}->value['loop']";
  136. }
  137. } elseif ($t == 1) {
  138. $initLocal[ 'loop' ] = $v;
  139. $v = "{$local}loop";
  140. }
  141. break;
  142. case 'show':
  143. if (is_bool($attr_value)) {
  144. $v = $attr_value ? 'true' : 'false';
  145. $t = 0;
  146. } else {
  147. $v = "(bool) $attr_value";
  148. $t = 3;
  149. }
  150. break;
  151. case 'step':
  152. if (is_numeric($attr_value)) {
  153. $v = (int) $attr_value;
  154. $v = ($v == 0) ? 1 : $v;
  155. $t = 0;
  156. break;
  157. }
  158. $initLocal[ 'step' ] = "((int)@$attr_value) == 0 ? 1 : (int)@$attr_value";
  159. $v = "{$local}step";
  160. $t = 2;
  161. break;
  162. case 'max':
  163. case 'start':
  164. if (is_numeric($attr_value)) {
  165. $v = (int) $attr_value;
  166. $t = 0;
  167. break;
  168. }
  169. $v = "(int)@$attr_value";
  170. $t = 3;
  171. break;
  172. }
  173. if ($t == 3 && $compiler->getId($attr_value)) {
  174. $t = 1;
  175. }
  176. $propValue[ $attr_name ] = $v;
  177. $propType[ $attr_name ] = $t;
  178. }
  179. if (isset($namedAttr[ 'step' ])) {
  180. $initNamedProperty[ 'step' ] = $propValue[ 'step' ];
  181. }
  182. if (isset($namedAttr[ 'iteration' ])) {
  183. $propValue[ 'iteration' ] = "{$sectionVar}->value['iteration']";
  184. }
  185. $incFor[ 'iteration' ] = "{$propValue['iteration']}++";
  186. $initFor[ 'iteration' ] = "{$propValue['iteration']} = 1";
  187. if ($propType[ 'step' ] == 0) {
  188. if ($propValue[ 'step' ] == 1) {
  189. $incFor[ 'index' ] = "{$sectionVar}->value['index']++";
  190. } elseif ($propValue[ 'step' ] > 1) {
  191. $incFor[ 'index' ] = "{$sectionVar}->value['index'] += {$propValue['step']}";
  192. } else {
  193. $incFor[ 'index' ] = "{$sectionVar}->value['index'] -= " . - $propValue[ 'step' ];
  194. }
  195. } else {
  196. $incFor[ 'index' ] = "{$sectionVar}->value['index'] += {$propValue['step']}";
  197. }
  198. if (!isset($propValue[ 'max' ])) {
  199. $propValue[ 'max' ] = $propValue[ 'loop' ];
  200. $propType[ 'max' ] = $propType[ 'loop' ];
  201. } elseif ($propType[ 'max' ] != 0) {
  202. $propValue[ 'max' ] = "{$propValue['max']} < 0 ? {$propValue['loop']} : {$propValue['max']}";
  203. $propType[ 'max' ] = 1;
  204. } else {
  205. if ($propValue[ 'max' ] < 0) {
  206. $propValue[ 'max' ] = $propValue[ 'loop' ];
  207. $propType[ 'max' ] = $propType[ 'loop' ];
  208. }
  209. }
  210. if (!isset($propValue[ 'start' ])) {
  211. $start_code =
  212. array(1 => "{$propValue['step']} > 0 ? ", 2 => '0', 3 => ' : ', 4 => $propValue[ 'loop' ], 5 => ' - 1');
  213. if ($propType[ 'loop' ] == 0) {
  214. $start_code[ 5 ] = '';
  215. $start_code[ 4 ] = $propValue[ 'loop' ] - 1;
  216. }
  217. if ($propType[ 'step' ] == 0) {
  218. if ($propValue[ 'step' ] > 0) {
  219. $start_code = array(1 => '0');
  220. $propType[ 'start' ] = 0;
  221. } else {
  222. $start_code[ 1 ] = $start_code[ 2 ] = $start_code[ 3 ] = '';
  223. $propType[ 'start' ] = $propType[ 'loop' ];
  224. }
  225. } else {
  226. $propType[ 'start' ] = 1;
  227. }
  228. $propValue[ 'start' ] = join('', $start_code);
  229. } else {
  230. $start_code =
  231. array(1 => "{$propValue['start']} < 0 ? ", 2 => 'max(', 3 => "{$propValue['step']} > 0 ? ", 4 => '0',
  232. 5 => ' : ', 6 => '-1', 7 => ', ', 8 => "{$propValue['start']} + {$propValue['loop']}", 10 => ')',
  233. 11 => ' : ', 12 => 'min(', 13 => $propValue[ 'start' ], 14 => ', ',
  234. 15 => "{$propValue['step']} > 0 ? ", 16 => $propValue[ 'loop' ], 17 => ' : ',
  235. 18 => $propType[ 'loop' ] == 0 ? $propValue[ 'loop' ] - 1 : "{$propValue['loop']} - 1",
  236. 19 => ')');
  237. if ($propType[ 'step' ] == 0) {
  238. $start_code[ 3 ] = $start_code[ 5 ] = $start_code[ 15 ] = $start_code[ 17 ] = '';
  239. if ($propValue[ 'step' ] > 0) {
  240. $start_code[ 6 ] = $start_code[ 18 ] = '';
  241. } else {
  242. $start_code[ 4 ] = $start_code[ 16 ] = '';
  243. }
  244. }
  245. if ($propType[ 'start' ] == 0) {
  246. if ($propType[ 'loop' ] == 0) {
  247. $start_code[ 8 ] = $propValue[ 'start' ] + $propValue[ 'loop' ];
  248. }
  249. $propType[ 'start' ] = $propType[ 'step' ] + $propType[ 'loop' ];
  250. $start_code[ 1 ] = '';
  251. if ($propValue[ 'start' ] < 0) {
  252. for ($i = 11; $i <= 19; $i ++) {
  253. $start_code[ $i ] = '';
  254. }
  255. if ($propType[ 'start' ] == 0) {
  256. $start_code = array(max($propValue[ 'step' ] > 0 ? 0 : - 1,
  257. $propValue[ 'start' ] + $propValue[ 'loop' ]));
  258. }
  259. } else {
  260. for ($i = 1; $i <= 11; $i ++) {
  261. $start_code[ $i ] = '';
  262. }
  263. if ($propType[ 'start' ] == 0) {
  264. $start_code =
  265. array(min($propValue[ 'step' ] > 0 ? $propValue[ 'loop' ] : $propValue[ 'loop' ] - 1,
  266. $propValue[ 'start' ]));
  267. }
  268. }
  269. }
  270. $propValue[ 'start' ] = join('', $start_code);
  271. }
  272. if ($propType[ 'start' ] != 0) {
  273. $initLocal[ 'start' ] = $propValue[ 'start' ];
  274. $propValue[ 'start' ] = "{$local}start";
  275. }
  276. $initFor[ 'index' ] = "{$sectionVar}->value['index'] = {$propValue['start']}";
  277. if (!isset($_attr[ 'start' ]) && !isset($_attr[ 'step' ]) && !isset($_attr[ 'max' ])) {
  278. $propValue[ 'total' ] = $propValue[ 'loop' ];
  279. $propType[ 'total' ] = $propType[ 'loop' ];
  280. } else {
  281. $propType[ 'total' ] =
  282. $propType[ 'start' ] + $propType[ 'loop' ] + $propType[ 'step' ] + $propType[ 'max' ];
  283. if ($propType[ 'total' ] == 0) {
  284. $propValue[ 'total' ] =
  285. min(ceil(($propValue[ 'step' ] > 0 ? $propValue[ 'loop' ] - $propValue[ 'start' ] :
  286. (int) $propValue[ 'start' ] + 1) / abs($propValue[ 'step' ])), $propValue[ 'max' ]);
  287. } else {
  288. $total_code = array(1 => 'min(', 2 => 'ceil(', 3 => '(', 4 => "{$propValue['step']} > 0 ? ",
  289. 5 => $propValue[ 'loop' ], 6 => ' - ', 7 => $propValue[ 'start' ], 8 => ' : ',
  290. 9 => $propValue[ 'start' ], 10 => '+ 1', 11 => ')', 12 => '/ ', 13 => 'abs(',
  291. 14 => $propValue[ 'step' ], 15 => ')', 16 => ')', 17 => ", {$propValue['max']})",);
  292. if (!isset($propValue[ 'max' ])) {
  293. $total_code[ 1 ] = $total_code[ 17 ] = '';
  294. }
  295. if ($propType[ 'loop' ] + $propType[ 'start' ] == 0) {
  296. $total_code[ 5 ] = $propValue[ 'loop' ] - $propValue[ 'start' ];
  297. $total_code[ 6 ] = $total_code[ 7 ] = '';
  298. }
  299. if ($propType[ 'start' ] == 0) {
  300. $total_code[ 9 ] = (int) $propValue[ 'start' ] + 1;
  301. $total_code[ 10 ] = '';
  302. }
  303. if ($propType[ 'step' ] == 0) {
  304. $total_code[ 13 ] = $total_code[ 15 ] = '';
  305. if ($propValue[ 'step' ] == 1 || $propValue[ 'step' ] == - 1) {
  306. $total_code[ 2 ] = $total_code[ 12 ] = $total_code[ 14 ] = $total_code[ 16 ] = '';
  307. } elseif ($propValue[ 'step' ] < 0) {
  308. $total_code[ 14 ] = - $propValue[ 'step' ];
  309. }
  310. $total_code[ 4 ] = '';
  311. if ($propValue[ 'step' ] > 0) {
  312. $total_code[ 8 ] = $total_code[ 9 ] = $total_code[ 10 ] = '';
  313. } else {
  314. $total_code[ 5 ] = $total_code[ 6 ] = $total_code[ 7 ] = $total_code[ 8 ] = '';
  315. }
  316. }
  317. $propValue[ 'total' ] = join('', $total_code);
  318. }
  319. }
  320. if (isset($namedAttr[ 'loop' ])) {
  321. $initNamedProperty[ 'loop' ] = "'loop' => {$propValue['total']}";
  322. }
  323. if (isset($namedAttr[ 'total' ])) {
  324. $initNamedProperty[ 'total' ] = "'total' => {$propValue['total']}";
  325. if ($propType[ 'total' ] > 0) {
  326. $propValue[ 'total' ] = "{$sectionVar}->value['total']";
  327. }
  328. } elseif ($propType[ 'total' ] > 0) {
  329. $initLocal[ 'total' ] = $propValue[ 'total' ];
  330. $propValue[ 'total' ] = "{$local}total";
  331. }
  332. $cmpFor[ 'iteration' ] = "{$propValue['iteration']} <= {$propValue['total']}";
  333. foreach ($initLocal as $key => $code) {
  334. $output .= "{$local}{$key} = {$code};\n";
  335. }
  336. $_vars = 'array(' . join(', ', $initNamedProperty) . ')';
  337. $output .= "{$sectionVar} = new Smarty_Variable({$_vars});\n";
  338. $cond_code = "{$propValue['total']} != 0";
  339. if ($propType[ 'total' ] == 0) {
  340. if ($propValue[ 'total' ] == 0) {
  341. $cond_code = 'false';
  342. } else {
  343. $cond_code = 'true';
  344. }
  345. }
  346. if ($propType[ 'show' ] > 0) {
  347. $output .= "{$local}show = {$propValue['show']} ? {$cond_code} : false;\n";
  348. $output .= "if ({$local}show) {\n";
  349. } elseif ($propValue[ 'show' ] == 'true') {
  350. $output .= "if ({$cond_code}) {\n";
  351. } else {
  352. $output .= "if (false) {\n";
  353. }
  354. $jinit = join(', ', $initFor);
  355. $jcmp = join(', ', $cmpFor);
  356. $jinc = join(', ', $incFor);
  357. $output .= "for ({$jinit}; {$jcmp}; {$jinc}){\n";
  358. if (isset($namedAttr[ 'rownum' ])) {
  359. $output .= "{$sectionVar}->value['rownum'] = {$propValue['iteration']};\n";
  360. }
  361. if (isset($namedAttr[ 'index_prev' ])) {
  362. $output .= "{$sectionVar}->value['index_prev'] = {$propValue['index']} - {$propValue['step']};\n";
  363. }
  364. if (isset($namedAttr[ 'index_next' ])) {
  365. $output .= "{$sectionVar}->value['index_next'] = {$propValue['index']} + {$propValue['step']};\n";
  366. }
  367. if (isset($namedAttr[ 'first' ])) {
  368. $output .= "{$sectionVar}->value['first'] = ({$propValue['iteration']} == 1);\n";
  369. }
  370. if (isset($namedAttr[ 'last' ])) {
  371. $output .= "{$sectionVar}->value['last'] = ({$propValue['iteration']} == {$propValue['total']});\n";
  372. }
  373. $output .= "?>";
  374. return $output;
  375. }
  376. }
  377. /**
  378. * Smarty Internal Plugin Compile Sectionelse Class
  379. *
  380. * @package Smarty
  381. * @subpackage Compiler
  382. */
  383. class Smarty_Internal_Compile_Sectionelse extends Smarty_Internal_CompileBase
  384. {
  385. /**
  386. * Compiles code for the {sectionelse} tag
  387. *
  388. * @param array $args array with attributes from parser
  389. * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object
  390. *
  391. * @return string compiled code
  392. */
  393. public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler)
  394. {
  395. // check and get attributes
  396. $_attr = $this->getAttributes($compiler, $args);
  397. list($openTag, $nocache, $local, $sectionVar) = $this->closeTag($compiler, array('section'));
  398. $this->openTag($compiler, 'sectionelse', array('sectionelse', $nocache, $local, $sectionVar));
  399. return "<?php }} else {\n ?>";
  400. }
  401. }
  402. /**
  403. * Smarty Internal Plugin Compile Sectionclose Class
  404. *
  405. * @package Smarty
  406. * @subpackage Compiler
  407. */
  408. class Smarty_Internal_Compile_Sectionclose extends Smarty_Internal_CompileBase
  409. {
  410. /**
  411. * Compiles code for the {/section} tag
  412. *
  413. * @param array $args array with attributes from parser
  414. * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object
  415. *
  416. * @return string compiled code
  417. */
  418. public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler)
  419. {
  420. $compiler->loopNesting --;
  421. // must endblock be nocache?
  422. if ($compiler->nocache) {
  423. $compiler->tag_nocache = true;
  424. }
  425. list($openTag, $compiler->nocache, $local, $sectionVar) =
  426. $this->closeTag($compiler, array('section', 'sectionelse'));
  427. $output = "<?php\n";
  428. if ($openTag == 'sectionelse') {
  429. $output .= "}\n";
  430. } else {
  431. $output .= "}\n}\n";
  432. }
  433. $output .= "if ({$local}saved) {\n";
  434. $output .= "{$sectionVar} = {$local}saved;\n";
  435. $output .= "}\n";
  436. $output .= "?>";
  437. return $output;
  438. }
  439. }