Skip to content

Commit 50d3771

Browse files
committed
Add support for more literals, and binary expressions
Add limited support for binary expression ( `a != 3` ). The support is limited to comparing numbers, strings and bools with one another, and restricted to comparison operators common between Javascript and PHP. Bug: T398308
1 parent bd7e899 commit 50d3771

File tree

6 files changed

+180
-0
lines changed

6 files changed

+180
-0
lines changed

src/JsParsing/BinaryExpression.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
declare( strict_types = 1 );
3+
4+
namespace WMDE\VueJsTemplating\JsParsing;
5+
6+
use RuntimeException;
7+
8+
class BinaryExpression implements ParsedExpression {
9+
10+
private ParsedExpression $left;
11+
private ParsedExpression $right;
12+
private string $operator;
13+
14+
public function __construct( ParsedExpression $left, ParsedExpression $right, string $operator ) {
15+
$this->left = $left;
16+
$this->right = $right;
17+
$this->operator = $operator;
18+
}
19+
20+
/**
21+
* @param array $data
22+
*
23+
* @throws RuntimeException
24+
* @return bool
25+
*/
26+
public function evaluate( array $data ) {
27+
$lval = $this->left->evaluate( $data );
28+
$rval = $this->right->evaluate( $data );
29+
if (
30+
!( is_string( $rval ) || is_numeric( $rval ) || is_bool( $rval ) ) ||
31+
!( is_string( $lval ) || is_numeric( $lval ) || is_bool( $lval ) )
32+
) {
33+
throw new RuntimeException(
34+
'BooleanExpression must compare strings or numbers. Got ' .
35+
gettype( $lval ) . ' and ' . gettype( $rval )
36+
);
37+
}
38+
return match ( $this->operator ) {
39+
'===' => $lval === $rval,
40+
'==' => $lval == $rval,
41+
'>=' => $lval >= $rval,
42+
'>' => $lval > $rval,
43+
'<=' => $lval <= $rval,
44+
'<' => $lval < $rval,
45+
'!=' => $lval != $rval,
46+
default => throw new RuntimeException( 'Unknown operator in BooleanExpression: "' . $this->operator . '"' )
47+
};
48+
}
49+
50+
}

src/JsParsing/BooleanLiteral.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare( strict_types = 1 );
4+
5+
namespace WMDE\VueJsTemplating\JsParsing;
6+
7+
class BooleanLiteral implements ParsedExpression {
8+
9+
/**
10+
* @var bool value
11+
*/
12+
private $value;
13+
14+
public function __construct( bool $value ) {
15+
$this->value = $value;
16+
}
17+
18+
/**
19+
* @param array $data ignored
20+
*
21+
* @return value as provided on construction time
22+
*/
23+
public function evaluate( array $data ) {
24+
return $this->value;
25+
}
26+
27+
}

src/JsParsing/NumericLiteral.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare( strict_types = 1 );
4+
5+
namespace WMDE\VueJsTemplating\JsParsing;
6+
7+
class NumericLiteral implements ParsedExpression {
8+
9+
/**
10+
* @var int|float
11+
*/
12+
private $value;
13+
14+
/**
15+
* @param int|float $value
16+
*/
17+
public function __construct( $value ) {
18+
$this->value = $value;
19+
}
20+
21+
/**
22+
* @param array $data ignored
23+
*
24+
* @return value as provided on construction time
25+
*/
26+
public function evaluate( array $data ) {
27+
return $this->value;
28+
}
29+
30+
}

src/JsParsing/PeastExpressionConverter.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44

55
namespace WMDE\VueJsTemplating\JsParsing;
66

7+
use Peast\Syntax\Node\BinaryExpression as PeastBinaryExpression;
8+
use Peast\Syntax\Node\BooleanLiteral as PeastBooleanLiteral;
79
use Peast\Syntax\Node\CallExpression;
810
use Peast\Syntax\Node\Expression;
911
use Peast\Syntax\Node\Identifier;
1012
use Peast\Syntax\Node\MemberExpression;
13+
use Peast\Syntax\Node\NumericLiteral as PeastNumericLiteral;
1114
use Peast\Syntax\Node\ObjectExpression;
1215
use Peast\Syntax\Node\StringLiteral as PeastStringLiteral;
1316
use Peast\Syntax\Node\UnaryExpression;
@@ -71,6 +74,12 @@ protected function convertObjectExpression( ObjectExpression $expression ) {
7174
return new JsDictionary( $parsedExpressionMap );
7275
}
7376

