Example #1
0
 function template()
 {
     $this->set('title', 'Template Engine');
     $this->set('CACHE', TRUE);
     $this->expect(is_null($this->get('ERROR')), 'No errors expected at this point', 'ERROR variable is set: ' . $this->get('ERROR.text'));
     $this->set('a', 123);
     $this->set('b', '{{@a}}');
     $this->expect(Template::resolve('{{@b}}') == '123', 'Template token substituted; string value returned', 'Token substition failed: ' . Template::resolve('{{@b}}'));
     $this->expect(Template::resolve('{{@a}}') == '123', 'Template engine confirms substitution', 'Template engine failed: ' . Template::resolve('{{@a}}'));
     $this->set('a', 345);
     $this->expect(Template::resolve('{{@a}}') == '345', 'Template engine confirms replacement', 'Template engine failed: ' . Template::resolve('{{@a}}'));
     $this->expect(Template::resolve('{{@a+1}}') == '346', 'Mixed expression correct', 'Mixed expression failed: ' . Template::resolve('{{@a+1}}'));
     $this->expect(Template::resolve('{{@a + 1}}') == '346', 'Mixed expression (with whitespaces) correct', 'Mixed expression (with whitespaces) failed: ' . Template::resolve('{{@a + 1}}'));
     $this->set('x', '{{123}}');
     $this->expect(Template::resolve('{{@x}}') == '123', 'Integer constant in template expression correct', 'Template expression is wrong: ' . Template::resolve('{{@x}}'));
     $this->set('i', 'hello');
     $this->set('j', 'there');
     $this->expect(Template::resolve('{{@i.@j}}') == 'hellothere', 'String concatenation works', 'String concatenation problem: ' . Template::resolve('{{@i.@j}}'));
     $this->expect(Template::resolve('{{@i. @j}}') == 'hellothere', 'String concatenation (with whitespaces) works', 'String concatenation (with whitespaces) problem: ' . Template::resolve('{{@i. @j}}'));
     $this->expect(Template::resolve('{{@i .@j}}') == 'hellothere', 'Variation in string concatenation (with whitespaces) works', 'Variation in string concatenation (with whitespaces) problem: ' . Template::resolve('{{@i .@j}}'));
     $this->expect(Template::resolve('{{  @i  .  @j  }}') == 'hellothere', 'Liberal amounts of whitespaces produces the correct result', 'Liberal amounts of whitespaces produces strange result: ' . Template::resolve('{{  @i  .  @j  }}'));
     $this->set('x', '{{345+5}}');
     $this->expect(Template::resolve('{{@x}}') == '350', 'Arithmetic expression in template expression evaluated', 'Arithmetic expression is wrong: ' . Template::resolve('{{@x}}'));
     $this->set('x', '{{1+0.23e-4}}');
     $this->expect(Template::resolve('{{@x}}') == '1.000023', 'Negative exponential float in template expression correct', 'Negative exponential float is wrong: ' . Template::resolve('{{@x}}'));
     $this->set('x', '{{1+0.23e+4}}');
     $this->expect(Template::resolve('{{@x}}') == '2301', 'Positive exponential float in template expression correct', 'Positive exponential float is wrong: ' . Template::resolve('{{@x}}'));
     $this->set('x', '{{1+0.23e4}}');
     $this->expect(Template::resolve('{{@x}}') == '2301', 'Unsigned exponential float in template expression correct', 'Unsigned exponential float is wrong: ' . Template::resolve('{{@x}}'));
     $this->set('x', '{{456+7.5}}');
     $this->expect(Template::resolve('{{@x}}') == '463.5', 'Integer + float in template expression correct', 'Integer + float is wrong: ' . Template::resolve('{{@x}}'));
     $this->set('x', '{{(1+2)*3}}');
     $this->expect(Template::resolve('{{@x}}') == '9', 'Parenthesized arithmetic expression evaluated', 'Parenthesized expression is wrong: ' . Template::resolve('{{@x}}'));
     $this->expect(Template::resolve('{{(@a+1)*2}}') == '692', 'Variable + arithmetic expression evaluated', 'Variable + arithmetic expression is wrong: ' . Template::resolve('{{(@a+1)*2}}'));
     $this->set('x', '{{(intval(1+2.25))*3}}');
     $this->expect(Template::resolve('{{@x}}') == '9', 'Allowed function and nested parentheses evaluated', 'Allowed function/parentheses failed: ' . Template::resolve('{{@x}}'));
     $this->set('x', '{{(round(234.567,1)+(-1)+1)*2}}');
     $this->expect(Template::resolve('{{@x}}') == '469.2', 'Function with multiple arguments evaluated', 'Function with multiple arguments failed: ' . Template::resolve('{{@x}}'));
     $this->set('x', NULL);
     $this->expect(Template::resolve('{{@x}}') == '', 'NULL converted to empty string', 'NULL not converted to empty string: ' . Template::resolve('{{@x}}'));
     $this->set('x', '{{array()}}');
     $this->expect(Template::resolve('{{@x}}') == '', 'Empty array converted to empty string', 'Array conversion failed: ' . Template::resolve('{{@x}}'));
     $this->set('x', '{{array(1,2,3)}}');
     $this->expect(Template::resolve('{{@x}}') == 'Array', 'Array converted to string \'Array\'', 'Array conversion failed: ' . Template::resolve('{{@x}}'));
     $this->set('x', '{{NULL}}');
     $this->expect(Template::resolve('{{@x}}') == '', 'NULL value evaluated', 'Incorrect NULL evaluation: ' . Template::resolve('{{@x}}'));
     $this->set('x', '{{null}}');
     $this->expect(Template::resolve('{{@x}}') == '', 'NULL value evaluated (case-insensitive)', 'Incorrect NULL evaluation: ' . Template::resolve('{{@x}}'));
     $this->set('x', '{{TRUE}}');
     $this->expect(Template::resolve('{{@x}}') == '1', 'Boolean TRUE expression evaluated', 'Incorrect boolean evaluation: ' . Template::resolve('{{@x}}'));
     $this->set('x', '{{FALSE}}');
     $this->expect(Template::resolve('{{@x}}') == '', 'Boolean FALSE expression converted to empty string', 'Incorrect boolean evaluation: ' . Template::resolve('{{@x}}'));
     $this->set('x', '{{0}}');
     $this->expect(Template::resolve('{{@x}}') == '0', 'Zero remains as-is', 'Incorrect evaluation of integer zero: ' . Template::resolve('{{@x}}'));
     $this->set('x', '{{a@b.com}}');
     $this->expect(Template::resolve('{{@x}}') == '\'a@b.com\'', 'E-mail address preserved', 'Incorrect interpretation of e-mail address: ' . Template::resolve('{{@x}}'));
     $this->set('x', '{{new CustomObj}}');
     $this->expect(Template::resolve('{{@x}}') == '\'new CustomObj\'', 'Object instantiation using template engine prohibited', 'Object instantiation issue: ' . Template::resolve('{{@x}}'));
     $this->set('func', function ($x) {
         return 123;
     });
     $this->expect(Template::resolve('{{@func("hello")}}') == 123, 'Variable containing anonymous function interpreted correctly', 'Template misunderstood variable containing anonymous function: ' . Template::resolve('{{@func("hello")}}'));
     $z = new stdClass();
     $z->a = 123;
     $z->b = 345;
     $this->set('var', $z);
     $this->expect(Template::resolve('{{@var->a}}') == 123 && Template::resolve('{{@var->b}}') == 345, 'Variable containing an object interpreted correctly', 'Template misunderstood variable containing an object/properties: ' . Template::resolve('{{@var->a}}'));
     $z->c = function () {
         return 'foo';
     };
     $this->expect(Template::resolve('{{@var->c()}}') == 'foo', 'Variable containing an anonymous function rendered properly', 'Variable containing an anonymous function evaluated wrong: ' . Template::resolve('{{@var->c()}}'));
     $this->set('z.x', 'good idea');
     $this->expect(Template::resolve('{{@z.x}}') == 'good idea', 'Array element evaluated', 'Array element failed: ' . Template::resolve('{{@z.x}}'));
     $this->expect(Template::resolve('{{@z.y}}') == '', 'Non-existent array element converted to empty string', 'Non-existent element failed: ' . Template::resolve('{{@z.y}}'));
     $this->set('q', ' indeed');
     $this->expect(Template::resolve('{{@z.@q}}') == 'Array indeed', 'Concatenation of array and string produces expected result', 'Illegal concatenation: ' . Template::resolve('{{@z.@q}}'));
     $this->expect(Template::resolve('{{@z.x.@q}}') == 'good idea indeed', 'Concatenation of array element and string correct', 'Incorrect concatenation: ' . Template::resolve('{{@z.x.@q}}'));
     $this->set('my_plans', array('test' => 1, 'plan' => array('city_name' => 2)));
     $this->expect(Template::resolve('{{@my_plans[plan][city_name]}}') == 2, 'Got the right value of a deeply-nested array element', 'Incorrect evaluation of a deeply-nested array element');
     $out = Template::serve('template/layout.htm');
     $this->expect($out === "", 'Subtemplate not defined - none inserted', 'Subtemplate insertion issue: ' . $out);
     $this->set('sub', 'sub1.htm');
     $this->set('test', '<i>italics</i>');
     $out = Template::serve('template/layout.htm');
     $this->expect($out == "<i>italics</i>", 'HTML special characters retained', 'Problem with HTML insertion: ' . $out);
     $this->set('sub', 'sub1.htm');
     $this->set('test', '&copy;');
     $out = Template::serve('template/layout.htm');
     $this->expect($out == "&copy;", 'HTML entity inserted: ' . Template::serve('template/layout.htm'), 'Problem with HTML insertion: ' . $out);
     $this->set('sub', 'sub1.htm');
     $this->set('test', 'אני יכול לאכול זכוכית וזה לא מזיק לי.');
     $out = Template::serve('template/layout.htm');
     $this->expect(Template::serve('template/layout.htm') == "אני יכול לאכול זכוכית וזה לא מזיק לי.", 'UTF-8 character set rendered correctly: ' . $out, 'UTF-8 issue: ' . $out);
     $this->set('sub', 'sub1.htm');
     $this->set('test', 'I&nbsp;am&nbsp;here.');
     $out = Template::serve('template/layout.htm');
     $this->expect(Template::serve('template/layout.htm') == "I&nbsp;am&nbsp;here.", 'HTML entities preserved: ' . $out, 'HTML entities converted: ' . $out);
     $this->set('sub', 'sub2.htm');
     $this->set('src', '/test/image');
     $this->set('alt', htmlspecialchars('this is "the" life'));
     $out = Template::serve('template/layout.htm');
     $this->expect($out == '<img src="/test/image" alt="this is &quot;the&quot; life"/>', 'Double-quotes inside HTML attributes converted to XML entities', 'Problem with double-quotes inside HTML attributes: ' . $out);
     $this->set('sub', 'sub3.htm');
     $out = Template::serve('template/layout.htm');
     $this->clear('div');
     $this->expect($out == '', 'Undefined array renders empty output', 'Output not empty: ' . $out);
     $this->set('sub', 'sub3.htm');
     $out = Template::serve('template/layout.htm');
     $this->set('div', NULL);
     $this->expect($out == '', 'NULL used as group attribute renders empty output', 'Output not empty: ' . $out);
     $this->set('sub', 'sub3.htm');
     $out = Template::serve('template/layout.htm');
     $this->set('div', array());
     $this->expect($out == '', 'Empty array used as group attribute renders empty output', 'Output not empty: ' . $out);
     $this->set('sub', 'sub3.htm');
     $this->set('div', array('coffee' => array('arabica', 'barako', 'liberica', 'kopiluwak'), 'tea' => array('darjeeling', 'pekoe', 'samovar')));
     $out = Template::serve('template/layout.htm');
     $this->expect(preg_match('#' . '<div>\\s+' . '<p><span><b>coffee</b></span></p>\\s+' . '<p>\\s+' . '<span>arabica</span>\\s+' . '<span>barako</span>\\s+' . '<span>liberica</span>\\s+' . '<span>kopiluwak</span>\\s+' . '</p>\\s+' . '</div>\\s+' . '<div>\\s+' . '<p><span><b>tea</b></span></p>\\s+' . '<p>\\s+' . '<span>darjeeling</span>\\s+' . '<span>pekoe</span>\\s+' . '<span>samovar</span>\\s+' . '</p>\\s+' . '</div>' . '#s', $out), 'Subtemplate inserted; nested repeat directives rendered correctly', 'Template rendering issue: ' . $out);
     $this->set('sub', 'sub4.htm');
     $this->set('group', array('world', 'me', 'others'));
     $out = Template::serve('template/layout.htm');
     $this->expect(preg_match('#' . '<script type="text/javascript">\\s*' . 'function hello\\(\\) {\\s*' . 'alert\\(\'Javascript works\'\\);\\s*' . '}\\s*' . '</script>\\s*' . '<script type="text/javascript">alert\\(unescape\\("%3Cscript src=\'" \\+ gaJsHost \\+ "google-analytics\\.com/ga\\.js\' type=\'text/javascript\'%3E%3C/script%3E"\\)\\);</script>\\s' . 'world,\\s+me,\\s+others,\\s+' . '#s', $out), 'Javascript preserved', 'Javascript mangled: ' . htmlentities($out));
     $this->set('sub', 'sub5.htm');
     $this->set('cond1', FALSE);
     $this->set('cond3', FALSE);
     $out = trim(Template::serve('template/layout.htm'));
     $this->expect($out == 'c1:F,c3:F', 'Conditional directives evaluated correctly: FALSE, FALSE', 'Incorrect evaluation of conditional directives: ' . $out);
     $this->set('cond1', FALSE);
     $this->set('cond3', TRUE);
     $out = trim(Template::serve('template/layout.htm'));
     $this->expect($out == 'c1:F,c3:T', 'Conditional directives evaluated correctly: FALSE, TRUE', 'Incorrect evaluation of conditional directives: ' . $out);
     $this->set('cond1', TRUE);
     $this->set('cond2', FALSE);
     $out = trim(Template::serve('template/layout.htm'));
     $this->expect($out == 'c1:T,c2:F', 'Conditional directives evaluated correctly: TRUE, FALSE', 'Incorrect evaluation of conditional directives: ' . $out);
     $this->set('cond1', TRUE);
     $this->set('cond2', TRUE);
     $out = trim(Template::serve('template/layout.htm'));
     $this->expect($out == 'c1:T,c2:T', 'Conditional directives evaluated correctly: TRUE, TRUE', 'Incorrect evaluation of conditional directives: ' . $out);
     $this->set('pi_val', $pi = 3.141592654);
     $money = 63950.25;
     $this->set('sub', 'sub6.htm');
     $this->set('LANGUAGE', 'en');
     $out = trim(Template::serve('template/layout.htm'));
     // PHP 5.3.2 inserts a line feed at end of translation
     $this->expect($out == "<h3>I love Fat-Free!</h3>\n" . "<p>Today is " . ICU::format('{0,date}', array(time())) . "</p>\n" . "<p>The quick brown fox jumps over the lazy dog.</p>\n" . "<p>" . ICU::format('{0,number}', array($pi)) . "</p>\n" . "<p>" . ICU::format('{0,number,currency}', array($money)) . "</p>", 'English locale (i18n)', 'English locale mangled: ' . $out);
     $this->set('sub', 'sub6.htm');
     $this->set('LANGUAGE', 'fr-FR');
     $out = trim(Template::serve('template/layout.htm'));
     // PHP 5.3.2 inserts a line feed at end of translation
     $this->expect($out == "<h3>J'aime Fat-Free!</h3>\n" . "<p>Aujourd'hui, c'est " . ICU::format('{0,date}', array(time())) . "</p>\n" . "<p>Les naïfs ægithales hâtifs pondant à Noël où il gèle sont sûrs d'être déçus et de voir leurs drôles d'œufs abîmés.</p>\n" . "<p>" . ICU::format('{0,number}', array($pi)) . "</p>\n" . "<p>" . ICU::format('{0,number,currency}', array($money)) . "</p>", 'Translated properly to French', 'French translation mangled: ' . $out);
     $this->set('sub', 'sub6.htm');
     $this->set('LANGUAGE', 'es-AR');
     $out = trim(Template::serve('template/layout.htm'));
     // PHP 5.3.2 inserts a line feed at end of translation
     $this->expect($out == "<h3>Me encanta Fat-Free!</h3>\n" . "<p>Hoy es " . ICU::format('{0,date}', array(time())) . "</p>\n" . "<p>El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y frío, añoraba a su querido cachorro.</p>\n" . "<p>" . ICU::format('{0,number}', array($pi)) . "</p>\n" . "<p>" . ICU::format('{0,number,currency}', array($money)) . "</p>", 'Translated properly to Spanish', 'Spanish translation mangled: ' . $out);
     $this->set('sub', 'sub6.htm');
     $this->set('LANGUAGE', 'de-DE');
     $out = trim(Template::serve('template/layout.htm'));
     // PHP 5.3.2 inserts a line feed at end of translation
     $this->expect($out == "<h3>Ich liebe Fat-Free!</h3>\n" . "<p>Heute ist " . ICU::format('{0,date}', array(time())) . "</p>\n" . "<p>Im finsteren Jagdschloß am offenen Felsquellwasser patzte der affig-flatterhafte kauzig-höfliche Bäcker über seinem versifften kniffligen Xylophon.</p>\n" . "<p>" . ICU::format('{0,number}', array($pi)) . "</p>\n" . "<p>" . ICU::format('{0,number,currency}', array($money)) . "</p>", 'Translated properly to German', 'German translation mangled: ' . $out);
     $this->set('LANGUAGE', 'en');
     $this->set('sub', 'sub7.htm');
     $this->set('array', array('a' => 'apple', 'b' => 'blueberry', 'c' => 'cherry'));
     $this->set('element', 'b');
     $out = trim(Template::serve('template/layout.htm'));
     $this->expect($out == 'blueberry', 'Array with variable element rendered correctly', 'Array variable element failed to render: ' . var_export($out, TRUE));
     $this->set('sub', 'sub8.htm');
     $this->set('func', function ($arg1, $arg2) {
         return 'hello, ' . $arg1 . ' ' . $arg2;
     });
     $this->set('arg1', 'wise');
     $this->set('arg2', 'guy');
     $out = trim(Template::serve('template/layout.htm'));
     $this->expect($out == 'hello, wise guy', 'Function with variable arguments rendered correctly', 'Array with variable arguments failed to render: ' . var_export($out, TRUE));
     $this->set('benchmark', array_fill(1, 100, array('a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5, 'f' => 6, 'g' => 7, 'h' => 8, 'i' => 9, 'j' => 10)));
     $time = microtime(TRUE);
     Template::serve('template/benchmark.htm');
     $elapsed = round(microtime(TRUE) - $time, 3);
     $this->expect($elapsed < 0.05, 'Template containing ' . count($this->get('benchmark')) * 10 . '+ HTML elements/calculations rendered in ' . $elapsed . ' seconds', 'Template rendering too slow on this server: ' . $elapsed . ' seconds');
     echo $this->render('basic/results.htm');
 }