Skip to content

Commit 2e28f04

Browse files
committed
Handle truncated expressions
1 parent 46d602e commit 2e28f04

File tree

6 files changed

+74
-8
lines changed

6 files changed

+74
-8
lines changed

src/JsPhpize/JsPhpize.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ public function compile($input, $filename = null)
4747
$filename = file_exists($input) ? $input : null;
4848
$input = $filename === null ? $input : file_get_contents($filename);
4949
}
50+
51+
$start = '';
52+
$end = '';
53+
if (preg_match('/^([)}\]\s]*)(.*?)([({\[\s]*)$/', trim($input), $match)) {
54+
list(, $start, $input, $end) = $match;
55+
}
56+
5057
$parser = new Parser($this, $input, $filename);
5158
$compiler = new Compiler($this);
5259
$block = $parser->parse();
@@ -57,9 +64,10 @@ public function compile($input, $filename = null)
5764
$this->dependencies = array_merge($this->dependencies, $dependencies);
5865
$dependencies = array();
5966
}
60-
$php = $compiler->compileDependencies($dependencies) . $php;
6167

62-
return $php;
68+
$php = $compiler->compileDependencies($dependencies) . $start . $php . $end;
69+
70+
return preg_replace('/\{(\s*\}\s*\{)+$/', '{', $php);
6371
}
6472

