You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

smarty_internal_compile_section.php 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  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. * @throws \SmartyException
  79. */
  80. public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler)
  81. {
  82. $compiler->loopNesting ++;
  83. // check and get attributes
  84. $_attr = $this->getAttributes($compiler, $args);
  85. $attributes = array('name' => $compiler->getId($_attr[ 'name' ]));
  86. unset($_attr[ 'name' ]);
  87. foreach ($attributes as $a => $v) {
  88. if ($v === false) {
  89. $compiler->trigger_template_error("'{$a}' attribute/variable has illegal value", null, true);
  90. }
  91. }
  92. $local = "\$__section_{$attributes['name']}_" . $this->counter ++ . '_';
  93. $sectionVar = "\$_smarty_tpl->tpl_vars['__smarty_section_{$attributes['name']}']";
  94. $this->openTag($compiler, 'section', array('section', $compiler->nocache, $local, $sectionVar));
  95. // maybe nocache because of nocache variables
  96. $compiler->nocache = $compiler->nocache | $compiler->tag_nocache;
  97. $initLocal = array();
  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 ($t === 1) {
  133. $initLocal[ 'loop' ] = $v;
  134. $v = "{$local}loop";
  135. }
  136. break;
  137. case 'show':
  138. if (is_bool($attr_value)) {
  139. $v = $attr_value ? 'true' : 'false';
  140. $t = 0;
  141. } else {
  142. $v = "(bool) $attr_value";
  143. $t = 3;
  144. }
  145. break;
  146. case 'step':
  147. if (is_numeric($attr_value)) {
  148. $v = (int) $attr_value;
  149. $v = ($v === 0) ? 1 : $v;
  150. $t = 0;
  151. break;
  152. }
  153. $initLocal[ 'step' ] = "((int)@$attr_value) === 0 ? 1 : (int)@$attr_value";
  154. $v = "{$local}step";
  155. $t = 2;
  156. break;
  157. case 'max':
  158. case 'start':
  159. if (is_numeric($attr_value)) {
  160. $v = (int) $attr_value;
  161. $t = 0;
  162. break;
  163. }
  164. $v = "(int)@$attr_value";
  165. $t = 3;
  166. break;
  167. }
  168. if ($t === 3 && $compiler->getId($attr_value)) {
  169. $t = 1;
  170. }
  171. $propValue[ $attr_name ] = $v;
  172. $propType[ $attr_name ] = $t;
  173. }
  174. if (isset($namedAttr[ 'step' ])) {
  175. $initNamedProperty[ 'step' ] = $propValue[ 'step' ];
  176. }
  177. if (isset($namedAttr[ 'iteration' ])) {
  178. $propValue[ 'iteration' ] = "{$sectionVar}->value['iteration']";
  179. }
  180. $incFor[ 'iteration' ] = "{$propValue['iteration']}++";
  181. $initFor[ 'iteration' ] = "{$propValue['iteration']} = 1";
  182. if ($propType[ 'step' ] === 0) {
  183. if ($propValue[ 'step' ] === 1) {
  184. $incFor[ 'index' ] = "{$sectionVar}->value['index']++";
  185. } elseif ($propValue[ 'step' ] > 1) {
  186. $incFor[ 'index' ] = "{$sectionVar}->value['index'] += {$propValue['step']}";
  187. } else {
  188. $incFor[ 'index' ] = "{$sectionVar}->value['index'] -= " . - $propValue[ 'step' ];
  189. }
  190. } else {
  191. $incFor[ 'index' ] = "{$sectionVar}->value['index'] += {$propValue['step']}";
  192. }
  193. if (!isset($propValue[ 'max' ])) {
  194. $propValue[ 'max' ] = $propValue[ 'loop' ];
  195. $propType[ 'max' ] = $propType[ 'loop' ];
  196. } elseif ($propType[ 'max' ] !== 0) {
  197. $propValue[ 'max' ] = "{$propValue['max']} < 0 ? {$propValue['loop']} : {$propValue['max']}";
  198. $propType[ 'max' ] = 1;
  199. } else {
  200. if ($propValue[ 'max' ] < 0) {
  201. $propValue[ 'max' ] = $propValue[ 'loop' ];
  202. $propType[ 'max' ] = $propType[ 'loop' ];
  203. }
  204. }
  205. if (!isset($propValue[ 'start' ])) {
  206. $start_code =
  207. array(1 => "{$propValue['step']} > 0 ? ", 2 => '0', 3 => ' : ', 4 => $propValue[ 'loop' ], 5 => ' - 1');
  208. if ($propType[ 'loop' ] === 0) {
  209. $start_code[ 5 ] = '';
  210. $start_code[ 4 ] = $propValue[ 'loop' ] - 1;
  211. }
  212. if ($propType[ 'step' ] === 0) {
  213. if ($propValue[ 'step' ] > 0) {
  214. $start_code = array(1 => '0');
  215. $propType[ 'start' ] = 0;
  216. } else {
  217. $start_code[ 1 ] = $start_code[ 2 ] = $start_code[ 3 ] = '';
  218. $propType[ 'start' ] = $propType[ 'loop' ];
  219. }
  220. } else {
  221. $propType[ 'start' ] = 1;
  222. }
  223. $propValue[ 'start' ] = join('', $start_code);
  224. } else {
  225. $start_code =
  226. array(1 => "{$propValue['start']} < 0 ? ", 2 => 'max(', 3 => "{$propValue['step']} > 0 ? ", 4 => '0',
  227. 5 => ' : ', 6 => '-1', 7 => ', ', 8 => "{$propValue['start']} + {$propValue['loop']}", 10 => ')',
  228. 11 => ' : ', 12 => 'min(', 13 => $propValue[ 'start' ], 14 => ', ',
  229. 15 => "{$propValue['step']} > 0 ? ", 16 => $propValue[ 'loop' ], 17 => ' : ',
  230. 18 => $propType[ 'loop' ] === 0 ? $propValue[ 'loop' ] - 1 : "{$propValue['loop']} - 1",
  231. 19 => ')');
  232. if ($propType[ 'step' ] === 0) {
  233. $start_code[ 3 ] = $start_code[ 5 ] = $start_code[ 15 ] = $start_code[ 17 ] = '';
  234. if ($propValue[ 'step' ] > 0) {
  235. $start_code[ 6 ] = $start_code[ 18 ] = '';
  236. } else {
  237. $start_code[ 4 ] = $start_code[ 16 ] = '';
  238. }
  239. }
  240. if ($propType[ 'start' ] === 0) {
  241. if ($propType[ 'loop' ] === 0) {
  242. $start_code[ 8 ] = $propValue[ 'start' ] + $propValue[ 'loop' ];
  243. }
  244. $propType[ 'start' ] = $propType[ 'step' ] + $propType[ 'loop' ];
  245. $start_code[ 1 ] = '';
  246. if ($propValue[ 'start' ] < 0) {
  247. for ($i = 11; $i <= 19; $i ++) {
  248. $start_code[ $i ] = '';
  249. }
  250. if ($propType[ 'start' ] === 0) {
  251. $start_code = array(max($propValue[ 'step' ] > 0 ? 0 : - 1,
  252. $propValue[ 'start' ] + $propValue[ 'loop' ]));
  253. }
  254. } else {
  255. for ($i = 1; $i <= 11; $i ++) {
  256. $start_code[ $i ] = '';
  257. }
  258. if ($propType[ 'start' ] === 0) {
  259. $start_code =
  260. array(min($propValue[ 'step' ] > 0 ? $propValue[ 'loop' ] : $propValue[ 'loop' ] - 1,
  261. $propValue[ 'start' ]));
  262. }
  263. }
  264. }
  265. $propValue[ 'start' ] = join('', $start_code);
  266. }
  267. if ($propType[ 'start' ] !== 0) {
  268. $initLocal[ 'start' ] = $propValue[ 'start' ];
  269. $propValue[ 'start' ] = "{$local}start";
  270. }
  271. $initFor[ 'index' ] = "{$sectionVar}->value['index'] = {$propValue['start']}";
  272. if (!isset($_attr[ 'start' ]) && !isset($_attr[ 'step' ]) && !isset($_attr[ 'max' ])) {
  273. $propValue[ 'total' ] = $propValue[ 'loop' ];
  274. $propType[ 'total' ] = $propType[ 'loop' ];
  275. } else {
  276. $propType[ 'total' ] =
  277. $propType[ 'start' ] + $propType[ 'loop' ] + $propType[ 'step' ] + $propType[ 'max' ];
  278. if ($propType[ 'total' ] === 0) {
  279. $propValue[ 'total' ] =
  280. min(ceil(($propValue[ 'step' ] > 0 ? $propValue[ 'loop' ] - $propValue[ 'start' ] :
  281. (int) $propValue[ 'start' ] + 1) / abs($propValue[ 'step' ])), $propValue[ 'max' ]);
  282. } else {
  283. $total_code = array(1 => 'min(', 2 => 'ceil(', 3 => '(', 4 => "{$propValue['step']} > 0 ? ",
  284. 5 => $propValue[ 'loop' ], 6 => ' - ', 7 => $propValue[ 'start' ], 8 => ' : ',
  285. 9 => $propValue[ 'start' ], 10 => '+ 1', 11 => ')', 12 => '/ ', 13 => 'abs(',
  286. 14 => $propValue[ 'step' ], 15 => ')', 16 => ')', 17 => ", {$propValue['max']})",);
  287. if (!isset($propValue[ 'max' ])) {
  288. $total_code[ 1 ] = $total_code[ 17 ] = '';
  289. }
  290. if ($propType[ 'loop' ] + $propType[ 'start' ] === 0) {
  291. $total_code[ 5 ] = $propValue[ 'loop' ] - $propValue[ 'start' ];
  292. $total_code[ 6 ] = $total_code[ 7 ] = '';
  293. }
  294. if ($propType[ 'start' ] === 0) {
  295. $total_code[ 9 ] = (int) $propValue[ 'start' ] + 1;
  296. $total_code[ 10 ] = '';
  297. }
  298. if ($propType[ 'step' ] === 0) {
  299. $total_code[ 13 ] = $total_code[ 15 ] = '';
  300. if ($propValue[ 'step' ] === 1 || $propValue[ 'step' ] === - 1) {
  301. $total_code[ 2 ] = $total_code[ 12 ] = $total_code[ 14 ] = $total_code[ 16 ] = '';
  302. } elseif ($propValue[ 'step' ] < 0) {
  303. $total_code[ 14 ] = - $propValue[ 'step' ];
  304. }
  305. $total_code[ 4 ] = '';
  306. if ($propValue[ 'step' ] > 0) {
  307. $total_code[ 8 ] = $total_code[ 9 ] = $total_code[ 10 ] = '';
  308. } else {
  309. $total_code[ 5 ] = $total_code[ 6 ] = $total_code[ 7 ] = $total_code[ 8 ] = '';
  310. }
  311. }
  312. $propValue[ 'total' ] = join('', $total_code);
  313. }
  314. }
  315. if (isset($namedAttr[ 'loop' ])) {
  316. $initNamedProperty[ 'loop' ] = "'loop' => {$propValue['loop']}";
  317. }
  318. if (isset($namedAttr[ 'total' ])) {
  319. $initNamedProperty[ 'total' ] = "'total' => {$propValue['total']}";
  320. if ($propType[ 'total' ] > 0) {
  321. $propValue[ 'total' ] = "{$sectionVar}->value['total']";
  322. }
  323. } elseif ($propType[ 'total' ] > 0) {
  324. $initLocal[ 'total' ] = $propValue[ 'total' ];
  325. $propValue[ 'total' ] = "{$local}total";
  326. }
  327. $cmpFor[ 'iteration' ] = "{$propValue['iteration']} <= {$propValue['total']}";
  328. foreach ($initLocal as $key => $code) {
  329. $output .= "{$local}{$key} = {$code};\n";
  330. }
  331. $_vars = 'array(' . join(', ', $initNamedProperty) . ')';
  332. $output .= "{$sectionVar} = new Smarty_Variable({$_vars});\n";
  333. $cond_code = "{$propValue['total']} !== 0";
  334. if ($propType[ 'total' ] === 0) {
  335. if ($propValue[ 'total' ] === 0) {
  336. $cond_code = 'false';
  337. } else {
  338. $cond_code = 'true';
  339. }
  340. }
  341. if ($propType[ 'show' ] > 0) {
  342. $output .= "{$local}show = {$propValue['show']} ? {$cond_code} : false;\n";
  343. $output .= "if ({$local}show) {\n";
  344. } elseif ($propValue[ 'show' ] === 'true') {
  345. $output .= "if ({$cond_code}) {\n";
  346. } else {
  347. $output .= "if (false) {\n";
  348. }
  349. $jinit = join(', ', $initFor);
  350. $jcmp = join(', ', $cmpFor);
  351. $jinc = join(', ', $incFor);
  352. $output .= "for ({$jinit}; {$jcmp}; {$jinc}){\n";
  353. if (isset($namedAttr[ 'rownum' ])) {
  354. $output .= "{$sectionVar}->value['rownum'] = {$propValue['iteration']};\n";
  355. }
  356. if (isset($namedAttr[ 'index_prev' ])) {
  357. $output .= "{$sectionVar}->value['index_prev'] = {$propValue['index']} - {$propValue['step']};\n";
  358. }
  359. if (isset($namedAttr[ 'index_next' ])) {
  360. $output .= "{$sectionVar}->value['index_next'] = {$propValue['index']} + {$propValue['step']};\n";
  361. }
  362. if (isset($namedAttr[ 'first' ])) {
  363. $output .= "{$sectionVar}->value['first'] = ({$propValue['iteration']} === 1);\n";
  364. }
  365. if (isset($namedAttr[ 'last' ])) {
  366. $output .= "{$sectionVar}->value['last'] = ({$propValue['iteration']} === {$propValue['total']});\n";
  367. }
  368. $output .= '?>';
  369. return $output;
  370. }
  371. }
  372. /**
  373. * Smarty Internal Plugin Compile Sectionelse Class
  374. *
  375. * @package Smarty
  376. * @subpackage Compiler
  377. */
  378. class Smarty_Internal_Compile_Sectionelse extends Smarty_Internal_CompileBase
  379. {
  380. /**
  381. * Compiles code for the {sectionelse} tag
  382. *
  383. * @param array $args array with attributes from parser
  384. * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object
  385. *
  386. * @return string compiled code
  387. */
  388. public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler)
  389. {
  390. // check and get attributes
  391. $_attr = $this->getAttributes($compiler, $args);
  392. list($openTag, $nocache, $local, $sectionVar) = $this->closeTag($compiler, array('section'));
  393. $this->openTag($compiler, 'sectionelse', array('sectionelse', $nocache, $local, $sectionVar));
  394. return "<?php }} else {\n ?>";
  395. }
  396. }
  397. /**
  398. * Smarty Internal Plugin Compile Sectionclose Class
  399. *
  400. * @package Smarty
  401. * @subpackage Compiler
  402. */
  403. class Smarty_Internal_Compile_Sectionclose extends Smarty_Internal_CompileBase
  404. {
  405. /**
  406. * Compiles code for the {/section} tag
  407. *
  408. * @param array $args array with attributes from parser
  409. * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object
  410. *
  411. * @return string compiled code
  412. */
  413. public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler)
  414. {
  415. $compiler->loopNesting --;
  416. // must endblock be nocache?
  417. if ($compiler->nocache) {
  418. $compiler->tag_nocache = true;
  419. }
  420. list($openTag, $compiler->nocache, $local, $sectionVar) =
  421. $this->closeTag($compiler, array('section', 'sectionelse'));
  422. $output = "<?php\n";
  423. if ($openTag === 'sectionelse') {
  424. $output .= "}\n";
  425. } else {
  426. $output .= "}\n}\n";
  427. }
  428. $output .= '?>';
  429. return $output;
  430. }
  431. }