Skip to content

Commit a34f023

Browse files
committed
Add else to if and optional key in for
1 parent c85d6e7 commit a34f023

2 files changed

Lines changed: 51 additions & 20 deletions

File tree

src/Template.php

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public static function render($template, $data, $functions = array())
1212

1313
private static function createNode($type, $expression)
1414
{
15-
return (object) array('type' => $type, 'expression' => $expression, 'children' => array());
15+
return (object) array('type' => $type, 'expression' => $expression, 'children' => array(), 'value' => null);
1616
}
1717

1818
private static function tokenize($template)
@@ -47,6 +47,9 @@ private static function createSyntaxTree(&$tokens)
4747
} elseif ($token == 'endfor') {
4848
$type = 'endfor';
4949
$expression = false;
50+
} elseif ($token == 'else') {
51+
$type = 'else';
52+
$expression = false;
5053
} elseif (substr($token, 0, 3) == 'if:') {
5154
$type = 'if';
5255
$expression = substr($token, 3);
@@ -57,15 +60,18 @@ private static function createSyntaxTree(&$tokens)
5760
$type = 'var';
5861
$expression = $token;
5962
}
60-
if (in_array($type, array('endif', 'endfor'))) {
61-
$current = array_pop($stack);
62-
} else {
63+
if (in_array($type, array('endif', 'endfor', 'else'))) {
64+
if (count($stack)) {
65+
$current = array_pop($stack);
66+
}
67+
}
68+
if (in_array($type, array('if', 'for', 'var', 'else'))) {
6369
$node = Template::createNode($type, $expression);
6470
array_push($current->children, $node);
65-
if (in_array($type, array('if', 'for'))) {
66-
array_push($stack, $current);
67-
$current = $node;
68-
}
71+
}
72+
if (in_array($type, array('if', 'for', 'else'))) {
73+
array_push($stack, $current);
74+
$current = $node;
6975
}
7076
} else {
7177
array_push($current->children, Template::createNode('lit', $token));
@@ -77,11 +83,15 @@ private static function createSyntaxTree(&$tokens)
7783
private static function renderChildren($node, $data, $functions)
7884
{
7985
$result = '';
86+
$previousChild = null;
8087
foreach ($node->children as $child) {
8188
switch ($child->type) {
8289
case 'if':
8390
$result .= Template::renderIfNode($child, $data, $functions);
8491
break;
92+
case 'else':
93+
$result .= Template::renderElseNode($child, $previousChild, $data, $functions);
94+
break;
8595
case 'for':
8696
$result .= Template::renderForNode($child, $data, $functions);
8797
break;
@@ -92,6 +102,7 @@ private static function renderChildren($node, $data, $functions)
92102
$result .= $child->expression;
93103
break;
94104
}
105+
$previousChild = $child;
95106
}
96107
return $result;
97108
}
@@ -109,6 +120,19 @@ private static function renderIfNode($node, $data, $functions)
109120
$result = '';
110121
if ($value) {
111122
$result .= Template::renderChildren($node, $data, $functions);
123+
$node->value = $value;
124+
}
125+
return $result;
126+
}
127+
128+
private static function renderElseNode($node, $previousNode, $data, $functions)
129+
{
130+
if ($previousNode->type != 'if') {
131+
return "{{else!!could not find matching 'if'}}";
132+
}
133+
$result = '';
134+
if (!$previousNode->value) {
135+
$result .= Template::renderChildren($node, $data, $functions);
112136
}
113137
return $result;
114138
}
@@ -117,11 +141,15 @@ private static function renderForNode($node, $data, $functions)
117141
{
118142
$parts = explode('|', $node->expression);
119143
$path = array_shift($parts);
120-
$path = explode(':', $path, 2);
121-
if (count($path) != 2) {
144+
$path = explode(':', $path, 3);
145+
if (count($path) == 2) {
146+
list($var, $path) = $path;
147+
$key = false;
148+
} elseif (count($path) == 3) {
149+
list($var, $key, $path) = $path;
150+
} else {
122151
return '{{for:' . $node->expression . '!!' . "for must have 'for:var:array' format" . '}}';
123152
}
124-
list($var, $path) = $path;
125153
try {
126154
$value = Template::resolvePath($path, $data);
127155
$value = Template::applyFunctions($value, $parts, $functions);
@@ -132,8 +160,8 @@ private static function renderForNode($node, $data, $functions)
132160
return '{{for:' . $node->expression . '!!' . "expression must evaluate to an array" . '}}';
133161
}
134162
$result = '';
135-
foreach ($value as $v) {
136-
$data = array_merge($data, [$var => $v]);
163+
foreach ($value as $k => $v) {
164+
$data = array_merge($data, $key ? [$var => $v, $key => $k] : [$var => $v]);
137165
$result .= Template::renderChildren($node, $data, $functions);
138166
}
139167
return $result;

src/Tests/TemplateTest.php

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55

66
class TemplateTest extends \PHPUnit\Framework\TestCase
77
{
8-
public function testRender() {
9-
$this->assertEquals("hello World",Template::render('hello {{name|capitalize}}', ['name'=>'world'], ['capitalize'=>'ucfirst']));
10-
$this->assertEquals("hello {{name|failure!!function 'failure' not found}}",Template::render('hello {{name|failure}}', ['name'=>'world'], ['capitalize'=>'ucfirst']));
11-
$this->assertEquals("hello m is 3",Template::render('hello {{if:n.m|eq(3)}}m is 3{{endif}}', ['n'=>['m'=>3]], ['eq'=>function($a,$b){return $a==$b;}]));
12-
$this->assertEquals("hello 1980-05-13",Template::render('hello {{name|dateFormat(Y-m-d)}}', ['name'=>'May 13, 1980'], ['dateFormat'=>function($date, $format) { return date($format, strtotime($date)); }]));
13-
$this->assertEquals("test 1 2 3",Template::render('test{{for:i:counts}} {{i}}{{endfor}}', ['counts'=>[1,2,3]]));
14-
$this->assertEquals("test (-1,-1) (-1,1) (1,-1) (1,1)",Template::render('test{{for:x:steps}}{{for:y:steps}} ({{x}},{{y}}){{endfor}}{{endfor}}', ['steps'=>[-1,1]]));
8+
public function testRender()
9+
{
10+
$this->assertEquals("hello World", Template::render('hello {{name|capitalize}}', ['name' => 'world'], ['capitalize' => 'ucfirst']));
11+
$this->assertEquals("hello {{name|failure!!function 'failure' not found}}", Template::render('hello {{name|failure}}', ['name' => 'world'], ['capitalize' => 'ucfirst']));
12+
$this->assertEquals("hello m is 3", Template::render('hello {{if:n.m|eq(3)}}m is 3{{endif}}', ['n' => ['m' => 3]], ['eq' => function ($a, $b) {return $a == $b;}]));
13+
$this->assertEquals("hello not n", Template::render('hello {{if:n}}n{{else}}not n{{endif}}', ['n' => false]));
14+
$this->assertEquals("hello 1980-05-13", Template::render('hello {{name|dateFormat(Y-m-d)}}', ['name' => 'May 13, 1980'], ['dateFormat' => function ($date, $format) {return date($format, strtotime($date));}]));
15+
$this->assertEquals("test 1 2 3", Template::render('test{{for:i:counts}} {{i}}{{endfor}}', ['counts' => [1, 2, 3]]));
16+
$this->assertEquals("test a=1 b=2 c=3", Template::render('test{{for:v:k:counts}} {{k}}={{v}}{{endfor}}', ['counts' => ['a' => 1, 'b' => 2, 'c' => 3]]));
17+
$this->assertEquals("test (-1,-1) (-1,1) (1,-1) (1,1)", Template::render('test{{for:x:steps}}{{for:y:steps}} ({{x}},{{y}}){{endfor}}{{endfor}}', ['steps' => [-1, 1]]));
1518
}
1619
}

0 commit comments

Comments
 (0)