6573
/**
@@ -148,6 +156,7 @@ public function render($input, $filename = null, array $variables = array())
148156
if (strlen($summary) > 50) {
149157
$summary = substr($summary, 0, 47) . '...';
150158
}
159+
151160
throw new Exception("An error occur in [$summary]:\n" . $e->getMessage(), 2, E_ERROR, __FILE__, __LINE__, $e);
152161
}
153162
}

src/JsPhpize/Lexer/Scanner.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,19 @@ public function scanConstant($matches)
2323
if (strpos($constant, $constPrefix) === 0) {
2424
throw new Exception('Constants cannot start with ' . $constPrefix . ', this prefix is reserved for JsPhpize' . $this->exceptionInfos(), 1);
2525
}
26+
2627
$translate = array(
2728
'Infinity' => 'INF',
2829
'NaN' => 'NAN',
2930
'undefined' => 'null',
3031
);
32+
3133
if (isset($translate[$constant])) {
3234
$constant = $translate[$constant];
3335
} elseif (substr($matches[0], 0, 5) === 'Math.') {
3436
$constant = 'M_' . substr($constant, 5);
3537
}
38+
3639
$this->consume($matches[0]);
3740

3841
return $this->token('constant', $constant);

src/JsPhpize/Parser/Parser.php

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,20 +77,24 @@ protected function parseParentheses()
7777

7878
return $parentheses;
7979
}
80+
8081
if ($expectComma) {
8182
if ($token->isIn(',', ';')) {
8283
$expectComma = false;
8384

8485
continue;
8586
}
87+
8688
throw $this->unexpected($token);
8789
}
90+
8891
if ($value = $this->getValueFromToken($token)) {
8992
$expectComma = true;
9093
$parentheses->addNode($value);
9194

9295
continue;
9396
}
97+
9498
throw $this->unexpected($token);
9599
}
96100

@@ -106,20 +110,24 @@ protected function parseHooksArray()
106110
if ($token->is(']')) {
107111
return $array;
108112
}
113+
109114
if ($expectComma) {
110115
if ($token->is(',')) {
111116
$expectComma = false;
112117

113118
continue;
114119
}
120+
115121
throw $this->unexpected($token);
116122
}
123+
117124
if ($value = $this->getValueFromToken($token)) {
118125
$expectComma = true;
119126
$array->addItem($value);
120127

121128
continue;
122129
}
130+
123131
throw $this->unexpected($token);
124132
}
125133

@@ -135,21 +143,25 @@ protected function parseBracketsArray()
135143
if ($token->is('}')) {
136144
return $array;
137145
}
146+
138147
if ($expectComma) {
139148
if ($token->is(',')) {
140149
$expectComma = false;
141150

142151
continue;
143152
}
153+
144154
throw $this->unexpected($token);
145155
}
156+
146157
if ($pair = $this->getBracketsArrayItemKeyFromToken($token)) {
147158
list($key, $value) = $pair;
148159
$expectComma = true;
149160
$array->addItem($key, $value);
150161

151162
continue;
152163
}
164+
153165
throw $this->unexpected($token);
154166
}
155167

@@ -198,15 +210,18 @@ protected function parseTernary(Node $condition)
198210
if (!$next) {
199211
throw new Exception("Ternary expression not properly closed after '?' " . $this->exceptionInfos(), 14);
200212
}
213+
201214
if (!$next->is(':')) {
202215
throw new Exception("':' expected but " . ($next->value ?: $next->type) . ' given ' . $this->exceptionInfos(), 15);
203216
}
217+
204218
$next = $this->next();
205219
if (!$next) {
206220
throw new Exception("Ternary expression not properly closed after ':' " . $this->exceptionInfos(), 16);
207221
}
222+
208223
$falseValue = $this->expectValue($next);
209-
$next = $this->get(0);
224+
$this->get(0);
210225

211226
return new Ternary($condition, $trueValue, $falseValue);
212227
}
@@ -226,15 +241,18 @@ protected function parseFunction($token)
226241
$this->skip();
227242
$token = $this->get(0);
228243
}
244+
229245
if (!$token->is('(')) {
230246
throw $this->unexpected($token);
231247
}
248+
232249
$this->skip();
233250
$function->setValue($this->parseParentheses());
234251
$token = $this->get(0);
235252
if (!$token->is('{')) {
236253
throw $this->unexpected($token);
237254
}
255+
238256
$this->skip();
239257
$this->parseBlock($function);
240258
$this->skip();
@@ -294,23 +312,31 @@ protected function parseInstructions($block)
294312
if ($token->is($endToken)) {
295313
break;
296314
}
315+
297316
if ($token->type === 'keyword') {
298317
if ($token->isIn('var', 'const')) {
299318
continue;
300319
}
320+
301321
if ($token->value === 'let') {
302322
$block->let($this->parseLet($token));
323+
303324
continue;
304325
}
305326
}
327+
306328
if ($instruction = $this->getInstructionFromToken($token)) {
307329
$block->addInstruction($instruction);
330+
308331
continue;
309332
}
333+
310334
if ($token->is(';')) {
311335
$block->endInstruction();
336+
312337
continue;
313338
}
339+
314340
throw $this->unexpected($token);
315341
}
316342
}

src/JsPhpize/Parser/TokenExtractor.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ protected function getBracketsArrayItemKeyFromToken($token)
1919
if (!$token) {
2020
throw new Exception('Missing value after ' . $value . $this->exceptionInfos(), 12);
2121
}
22+
2223
if (!$token->is(':')) {
2324
throw $this->unexpected($token);
2425
}
26+
2527
$key = new Constant($type, $value);
2628
$value = $this->expectValue($this->next());
2729

@@ -136,25 +138,29 @@ protected function appendFunctionsCalls(&$value)
136138
if ($token->is('{') || $token->expectNoLeftMember()) {
137139
throw $this->unexpected($this->next());
138140
}
141+
139142
if ($token->is('?')) {
140143
$this->skip();
141144
$value = $this->parseTernary($value);
142145

143146
continue;
144147
}
148+
145149
if ($token->is('(')) {
146150
$this->skip();
147151
$arguments = array();
148152
$value = new FunctionCall($value, $this->parseParentheses()->nodes);
149153

150154
continue;
151155
}
156+
152157
if ($token->isOperator()) {
153158
if ($token->isIn('++', '--')) {
154159
$value->append($this->next()->type);
155160

156161
break;
157162
}
163+
158164
if ($token->isAssignation()) {
159165
$this->skip();
160166
$arguments = array();
@@ -167,7 +173,7 @@ protected function appendFunctionsCalls(&$value)
167173
$this->skip();
168174
$nextValue = $this->expectValue($this->next());
169175
$value = new Dyiade($token->type, $value, $nextValue);
170-
$token = $this->get(0);
176+
$this->get(0);
171177

172178
continue;
173179
}
@@ -182,8 +188,10 @@ protected function expectValue($next, $token = null)
182188
if ($token) {
183189
throw $this->unexpected($token);
184190
}
191+
185192
throw new Exception('Value expected after ' . $this->exceptionInfos(), 20);
186193
}
194+
187195
$value = $this->getValueFromToken($next);
188196
if (!$value) {
189197
throw $this->unexpected($next);

tests/badSyntaxes.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class BadSyntaxesTest extends \PHPUnit_Framework_TestCase
1111
public function testNoParenthesesClose()
1212
{
1313
$jsPhpize = new JsPhpize();
14-
$jsPhpize->render('(');
14+
$jsPhpize->render('if ( {}');
1515
}
1616

1717
/**
@@ -21,7 +21,7 @@ public function testNoParenthesesClose()
2121
public function testNoHookClose()
2222
{
2323
$jsPhpize = new JsPhpize();
24-
$jsPhpize->render('[');
24+
$jsPhpize->render('a = [1,');
2525
}
2626

2727
/**
@@ -41,7 +41,7 @@ public function testBracketMissingAfterKey()
4141
public function testNoBracketClose()
4242
{
4343
$jsPhpize = new JsPhpize();
44-
$jsPhpize->render('{');
44+
$jsPhpize->render('a = {a: "b",');
4545
}
4646

4747
/**
@@ -51,7 +51,7 @@ public function testNoBracketClose()
5151
public function testValueExpected()
5252
{
5353
$jsPhpize = new JsPhpize();
54-
$jsPhpize->render('a[');
54+
$jsPhpize->render('a =');
5555
}
5656

5757
/**

tests/compile.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,26 @@ public function testCompileWithoutDependencies()
4747
$this->assertSame($expected, $actual);
4848
}
4949

50+
public function testTruncatedCode()
51+
{
52+
$jsPhpize = new JsPhpize(array(
53+
'catchDependencies' => true,
54+
));
55+
$result = $jsPhpize->compileCode('} else {');
56+
57+
$expected = str_replace("\r", '', trim("} else {"));
58+
$actual = str_replace("\r", '', trim($result));
59+
60+
$this->assertSame($expected, $actual);
61+
62+
$result = $jsPhpize->compileCode('}');
63+
64+
$expected = str_replace("\r", '', trim("}"));
65+
$actual = str_replace("\r", '', trim($result));
66+
67+
$this->assertSame($expected, $actual);
68+
}
69+
5070
/**
5171
* @expectedException \JsPhpize\Compiler\Exception
5272
* @expectedExceptionCode 1111111

0 commit comments

Comments
 (0)