Done range in foreach

This commit is contained in:
bzick 2015-02-22 23:07:26 +03:00
parent 83fbfb1fca
commit 9a725deaff
5 changed files with 143 additions and 212 deletions

View File

@ -136,21 +136,20 @@ class Compiler
$key = null; $key = null;
$before = $body = array(); $before = $body = array();
$prepend = ""; $prepend = "";
if ($tokens->is(T_VARIABLE)) { if ($tokens->is('[')) {
$from = $scope->tpl->parseTerm($tokens, $is_var);
if($is_var) {
$check = '!empty('.$from.')';
} else {
$scope["var"] = $scope->tpl->tmpVar();
$prepend = $scope["var"].' = '.$from.';';
$from = $check = $scope["var"];
}
} elseif ($tokens->is('[')) {
$count = 0; $count = 0;
$from = $scope->tpl->parseArray($tokens, $count); $from = $scope->tpl->parseArray($tokens, $count);
$check = $count; $check = $count;
} else { } else {
throw new UnexpectedTokenException($tokens, null, "tag {foreach}"); $from = $scope->tpl->parseExpr($tokens, $is_var);
if($is_var) {
$check = '!empty('.$from.') && (is_array('.$from.') || '.$from.' instanceof \Traversable)';
} else {
$scope["var"] = $scope->tpl->tmpVar();
$prepend = $scope["var"].' = '.$from.';';
$from = $scope["var"];
$check = 'is_array('.$from.') && count('.$from.') || ('.$from.' instanceof \Traversable)';
}
} }
$tokens->get(T_AS); $tokens->get(T_AS);
$tokens->next(); $tokens->next();
@ -193,9 +192,9 @@ class Compiler
$body = $body ? implode("; ", $body) . ";" : ""; $body = $body ? implode("; ", $body) . ";" : "";
$scope["after"] = $scope["after"] ? implode("; ", $scope["after"]) . ";" : ""; $scope["after"] = $scope["after"] ? implode("; ", $scope["after"]) . ";" : "";
if ($key) { if ($key) {
return "$prepend if($check) { $before foreach($from as $key => $value) { $body"; return "$prepend if($check) {\n $before foreach($from as $key => $value) { $body";
} else { } else {
return "$prepend if($check) { $before foreach($from as $value) { $body"; return "$prepend if($check) {\n $before foreach($from as $value) { $body";
} }
} }
@ -236,10 +235,11 @@ class Compiler
* @throws Error\UnexpectedTokenException * @throws Error\UnexpectedTokenException
* @throws Error\InvalidUsageException * @throws Error\InvalidUsageException
* @return string * @return string
* @codeCoverageIgnore
*/ */
public static function forOpen(Tokenizer $tokens, Tag $scope) public static function forOpen(Tokenizer $tokens, Tag $scope)
{ {
trigger_error("Fenom: tag {for} deprecated, use {foreach 1..4 as \$value}", E_USER_DEPRECATED); trigger_error("Fenom: tag {for} deprecated, use {foreach 1..4 as \$value} (in {$scope->tpl->getName()}:{$scope->line})", E_USER_DEPRECATED);
$p = array( $p = array(
"index" => false, "index" => false,
"first" => false, "first" => false,
@ -310,6 +310,7 @@ class Compiler
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Tag $scope * @param Tag $scope
* @return string * @return string
* @codeCoverageIgnore
*/ */
public static function forElse($tokens, Tag $scope) public static function forElse($tokens, Tag $scope)
{ {
@ -323,6 +324,7 @@ class Compiler
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Tag $scope * @param Tag $scope
* @return string * @return string
* @codeCoverageIgnore
*/ */
public static function forClose($tokens, Tag $scope) public static function forClose($tokens, Tag $scope)
{ {

View File

@ -657,10 +657,11 @@ class Template extends Render
* Parse expressions. The mix of operators and terms. * Parse expressions. The mix of operators and terms.
* *
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param bool $is_var
* @throws \Exception
* @return string * @return string
* @throws Error\UnexpectedTokenException
*/ */
public function parseExpr(Tokenizer $tokens) public function parseExpr(Tokenizer $tokens, &$is_var = false)
{ {
$exp = array(); $exp = array();
$var = false; // last term was: true - variable, false - mixed $var = false; // last term was: true - variable, false - mixed
@ -755,6 +756,10 @@ class Template extends Render
if ($op || !$exp) { if ($op || !$exp) {
throw new UnexpectedTokenException($tokens); throw new UnexpectedTokenException($tokens);
} }
if(count($exp) == 1 && $var) {
$is_var = true;
}
return implode(' ', $exp); return implode(' ', $exp);
} }

View File

@ -18,7 +18,7 @@ class SandboxTest extends TestCase {
// $this->tpl('welcome.tpl', '{$a}'); // $this->tpl('welcome.tpl', '{$a}');
// var_dump($this->fenom->compileCode('{set $a=$one|min:0..$three|max:4}')->getBody()); // var_dump($this->fenom->compileCode('{set $a=$one|min:0..$three|max:4}')->getBody());
// try { // try {
// var_dump($this->fenom->compileCode('{foreach $a..$b|up as $k => $v} {/foreach}')->getBody()); // var_dump($this->fenom->compileCode('{foreach $a as $k => $v} {/foreach}')->getBody());
// } catch (\Exception $e) { // } catch (\Exception $e) {
// print_r($e->getMessage() . "\n" . $e->getTraceAsString()); // print_r($e->getMessage() . "\n" . $e->getTraceAsString());
// while ($e->getPrevious()) { // while ($e->getPrevious()) {

View File

@ -8,17 +8,17 @@ class TagsTest extends TestCase
/** /**
* @group test-for * @group test-for
*/ */
public function testFor() // public function testFor()
{ // {
$this->assertRender('{for $i=0 to=3}{$i},{/for}', "0,1,2,3,"); // $this->assertRender('{for $i=0 to=3}{$i},{/for}', "0,1,2,3,");
} // }
/** /**
* @dataProvider providerScalars * @dataProvider providerScalars
*/ */
public function testVar($tpl_val, $val) public function testVar($tpl_val, $val)
{ {
$this->assertRender("{var \$a=$tpl_val}\nVar: {\$a}", "Var: " . $val); $this->assertRender("{set \$a=$tpl_val}\nVar: {\$a}", "Var: " . $val);
} }
/** /**
@ -26,7 +26,7 @@ class TagsTest extends TestCase
*/ */
public function testVarBlock($tpl_val, $val) public function testVarBlock($tpl_val, $val)
{ {
$this->assertRender("{var \$a}before {{$tpl_val}} after{/var}\nVar: {\$a}", "Var: before " . $val . " after"); $this->assertRender("{set \$a}before {{$tpl_val}} after{/set}\nVar: {\$a}", "Var: before " . $val . " after");
} }
/** /**
@ -35,14 +35,14 @@ class TagsTest extends TestCase
public function testVarBlockModified($tpl_val, $val) public function testVarBlockModified($tpl_val, $val)
{ {
$this->assertRender( $this->assertRender(
"{var \$a|low|dots}before {{$tpl_val}} after{/var}\nVar: {\$a}", "{set \$a|low|dots}before {{$tpl_val}} after{/set}\nVar: {\$a}",
"Var: " . strtolower("before " . $val . " after") . "..." "Var: " . strtolower("before " . $val . " after") . "..."
); );
} }
public function testCycle() public function testCycle()
{ {
$this->assertRender('{for $i=0 to=4}{cycle ["one", "two"]}, {/for}', "one, two, one, two, one, "); $this->assertRender('{foreach 0..4 as $i}{cycle ["one", "two"]}, {/foreach}', "one, two, one, two, one, ");
} }
/** /**
@ -51,7 +51,7 @@ class TagsTest extends TestCase
public function testCycleIndex() public function testCycleIndex()
{ {
$this->assertRender( $this->assertRender(
'{var $a=["one", "two"]}{for $i=1 to=5}{cycle $a index=$i}, {/for}', '{set $a=["one", "two"]}{foreach 1..5 as $i}{cycle $a index=$i}, {/foreach}',
"two, one, two, one, two, " "two, one, two, one, two, "
); );
} }

View File

@ -688,105 +688,28 @@ class TemplateTest extends TestCase
); );
} }
public static function providerForeach()
{
$a = array(
"list" => array(1 => "one", 2 => "two", 3 => "three"),
"empty" => array(),
"obj" => new Helper("testing")
);
return array(
array('Foreach: {foreach $list as $e} {$e}, {/foreach} end', $a, 'Foreach: one, two, three, end'),
array('Foreach: {foreach $list as $e} {$e},{break} break {/foreach} end', $a, 'Foreach: one, end'),
array(
'Foreach: {foreach $list as $e} {$e},{continue} continue {/foreach} end',
$a,
'Foreach: one, two, three, end'
),
array(
'Foreach: {foreach ["one", "two", "three"] as $e} {$e}, {/foreach} end',
$a,
'Foreach: one, two, three, end'
),
array(
'Foreach: {foreach $list as $k => $e} {$k} => {$e}, {/foreach} end',
$a,
'Foreach: 1 => one, 2 => two, 3 => three, end'
),
array(
'Foreach: {foreach [1 => "one", 2 => "two", 3 => "three"] as $k => $e} {$k} => {$e}, {/foreach} end',
$a,
'Foreach: 1 => one, 2 => two, 3 => three, end'
),
array('Foreach: {foreach $empty as $k => $e} {$k} => {$e}, {/foreach} end', $a, 'Foreach: end'),
array('Foreach: {foreach [] as $k => $e} {$k} => {$e}, {/foreach} end', $a, 'Foreach: end'),
array('Foreach: {foreach $obj->getArray() as $k => $e} {$k} => {$e}, {/foreach} end', $a, 'Foreach: 0 => 1, 1 => 2, 2 => 3, end'),
array('Foreach: {foreach $unexists as $k => $e} {$k} => {$e}, {/foreach} end', $a, 'Foreach: end'),
array(
'Foreach: {foreach $empty as $k => $e} {$k} => {$e}, {foreachelse} empty {/foreach} end',
$a,
'Foreach: empty end'
),
array(
'Foreach: {foreach $list as $e index=$i} {$i}: {$e}, {/foreach} end',
$a,
'Foreach: 0: one, 1: two, 2: three, end'
),
array(
'Foreach: {foreach $list as $k => $e index=$i} {$i}: {$k} => {$e}, {/foreach} end',
$a,
'Foreach: 0: 1 => one, 1: 2 => two, 2: 3 => three, end'
),
array(
'Foreach: {foreach $empty as $k => $e index=$i} {$i}: {$k} => {$e}, {foreachelse} empty {/foreach} end',
$a,
'Foreach: empty end'
),
array(
'Foreach: {foreach $list as $k => $e first=$f index=$i} {if $f}first{/if} {$i}: {$k} => {$e}, {/foreach} end',
$a,
'Foreach: first 0: 1 => one, 1: 2 => two, 2: 3 => three, end'
),
array(
'Foreach: {foreach $list as $k => $e last=$l first=$f index=$i} {if $f}first{/if} {$i}: {$k} => {$e}, {if $l}last{/if} {/foreach} end',
$a,
'Foreach: first 0: 1 => one, 1: 2 => two, 2: 3 => three, last end'
),
array(
'Foreach: {foreach $empty as $k => $e last=$l first=$f index=$i} {if $f}first{/if} {$i}: {$k} => {$e}, {if $l}last{/if} {foreachelse} empty {/foreach} end',
$a,
'Foreach: empty end'
),
array(
'Foreach: {foreach [1 => "one", 2 => "two", 3 => "three"] as $k => $e last=$l first=$f index=$i} {if $f}first{/if} {$i}: {$k} => {$e}, {if $l}last{/if} {/foreach} end',
$a,
'Foreach: first 0: 1 => one, 1: 2 => two, 2: 3 => three, last end'
),
);
}
public static function providerForeachInvalid() public static function providerForeachInvalid()
{ {
return array( return array(
array( array(
'Foreach: {foreach} {$e}, {/foreach} end', 'Foreach: {foreach} {$e}, {/foreach} end',
'Fenom\Error\CompileException', 'Fenom\Error\CompileException',
"Unexpected end of tag {foreach}" "Unexpected end of expression"
), ),
array( array(
'Foreach: {foreach $list} {$e}, {/foreach} end', 'Foreach: {foreach $list} {$e}, {/foreach} end',
'Fenom\Error\CompileException', 'Fenom\Error\CompileException',
"Unexpected end of expression" "Unexpected end of expression"
), ),
array( // array(
'Foreach: {foreach $list+1 as $e} {$e}, {/foreach} end', // 'Foreach: {foreach $list+1 as $e} {$e}, {/foreach} end',
'Fenom\Error\CompileException', // 'Fenom\Error\CompileException',
"Unexpected token '+'" // "Unexpected token '+'"
), // ),
array( array(
'Foreach: {foreach array_random() as $e} {$e}, {/foreach} end', 'Foreach: {foreach array_random() as $e} {$e}, {/foreach} end',
'Fenom\Error\CompileException', 'Fenom\Error\CompileException',
"Unexpected token 'array_random'" "Function array_random not found"
), ),
array( array(
'Foreach: {foreach $list as $e+1} {$e}, {/foreach} end', 'Foreach: {foreach $list as $e+1} {$e}, {/foreach} end',
@ -826,7 +749,7 @@ class TemplateTest extends TestCase
array( array(
'Foreach: {foreach last=$l $list as $e } {$e}, {/foreach} end', 'Foreach: {foreach last=$l $list as $e } {$e}, {/foreach} end',
'Fenom\Error\CompileException', 'Fenom\Error\CompileException',
"Unexpected token 'last' in tag {foreach}" "Unexpected token 'last' in expression"
), ),
array( array(
'Foreach: {foreach $list as $e unknown=1} {$e}, {/foreach} end', 'Foreach: {foreach $list as $e unknown=1} {$e}, {/foreach} end',
@ -966,95 +889,6 @@ class TemplateTest extends TestCase
); );
} }
public static function providerFor()
{
$a = array("c" => 1, "s" => 1, "m" => 3);
return array(
array('For: {for $a=4 to=6} $a: {$a}, {/for} end', $a, 'For: $a: 4, $a: 5, $a: 6, end'),
array('For: {for $a=4 step=2 to=10} $a: {$a}, {/for} end', $a, 'For: $a: 4, $a: 6, $a: 8, $a: 10, end'),
array('For: {for $a=4 step=-2 to=0} $a: {$a}, {/for} end', $a, 'For: $a: 4, $a: 2, $a: 0, end'),
array('For: {for $a=$c step=$s to=$m} $a: {$a}, {/for} end', $a, 'For: $a: 1, $a: 2, $a: 3, end'),
array('For: {for $a=-1 step=-max(1,2) to=-5} $a: {$a}, {/for} end', $a, 'For: $a: -1, $a: -3, $a: -5, end'),
array('For: {for $a=4 step=2 to=10} $a: {$a}, {break} break {/for} end', $a, 'For: $a: 4, end'),
array(
'For: {for $a=4 step=2 to=8} $a: {$a}, {continue} continue {/for} end',
$a,
'For: $a: 4, $a: 6, $a: 8, end'
),
array(
'For: {for $a=4 step=2 to=8 index=$i} $a{$i}: {$a}, {/for} end',
$a,
'For: $a0: 4, $a1: 6, $a2: 8, end'
),
array(
'For: {for $a=4 step=2 to=8 index=$i first=$f} {if $f}first{/if} $a{$i}: {$a}, {/for} end',
$a,
'For: first $a0: 4, $a1: 6, $a2: 8, end'
),
array(
'For: {for $a=4 step=2 to=8 index=$i first=$f last=$l} {if $f} first {/if} $a{$i}: {$a}, {if $l} last {/if} {/for} end',
$a,
'For: first $a0: 4, $a1: 6, $a2: 8, last end'
),
array('For: {for $a=1 to=-1 } $a: {$a}, {forelse} empty {/for} end', $a, 'For: empty end'),
array(
'For: {for $a=1 to=-1 index=$i first=$f last=$l} {if $f} first {/if} $a{$i}: {$a}, {if $l} last {/if} {forelse} empty {/for} end',
$a,
'For: empty end'
),
);
}
public static function providerForInvalid()
{
return array(
array('For: {for} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected end of expression"),
array('For: {for $a=} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected end of expression"),
array('For: {for $a+1=3 to=6} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected token '+'"),
array(
'For: {for max($a,$b)=3 to=6} block1 {/for} end',
'Fenom\Error\CompileException',
"Unexpected token '='"
),
array('For: {for to=6 $a=3} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected token 'to'"),
array(
'For: {for index=$i $a=3 to=6} block1 {/for} end',
'Fenom\Error\CompileException',
"Unexpected token 'index'"
),
array(
'For: {for first=$i $a=3 to=6} block1 {/for} end',
'Fenom\Error\CompileException',
"Unexpected token 'first'"
),
array(
'For: {for last=$i $a=3 to=6} block1 {/for} end',
'Fenom\Error\CompileException',
"Unexpected token 'last'"
),
array(
'For: {for $a=4 to=6 unk=4} block1 {/for} end',
'Fenom\Error\CompileException',
"Unknown parameter 'unk'"
),
array(
'For: {for $a=4 to=6 step=0} block1 {/for} end',
'Fenom\Error\CompileException',
"Invalid step value"
),
array(
'For: {for $a=4 to=6} $a: {$a}, {forelse} {break} {/for} end',
'Fenom\Error\CompileException',
"Improper usage of the tag {break}"
),
array(
'For: {for $a=4 to=6} $a: {$a}, {forelse} {continue} {/for} end',
'Fenom\Error\CompileException',
"Improper usage of the tag {continue}"
),
);
}
public static function providerLayersInvalid() public static function providerLayersInvalid()
{ {
return array( return array(
@ -1070,14 +904,14 @@ class TemplateTest extends TestCase
), ),
array('Layers: {blah} end', 'Fenom\Error\CompileException', "Unexpected tag 'blah'"), array('Layers: {blah} end', 'Fenom\Error\CompileException', "Unexpected tag 'blah'"),
array( array(
'Layers: {for $a=4 to=6} block1 {if 1} {forelse} {/if} {/for} end', 'Layers: {foreach 4..6 as $a} block1 {if 1} {foreachelse} {/if} {/foreach} end',
'Fenom\Error\CompileException', 'Fenom\Error\CompileException',
"Unexpected tag 'forelse' (this tag can be used with 'for')" "Unexpected tag 'foreachelse' (this tag can be used with 'foreach')"
), ),
array( array(
'Layers: {for $a=4 to=6} block1 {if 1} {/for} {/if} end', 'Layers: {foreach 4..6 as $a} block1 {if 1} {/foreach} {/if} end',
'Fenom\Error\CompileException', 'Fenom\Error\CompileException',
"Unexpected closing of the tag 'for'" "Unexpected closing of the tag 'foreach'"
), ),
array( array(
'Layers: {switch 1} {if 1} {case 1} {/if} {/switch} end', 'Layers: {switch 1} {if 1} {case 1} {/if} {/switch} end',
@ -1464,11 +1298,101 @@ class TemplateTest extends TestCase
$this->exec(__FUNCTION__ . ": $code end", $vars, __FUNCTION__ . ": $result end"); $this->exec(__FUNCTION__ . ": $code end", $vars, __FUNCTION__ . ": $result end");
} }
public static function providerForeach()
{
$a = array(
"list" => array(1 => "one", 2 => "two", 3 => "three"),
"empty" => array(),
"obj" => new Helper("testing")
);
return array(
array('Foreach: {foreach $list as $e} {$e}, {/foreach} end', $a, 'Foreach: one, two, three, end'),
array('Foreach: {foreach $list as $e} {$e},{break} break {/foreach} end', $a, 'Foreach: one, end'),
array(
'Foreach: {foreach $list as $e} {$e},{continue} continue {/foreach} end',
$a,
'Foreach: one, two, three, end'
),
array(
'Foreach: {foreach ["one", "two", "three"] as $e} {$e}, {/foreach} end',
$a,
'Foreach: one, two, three, end'
),
array(
'Foreach: {foreach $list as $k => $e} {$k} => {$e}, {/foreach} end',
$a,
'Foreach: 1 => one, 2 => two, 3 => three, end'
),
array(
'Foreach: {foreach [1 => "one", 2 => "two", 3 => "three"] as $k => $e} {$k} => {$e}, {/foreach} end',
$a,
'Foreach: 1 => one, 2 => two, 3 => three, end'
),
array('Foreach: {foreach $empty as $k => $e} {$k} => {$e}, {/foreach} end', $a, 'Foreach: end'),
array('Foreach: {foreach [] as $k => $e} {$k} => {$e}, {/foreach} end', $a, 'Foreach: end'),
array('Foreach: {foreach $obj->getArray() as $k => $e} {$k} => {$e}, {/foreach} end', $a, 'Foreach: 0 => 1, 1 => 2, 2 => 3, end'),
array('Foreach: {foreach $unexists as $k => $e} {$k} => {$e}, {/foreach} end', $a, 'Foreach: end'),
array(
'Foreach: {foreach $empty as $k => $e} {$k} => {$e}, {foreachelse} empty {/foreach} end',
$a,
'Foreach: empty end'
),
array(
'Foreach: {foreach $list as $e index=$i} {$i}: {$e}, {/foreach} end',
$a,
'Foreach: 0: one, 1: two, 2: three, end'
),
array(
'Foreach: {foreach $list as $k => $e index=$i} {$i}: {$k} => {$e}, {/foreach} end',
$a,
'Foreach: 0: 1 => one, 1: 2 => two, 2: 3 => three, end'
),
array(
'Foreach: {foreach $empty as $k => $e index=$i} {$i}: {$k} => {$e}, {foreachelse} empty {/foreach} end',
$a,
'Foreach: empty end'
),
array(
'Foreach: {foreach $list as $k => $e first=$f index=$i} {if $f}first{/if} {$i}: {$k} => {$e}, {/foreach} end',
$a,
'Foreach: first 0: 1 => one, 1: 2 => two, 2: 3 => three, end'
),
array(
'Foreach: {foreach $list as $k => $e last=$l first=$f index=$i} {if $f}first{/if} {$i}: {$k} => {$e}, {if $l}last{/if} {/foreach} end',
$a,
'Foreach: first 0: 1 => one, 1: 2 => two, 2: 3 => three, last end'
),
array(
'Foreach: {foreach $empty as $k => $e last=$l first=$f index=$i} {if $f}first{/if} {$i}: {$k} => {$e}, {if $l}last{/if} {foreachelse} empty {/foreach} end',
$a,
'Foreach: empty end'
),
array(
'Foreach: {foreach [1 => "one", 2 => "two", 3 => "three"] as $k => $e last=$l first=$f index=$i} {if $f}first{/if} {$i}: {$k} => {$e}, {if $l}last{/if} {/foreach} end',
$a,
'Foreach: first 0: 1 => one, 1: 2 => two, 2: 3 => three, last end'
),
array(
'Foreach: {foreach 1..3 as $k => $e} {$k} => {$e}, {/foreach} end',
$a,
'Foreach: 0 => 1, 1 => 2, 2 => 3, end'
),
array(
'Foreach: {foreach $.get.items as $e} {$e}, {/foreach} end',
$a,
'Foreach: one, two, three, end'
),
);
}
/** /**
* @dataProvider providerForeach * @dataProvider providerForeach
* @backupGlobals
*/ */
public function testForeach($code, $vars, $result) public function testForeach($code, $vars, $result)
{ {
$_GET['items'] = array('one', 'two', 'three');
$this->exec($code, $vars, $result); $this->exec($code, $vars, $result);
} }
@ -1483,18 +1407,18 @@ class TemplateTest extends TestCase
/** /**
* @dataProvider providerFor * @dataProvider providerFor
*/ */
public function testFor($code, $vars, $result) // public function testFor($code, $vars, $result)
{ // {
$this->exec($code, $vars, $result); // $this->exec($code, $vars, $result);
} // }
/** /**
* @dataProvider providerForInvalid * @dataProvider providerForInvalid
*/ */
public function testForInvalid($code, $exception, $message, $options = 0) // public function testForInvalid($code, $exception, $message, $options = 0)
{ // {
$this->execError($code, $exception, $message, $options); // $this->execError($code, $exception, $message, $options);
} // }
/** /**
* @group testIgnores * @group testIgnores