77+
protected function convertBinaryExpression( PeastBinaryExpression $expression ) {
78+
$lexp = $this->convertExpression( $expression->getLeft() );
79+
$rexp = $this->convertExpression( $expression->getRight() );
80+
return new BinaryExpression( $lexp, $rexp, $expression->getOperator() );
81+
}
82+
7483
public function convertExpression( Expression $expression ) {
7584
return match( get_class( $expression ) ) {
7685
UnaryExpression::class => $this->convertUnaryExpression( $expression ),
@@ -79,6 +88,9 @@ public function convertExpression( Expression $expression ) {
7988
Identifier::class => new VariableAccess( [ $expression->getName() ] ),
8089
CallExpression::class => $this->convertCallExpression( $expression ),
8190
ObjectExpression::class => $this->convertObjectExpression( $expression ),
91+
PeastBooleanLiteral::class => new BooleanLiteral( $expression->getValue() ),
92+
PeastNumericLiteral::class => new NumericLiteral( $expression->getValue() ),
93+
PeastBinaryExpression::class => $this->convertBinaryExpression( $expression ),
8294
default => throw new RuntimeException(
8395
'Unable to parse complex expression of type ' . get_class( $expression )
8496
)

tests/php/JsParsing/BasicJsExpressionParserTest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace WMDE\VueJsTemplating\Test\JsParsing;
44

5+
use Exception;
56
use PHPUnit\Framework\TestCase;
67
use WMDE\VueJsTemplating\JsParsing\BasicJsExpressionParser;
78

@@ -116,4 +117,49 @@ public function testCanParse_dictionary_with_string_keys(): void {
116117
"wikibase-mex-icon-collapse-x-small" => false
117118
], $result );
118119
}
120+
121+
public function testCanParse_boolean_literal(): void {
122+
$jsExpressionEvaluator = new BasicJsExpressionParser( [] );
123+
124+
$parsedExpression = $jsExpressionEvaluator->parse( "false" );
125+
$result = $parsedExpression->evaluate( [] );
126+
127+
$this->assertFalse( $result );
128+
}
129+
130+
public function testCanParseBinaryExpression_withBools(): void {
131+
$jsExpressionEvaluator = new BasicJsExpressionParser( [] );
132+
133+
$parsedExpression = $jsExpressionEvaluator->parse( "false != true" );
134+
$result = $parsedExpression->evaluate( [] );
135+
136+
$this->assertTrue( $result );
137+
}
138+
139+
public function testCanParseBinaryExpression_withNumbers(): void {
140+
$jsExpressionEvaluator = new BasicJsExpressionParser( [] );
141+
142+
$parsedExpression = $jsExpressionEvaluator->parse( "3 < 4" );
143+
$result = $parsedExpression->evaluate( [] );
144+
145+
$this->assertTrue( $result );
146+
}
147+
148+
public function testCanParseBinaryExpression_withStrings(): void {
149+
$jsExpressionEvaluator = new BasicJsExpressionParser( [] );
150+
151+
$parsedExpression = $jsExpressionEvaluator->parse( "'this' == 'that'" );
152+
$result = $parsedExpression->evaluate( [] );
153+
154+
$this->assertFalse( $result );
155+
}
156+
157+
public function testParseBinaryExpressionWithComplexValues_throwsError(): void {
158+
$jsExpressionEvaluator = new BasicJsExpressionParser( [] );
159+
$this->expectException( Exception::class );
160+
161+
$parsedExpression = $jsExpressionEvaluator->parse( "3 < myvar" );
162+
$parsedExpression->evaluate( [ 'myvar' => [ 1, 2, 3 ] ] );
163+
}
164+
119165
}

tests/php/TemplatingTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,21 @@ public function testMoustacheVariableWithArrayObjectTypeSubstitution() {
362362
$this->assertSame( '<p>{"a":"b","c":"d"}</p>', $result );
363363
}
364364

365+
public function testTemplateWithBooleanExpression() {
366+
$result = $this->createAndRender(
367+
'<div><div v-if="myvar === \'myvalue\'"><p>Paragraph</p></div></div>',
368+
[ 'myvar' => 'myvalue' ]
369+
);
370+
371+
$this->assertSame( '<div><div><p>Paragraph</p></div></div>', $result );
372+
}
373+
374+
public function testTemplateWithBooleanValueInIf() {
375+
$result = $this->createAndRender( '<p><a v-if="false"></a></p>', [ 'variable' => true ] );
376+
377+
$this->assertSame( '<p></p>', $result );
378+
}
379+
365380
/**
366381
* @param string $template HTML
367382
* @param array $data

0 commit comments

Comments
 (0)