@@ -192,6 +192,7 @@ void startup_scanner(void)
192
192
CG (doc_comment) = NULL ;
193
193
CG (extra_fn_flags) = 0 ;
194
194
zend_stack_init (&SCNG (state_stack), sizeof (int ));
195
+ zend_stack_init (&SCNG (nest_location_stack), sizeof (zend_nest_location));
195
196
zend_ptr_stack_init (&SCNG (heredoc_label_stack));
196
197
SCNG (heredoc_scan_ahead) = 0 ;
197
198
}
@@ -205,6 +206,7 @@ void shutdown_scanner(void)
205
206
CG (parse_error) = 0 ;
206
207
RESET_DOC_COMMENT ();
207
208
zend_stack_destroy (&SCNG (state_stack));
209
+ zend_stack_destroy (&SCNG (nest_location_stack));
208
210
zend_ptr_stack_clean (&SCNG (heredoc_label_stack), (void (*)(void *)) &heredoc_label_dtor, 1 );
209
211
zend_ptr_stack_destroy (&SCNG (heredoc_label_stack));
210
212
SCNG (heredoc_scan_ahead) = 0 ;
@@ -223,6 +225,9 @@ ZEND_API void zend_save_lexical_state(zend_lex_state *lex_state)
223
225
lex_state->state_stack = SCNG (state_stack);
224
226
zend_stack_init (&SCNG (state_stack), sizeof (int ));
225
227
228
+ lex_state->nest_location_stack = SCNG (nest_location_stack);
229
+ zend_stack_init (&SCNG (nest_location_stack), sizeof (zend_nest_location));
230
+
226
231
lex_state->heredoc_label_stack = SCNG (heredoc_label_stack);
227
232
zend_ptr_stack_init (&SCNG (heredoc_label_stack));
228
233
@@ -258,6 +263,9 @@ ZEND_API void zend_restore_lexical_state(zend_lex_state *lex_state)
258
263
zend_stack_destroy (&SCNG (state_stack));
259
264
SCNG (state_stack) = lex_state->state_stack ;
260
265
266
+ zend_stack_destroy (&SCNG (nest_location_stack));
267
+ SCNG (nest_location_stack) = lex_state->nest_location_stack ;
268
+
261
269
zend_ptr_stack_clean (&SCNG (heredoc_label_stack), (void (*)(void *)) &heredoc_label_dtor, 1 );
262
270
zend_ptr_stack_destroy (&SCNG (heredoc_label_stack));
263
271
SCNG (heredoc_label_stack) = lex_state->heredoc_label_stack ;
@@ -1250,6 +1258,69 @@ static void copy_heredoc_label_stack(void *void_heredoc_label)
1250
1258
zend_ptr_stack_push (&SCNG (heredoc_label_stack), (void *) new_heredoc_label);
1251
1259
}
1252
1260
1261
+ /* Check that { }, [ ], ( ) are nested correctly */
1262
+ static void report_bad_nesting (char opening, int opening_lineno, char closing)
1263
+ {
1264
+ char buf[256 ];
1265
+ int used = 0 ;
1266
+
1267
+ if (opening == ' $' ) {
1268
+ used = snprintf (buf, sizeof (buf), " Unclosed '${'" );
1269
+ } else {
1270
+ used = snprintf (buf, sizeof (buf), " Unclosed '%c'" , opening);
1271
+ }
1272
+
1273
+ if (opening_lineno != CG (zend_lineno)) {
1274
+ used += snprintf (buf + used, sizeof (buf) - used, " on line %d" , opening_lineno);
1275
+ }
1276
+
1277
+ if (closing) { /* 'closing' will be 0 if at end of file */
1278
+ used += snprintf (buf + used, sizeof (buf) - used, " does not match '%c'" , closing);
1279
+ }
1280
+
1281
+ zend_throw_exception (zend_ce_parse_error, buf, 0 );
1282
+ }
1283
+
1284
+ static void enter_nesting (char *location)
1285
+ {
1286
+ zend_nest_location nest_loc = {location, CG (zend_lineno)};
1287
+ zend_stack_push (&SCNG (nest_location_stack), &nest_loc);
1288
+ }
1289
+
1290
+ static int exit_nesting (char *location)
1291
+ {
1292
+ if (zend_stack_is_empty (&SCNG (nest_location_stack))) {
1293
+ zend_throw_exception_ex (zend_ce_parse_error, 0 , " Unmatched '%c'" , *location);
1294
+ return -1 ;
1295
+ }
1296
+
1297
+ zend_nest_location *nest_loc = zend_stack_top (&SCNG (nest_location_stack));
1298
+ char opening = *nest_loc->text ;
1299
+ char closing = *location;
1300
+
1301
+ if ((opening == ' {' && closing != ' }' ) ||
1302
+ (opening == ' [' && closing != ' ]' ) ||
1303
+ (opening == ' (' && closing != ' )' ) ||
1304
+ (opening == ' $' && closing != ' }' )) { /* for ${ */
1305
+ report_bad_nesting (opening, nest_loc->lineno , closing);
1306
+ return -1 ;
1307
+ }
1308
+
1309
+ zend_stack_del_top (&SCNG (nest_location_stack));
1310
+ return 0 ;
1311
+ }
1312
+
1313
+ static int check_nesting_at_end ()
1314
+ {
1315
+ if (!zend_stack_is_empty (&SCNG (nest_location_stack))) {
1316
+ zend_nest_location *nest_loc = zend_stack_top (&SCNG (nest_location_stack));
1317
+ report_bad_nesting (*nest_loc->text , nest_loc->lineno , 0 );
1318
+ return -1 ;
1319
+ }
1320
+
1321
+ return 0 ;
1322
+ }
1323
+
1253
1324
#define PARSER_MODE () \
1254
1325
EXPECTED (elem != NULL )
1255
1326
@@ -1277,6 +1348,22 @@ static void copy_heredoc_label_stack(void *void_heredoc_label)
1277
1348
goto emit_token; \
1278
1349
} while (0 )
1279
1350
1351
+ #define RETURN_EXIT_NESTING_TOKEN (_token ) do { \
1352
+ if (exit_nesting (yytext) && PARSER_MODE ()) { \
1353
+ RETURN_TOKEN (T_ERROR); \
1354
+ } else { \
1355
+ RETURN_TOKEN (_token); \
1356
+ } \
1357
+ } while (0 )
1358
+
1359
+ #define RETURN_END_TOKEN do { \
1360
+ if (check_nesting_at_end () && PARSER_MODE ()) { \
1361
+ RETURN_TOKEN (T_ERROR); \
1362
+ } else { \
1363
+ RETURN_TOKEN (END); \
1364
+ } \
1365
+ } while (0 )
1366
+
1280
1367
int ZEND_FASTCALL lex_scan (zval *zendlval, zend_parser_stack_elem *elem)
1281
1368
{
1282
1369
int token;
@@ -1771,28 +1858,35 @@ NEWLINE ("\r"|"\n"|"\r\n")
1771
1858
}
1772
1859
1773
1860
<ST_IN_SCRIPTING>{TOKENS} {
1774
- RETURN_TOKEN (yytext[0 ]);
1861
+ if (yytext[0 ] == ' ]' || yytext[0 ] == ' )' ) {
1862
+ /* Check that ] and ) match up properly with a preceding [ or ( */
1863
+ RETURN_EXIT_NESTING_TOKEN (yytext[0 ]);
1864
+ } else {
1865
+ if (yytext[0 ] == ' [' || yytext[0 ] == ' (' ) enter_nesting (yytext);
1866
+ RETURN_TOKEN (yytext[0 ]);
1867
+ }
1775
1868
}
1776
1869
1777
1870
1778
1871
<ST_IN_SCRIPTING>" {" {
1779
1872
yy_push_state (ST_IN_SCRIPTING);
1873
+ enter_nesting (yytext);
1780
1874
RETURN_TOKEN (' {' );
1781
1875
}
1782
1876
1783
1877
1784
1878
<ST_DOUBLE_QUOTES,ST_BACKQUOTE,ST_HEREDOC>" ${" {
1785
1879
yy_push_state (ST_LOOKING_FOR_VARNAME);
1880
+ enter_nesting (yytext);
1786
1881
RETURN_TOKEN (T_DOLLAR_OPEN_CURLY_BRACES);
1787
1882
}
1788
1883
1789
-
1790
1884
<ST_IN_SCRIPTING>" }" {
1791
1885
RESET_DOC_COMMENT ();
1792
1886
if (!zend_stack_is_empty (&SCNG (state_stack))) {
1793
1887
yy_pop_state ();
1794
1888
}
1795
- RETURN_TOKEN (' }' );
1889
+ RETURN_EXIT_NESTING_TOKEN (' }' );
1796
1890
}
1797
1891
1798
1892
@@ -2088,7 +2182,7 @@ string:
2088
2182
2089
2183
<INITIAL>{ANY_CHAR} {
2090
2184
if (YYCURSOR > YYLIMIT) {
2091
- RETURN_TOKEN (END) ;
2185
+ RETURN_END_TOKEN ;
2092
2186
}
2093
2187
2094
2188
inline_char_handler:
@@ -2569,6 +2663,7 @@ skip_escape_conversion:
2569
2663
<ST_DOUBLE_QUOTES,ST_BACKQUOTE,ST_HEREDOC>"{$" {
2570
2664
yy_push_state(ST_IN_SCRIPTING);
2571
2665
yyless(1);
2666
+ enter_nesting(yytext);
2572
2667
RETURN_TOKEN(T_CURLY_OPEN);
2573
2668
}
2574
2669
@@ -2593,7 +2688,7 @@ skip_escape_conversion:
2593
2688
}
2594
2689
2595
2690
if (YYCURSOR > YYLIMIT) {
2596
- RETURN_TOKEN(END) ;
2691
+ RETURN_END_TOKEN ;
2597
2692
}
2598
2693
if (yytext[0] == '\\ ' && YYCURSOR < YYLIMIT) {
2599
2694
YYCURSOR++;
@@ -2640,7 +2735,7 @@ double_quotes_scan_done:
2640
2735
2641
2736
<ST_BACKQUOTE>{ANY_CHAR} {
2642
2737
if (YYCURSOR > YYLIMIT) {
2643
- RETURN_TOKEN(END) ;
2738
+ RETURN_END_TOKEN ;
2644
2739
}
2645
2740
if (yytext[0] == '\\ ' && YYCURSOR < YYLIMIT) {
2646
2741
YYCURSOR++;
@@ -2689,7 +2784,7 @@ double_quotes_scan_done:
2689
2784
int newline = 0, indentation = 0, spacing = 0;
2690
2785
2691
2786
if (YYCURSOR > YYLIMIT) {
2692
- RETURN_TOKEN(END) ;
2787
+ RETURN_END_TOKEN ;
2693
2788
}
2694
2789
2695
2790
YYCURSOR--;
@@ -2813,7 +2908,7 @@ heredoc_scan_done:
2813
2908
int newline = 0, indentation = 0, spacing = -1;
2814
2909
2815
2910
if (YYCURSOR > YYLIMIT) {
2816
- RETURN_TOKEN(END) ;
2911
+ RETURN_END_TOKEN ;
2817
2912
}
2818
2913
2819
2914
YYCURSOR--;
@@ -2901,7 +2996,7 @@ nowdoc_scan_done:
2901
2996
2902
2997
<ST_IN_SCRIPTING,ST_VAR_OFFSET>{ANY_CHAR} {
2903
2998
if (YYCURSOR > YYLIMIT) {
2904
- RETURN_TOKEN(END) ;
2999
+ RETURN_END_TOKEN ;
2905
3000
}
2906
3001
2907
3002
RETURN_TOKEN(T_BAD_CHARACTER);
0 commit comments