InheritanceTest.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. <?php
  2. /*
  3. * This file is part of Mustache.php.
  4. *
  5. * (c) 2010-2017 Justin Hileman
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. /**
  11. * @group inheritance
  12. * @group functional
  13. */
  14. class Mustache_Test_Functional_InheritanceTest extends PHPUnit_Framework_TestCase
  15. {
  16. private $mustache;
  17. public function setUp()
  18. {
  19. $this->mustache = new Mustache_Engine(array(
  20. 'pragmas' => array(Mustache_Engine::PRAGMA_BLOCKS),
  21. ));
  22. }
  23. public function getIllegalInheritanceExamples()
  24. {
  25. return array(
  26. array(
  27. array(
  28. 'foo' => '{{$baz}}default content{{/baz}}',
  29. ),
  30. array(
  31. 'bar' => 'set by user',
  32. ),
  33. '{{< foo }}{{# bar }}{{$ baz }}{{/ baz }}{{/ bar }}{{/ foo }}',
  34. ),
  35. array(
  36. array(
  37. 'foo' => '{{$baz}}default content{{/baz}}',
  38. ),
  39. array(
  40. ),
  41. '{{<foo}}{{^bar}}{{$baz}}set by template{{/baz}}{{/bar}}{{/foo}}',
  42. ),
  43. array(
  44. array(
  45. 'foo' => '{{$baz}}default content{{/baz}}',
  46. 'qux' => 'I am a partial',
  47. ),
  48. array(
  49. ),
  50. '{{<foo}}{{>qux}}{{$baz}}set by template{{/baz}}{{/foo}}',
  51. ),
  52. array(
  53. array(
  54. 'foo' => '{{$baz}}default content{{/baz}}',
  55. ),
  56. array(),
  57. '{{<foo}}{{=<% %>=}}<%={{ }}=%>{{/foo}}',
  58. ),
  59. );
  60. }
  61. public function getLegalInheritanceExamples()
  62. {
  63. return array(
  64. array(
  65. array(
  66. 'foo' => '{{$baz}}default content{{/baz}}',
  67. ),
  68. array(
  69. 'bar' => 'set by user',
  70. ),
  71. '{{<foo}}{{bar}}{{$baz}}override{{/baz}}{{/foo}}',
  72. 'override',
  73. ),
  74. array(
  75. array(
  76. 'foo' => '{{$baz}}default content{{/baz}}',
  77. ),
  78. array(
  79. ),
  80. '{{<foo}}{{! ignore me }}{{$baz}}set by template{{/baz}}{{/foo}}',
  81. 'set by template',
  82. ),
  83. array(
  84. array(
  85. 'foo' => '{{$baz}}defualt content{{/baz}}',
  86. ),
  87. array(),
  88. '{{<foo}}set by template{{$baz}}also set by template{{/baz}}{{/foo}}',
  89. 'also set by template',
  90. ),
  91. array(
  92. array(
  93. 'foo' => '{{$a}}FAIL!{{/a}}',
  94. 'bar' => 'WIN!!',
  95. ),
  96. array(),
  97. '{{<foo}}{{$a}}{{<bar}}FAIL{{/bar}}{{/a}}{{/foo}}',
  98. 'WIN!!',
  99. ),
  100. );
  101. }
  102. public function testDefaultContent()
  103. {
  104. $tpl = $this->mustache->loadTemplate('{{$title}}Default title{{/title}}');
  105. $data = array();
  106. $this->assertEquals('Default title', $tpl->render($data));
  107. }
  108. public function testDefaultContentRendersVariables()
  109. {
  110. $tpl = $this->mustache->loadTemplate('{{$foo}}default {{bar}} content{{/foo}}');
  111. $data = array(
  112. 'bar' => 'baz',
  113. );
  114. $this->assertEquals('default baz content', $tpl->render($data));
  115. }
  116. public function testDefaultContentRendersTripleMustacheVariables()
  117. {
  118. $tpl = $this->mustache->loadTemplate('{{$foo}}default {{{bar}}} content{{/foo}}');
  119. $data = array(
  120. 'bar' => '<baz>',
  121. );
  122. $this->assertEquals('default <baz> content', $tpl->render($data));
  123. }
  124. public function testDefaultContentRendersSections()
  125. {
  126. $tpl = $this->mustache->loadTemplate(
  127. '{{$foo}}default {{#bar}}{{baz}}{{/bar}} content{{/foo}}'
  128. );
  129. $data = array(
  130. 'bar' => array('baz' => 'qux'),
  131. );
  132. $this->assertEquals('default qux content', $tpl->render($data));
  133. }
  134. public function testDefaultContentRendersNegativeSections()
  135. {
  136. $tpl = $this->mustache->loadTemplate(
  137. '{{$foo}}default {{^bar}}{{baz}}{{/bar}} content{{/foo}}'
  138. );
  139. $data = array(
  140. 'foo' => array('bar' => 'qux'),
  141. 'baz' => 'three',
  142. );
  143. $this->assertEquals('default three content', $tpl->render($data));
  144. }
  145. public function testMustacheInjectionInDefaultContent()
  146. {
  147. $tpl = $this->mustache->loadTemplate(
  148. '{{$foo}}default {{#bar}}{{baz}}{{/bar}} content{{/foo}}'
  149. );
  150. $data = array(
  151. 'bar' => array('baz' => '{{qux}}'),
  152. );
  153. $this->assertEquals('default {{qux}} content', $tpl->render($data));
  154. }
  155. public function testDefaultContentRenderedInsideIncludedTemplates()
  156. {
  157. $partials = array(
  158. 'include' => '{{$foo}}default content{{/foo}}',
  159. );
  160. $this->mustache->setPartials($partials);
  161. $tpl = $this->mustache->loadTemplate(
  162. '{{<include}}{{/include}}'
  163. );
  164. $data = array();
  165. $this->assertEquals('default content', $tpl->render($data));
  166. }
  167. public function testOverriddenContent()
  168. {
  169. $partials = array(
  170. 'super' => '...{{$title}}Default title{{/title}}...',
  171. );
  172. $this->mustache->setPartials($partials);
  173. $tpl = $this->mustache->loadTemplate(
  174. '{{<super}}{{$title}}sub template title{{/title}}{{/super}}'
  175. );
  176. $data = array();
  177. $this->assertEquals('...sub template title...', $tpl->render($data));
  178. }
  179. public function testOverriddenPartial()
  180. {
  181. $partials = array(
  182. 'partial' => '|{{$stuff}}...{{/stuff}}{{$default}} default{{/default}}|',
  183. );
  184. $this->mustache->setPartials($partials);
  185. $tpl = $this->mustache->loadTemplate(
  186. 'test {{<partial}}{{$stuff}}override1{{/stuff}}{{/partial}} {{<partial}}{{$stuff}}override2{{/stuff}}{{/partial}}'
  187. );
  188. $data = array();
  189. $this->assertEquals('test |override1 default| |override2 default|', $tpl->render($data));
  190. }
  191. public function testBlocksDoNotLeakBetweenPartials()
  192. {
  193. $partials = array(
  194. 'partial' => '|{{$a}}A{{/a}} {{$b}}B{{/b}}|',
  195. );
  196. $this->mustache->setPartials($partials);
  197. $tpl = $this->mustache->loadTemplate(
  198. 'test {{<partial}}{{$a}}C{{/a}}{{/partial}} {{<partial}}{{$b}}D{{/b}}{{/partial}}'
  199. );
  200. $data = array();
  201. $this->assertEquals('test |C B| |A D|', $tpl->render($data));
  202. }
  203. public function testDataDoesNotOverrideBlock()
  204. {
  205. $partials = array(
  206. 'include' => '{{$var}}var in include{{/var}}',
  207. );
  208. $this->mustache->setPartials($partials);
  209. $tpl = $this->mustache->loadTemplate(
  210. '{{<include}}{{$var}}var in template{{/var}}{{/include}}'
  211. );
  212. $data = array(
  213. 'var' => 'var in data',
  214. );
  215. $this->assertEquals('var in template', $tpl->render($data));
  216. }
  217. public function testDataDoesNotOverrideDefaultBlockValue()
  218. {
  219. $partials = array(
  220. 'include' => '{{$var}}var in include{{/var}}',
  221. );
  222. $this->mustache->setPartials($partials);
  223. $tpl = $this->mustache->loadTemplate(
  224. '{{<include}}{{/include}}'
  225. );
  226. $data = array(
  227. 'var' => 'var in data',
  228. );
  229. $this->assertEquals('var in include', $tpl->render($data));
  230. }
  231. public function testOverridePartialWithNewlines()
  232. {
  233. $partials = array(
  234. 'partial' => '{{$ballmer}}peaking{{/ballmer}}',
  235. );
  236. $this->mustache->setPartials($partials);
  237. $tpl = $this->mustache->loadTemplate(
  238. "{{<partial}}{{\$ballmer}}\npeaked\n\n:(\n{{/ballmer}}{{/partial}}"
  239. );
  240. $data = array();
  241. $this->assertEquals("peaked\n\n:(\n", $tpl->render($data));
  242. }
  243. public function testInheritIndentationWhenOverridingAPartial()
  244. {
  245. $partials = array(
  246. 'partial' => 'stop:
  247. {{$nineties}}collaborate and listen{{/nineties}}',
  248. );
  249. $this->mustache->setPartials($partials);
  250. $tpl = $this->mustache->loadTemplate(
  251. '{{<partial}}{{$nineties}}hammer time{{/nineties}}{{/partial}}'
  252. );
  253. $data = array();
  254. $this->assertEquals(
  255. 'stop:
  256. hammer time',
  257. $tpl->render($data)
  258. );
  259. }
  260. public function testInheritSpacingWhenOverridingAPartial()
  261. {
  262. $partials = array(
  263. 'parent' => 'collaborate_and{{$id}}{{/id}}',
  264. 'child' => '{{<parent}}{{$id}}_listen{{/id}}{{/parent}}',
  265. );
  266. $this->mustache->setPartials($partials);
  267. $tpl = $this->mustache->loadTemplate(
  268. 'stop:
  269. {{>child}}'
  270. );
  271. $data = array();
  272. $this->assertEquals(
  273. 'stop:
  274. collaborate_and_listen',
  275. $tpl->render($data)
  276. );
  277. }
  278. public function testOverrideOneSubstitutionButNotTheOther()
  279. {
  280. $partials = array(
  281. 'partial' => '{{$stuff}}default one{{/stuff}}, {{$stuff2}}default two{{/stuff2}}',
  282. );
  283. $this->mustache->setPartials($partials);
  284. $tpl = $this->mustache->loadTemplate(
  285. '{{<partial}}{{$stuff2}}override two{{/stuff2}}{{/partial}}'
  286. );
  287. $data = array();
  288. $this->assertEquals('default one, override two', $tpl->render($data));
  289. }
  290. public function testSuperTemplatesWithNoParameters()
  291. {
  292. $partials = array(
  293. 'include' => '{{$foo}}default content{{/foo}}',
  294. );
  295. $this->mustache->setPartials($partials);
  296. $tpl = $this->mustache->loadTemplate(
  297. '{{>include}}|{{<include}}{{/include}}'
  298. );
  299. $data = array();
  300. $this->assertEquals('default content|default content', $tpl->render($data));
  301. }
  302. public function testRecursionInInheritedTemplates()
  303. {
  304. $partials = array(
  305. 'include' => '{{$foo}}default content{{/foo}} {{$bar}}{{<include2}}{{/include2}}{{/bar}}',
  306. 'include2' => '{{$foo}}include2 default content{{/foo}} {{<include}}{{$bar}}don\'t recurse{{/bar}}{{/include}}',
  307. );
  308. $this->mustache->setPartials($partials);
  309. $tpl = $this->mustache->loadTemplate(
  310. '{{<include}}{{$foo}}override{{/foo}}{{/include}}'
  311. );
  312. $data = array();
  313. $this->assertEquals('override override override don\'t recurse', $tpl->render($data));
  314. }
  315. public function testTopLevelSubstitutionsTakePrecedenceInMultilevelInheritance()
  316. {
  317. $partials = array(
  318. 'parent' => '{{<older}}{{$a}}p{{/a}}{{/older}}',
  319. 'older' => '{{<grandParent}}{{$a}}o{{/a}}{{/grandParent}}',
  320. 'grandParent' => '{{$a}}g{{/a}}',
  321. );
  322. $this->mustache->setPartials($partials);
  323. $tpl = $this->mustache->loadTemplate(
  324. '{{<parent}}{{$a}}c{{/a}}{{/parent}}'
  325. );
  326. $data = array();
  327. $this->assertEquals('c', $tpl->render($data));
  328. }
  329. public function testMultiLevelInheritanceNoSubChild()
  330. {
  331. $partials = array(
  332. 'parent' => '{{<older}}{{$a}}p{{/a}}{{/older}}',
  333. 'older' => '{{<grandParent}}{{$a}}o{{/a}}{{/grandParent}}',
  334. 'grandParent' => '{{$a}}g{{/a}}',
  335. );
  336. $this->mustache->setPartials($partials);
  337. $tpl = $this->mustache->loadTemplate(
  338. '{{<parent}}{{/parent}}'
  339. );
  340. $data = array();
  341. $this->assertEquals('p', $tpl->render($data));
  342. }
  343. public function testIgnoreTextInsideSuperTemplatesButParseArgs()
  344. {
  345. $partials = array(
  346. 'include' => '{{$foo}}default content{{/foo}}',
  347. );
  348. $this->mustache->setPartials($partials);
  349. $tpl = $this->mustache->loadTemplate(
  350. '{{<include}} asdfasd {{$foo}}hmm{{/foo}} asdfasdfasdf {{/include}}'
  351. );
  352. $data = array();
  353. $this->assertEquals('hmm', $tpl->render($data));
  354. }
  355. public function testIgnoreTextInsideSuperTemplates()
  356. {
  357. $partials = array(
  358. 'include' => '{{$foo}}default content{{/foo}}',
  359. );
  360. $this->mustache->setPartials($partials);
  361. $tpl = $this->mustache->loadTemplate(
  362. '{{<include}} asdfasd asdfasdfasdf {{/include}}'
  363. );
  364. $data = array();
  365. $this->assertEquals('default content', $tpl->render($data));
  366. }
  367. public function testInheritanceWithLazyEvaluation()
  368. {
  369. $partials = array(
  370. 'parent' => '{{#items}}{{$value}}ignored{{/value}}{{/items}}',
  371. );
  372. $this->mustache->setPartials($partials);
  373. $tpl = $this->mustache->loadTemplate(
  374. '{{<parent}}{{$value}}<{{ . }}>{{/value}}{{/parent}}'
  375. );
  376. $data = array('items' => array(1, 2, 3));
  377. $this->assertEquals('<1><2><3>', $tpl->render($data));
  378. }
  379. public function testInheritanceWithLazyEvaluationWhitespaceIgnored()
  380. {
  381. $partials = array(
  382. 'parent' => '{{#items}}{{$value}}\n\nignored\n\n{{/value}}{{/items}}',
  383. );
  384. $this->mustache->setPartials($partials);
  385. $tpl = $this->mustache->loadTemplate(
  386. '{{<parent}}\n\n\n{{$value}}<{{ . }}>{{/value}}\n\n{{/parent}}'
  387. );
  388. $data = array('items' => array(1, 2, 3));
  389. $this->assertEquals('<1><2><3>', $tpl->render($data));
  390. }
  391. public function testInheritanceWithLazyEvaluationAndSections()
  392. {
  393. $partials = array(
  394. 'parent' => '{{#items}}{{$value}}\n\nignored {{.}} {{#more}} there is more {{/more}}\n\n{{/value}}{{/items}}',
  395. );
  396. $this->mustache->setPartials($partials);
  397. $tpl = $this->mustache->loadTemplate(
  398. '{{<parent}}\n\n\n{{$value}}<{{ . }}>{{#more}} there is less {{/more}}{{/value}}\n\n{{/parent}}'
  399. );
  400. $data = array('items' => array(1, 2, 3), 'more' => 'stuff');
  401. $this->assertEquals('<1> there is less <2> there is less <3> there is less ', $tpl->render($data));
  402. }
  403. /**
  404. * @dataProvider getIllegalInheritanceExamples
  405. * @expectedException Mustache_Exception_SyntaxException
  406. * @expectedExceptionMessage Illegal content in < parent tag
  407. */
  408. public function testIllegalInheritanceExamples($partials, $data, $template)
  409. {
  410. $this->mustache->setPartials($partials);
  411. $tpl = $this->mustache->loadTemplate($template);
  412. $tpl->render($data);
  413. }
  414. /**
  415. * @dataProvider getLegalInheritanceExamples
  416. */
  417. public function testLegalInheritanceExamples($partials, $data, $template, $expect)
  418. {
  419. $this->mustache->setPartials($partials);
  420. $tpl = $this->mustache->loadTemplate($template);
  421. $this->assertSame($expect, $tpl->render($data));
  422. }
  423. }