@@ -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
+ size_t 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;
@@ -1297,7 +1384,7 @@ BNUM "0b"[01]+(_[01]+)*
1297
1384
LABEL [a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*
1298
1385
WHITESPACE [ \n\r\t]+
1299
1386
TABS_AND_SPACES [ \t]*
1300
- TOKENS [;:,.\[\]() |^&+-/*=%!~$<>?@]
1387
+ TOKENS [;:,.|^&+-/*=%!~$<>?@]
1301
1388
ANY_CHAR [^]
1302
1389
NEWLINE ("\r"|"\n"|"\r\n")
1303
1390
@@ -1770,29 +1857,40 @@ NEWLINE ("\r"|"\n"|"\r\n")
1770
1857
RETURN_TOKEN (T_SR);
1771
1858
}
1772
1859
1860
+ <ST_IN_SCRIPTING>" ]" |" )" {
1861
+ /* Check that ] and ) match up properly with a preceding [ or ( */
1862
+ RETURN_EXIT_NESTING_TOKEN (yytext[0 ]);
1863
+ }
1864
+
1865
+ <ST_IN_SCRIPTING>" [" |" (" {
1866
+ enter_nesting (yytext);
1867
+ RETURN_TOKEN (yytext[0 ]);
1868
+ }
1869
+
1773
1870
<ST_IN_SCRIPTING>{TOKENS} {
1774
1871
RETURN_TOKEN (yytext[0 ]);
1775
1872
}
1776
1873
1777
1874
1778
1875
<ST_IN_SCRIPTING>" {" {
1779
1876
yy_push_state (ST_IN_SCRIPTING);
1877
+ enter_nesting (yytext);
1780
1878
RETURN_TOKEN (' {' );
1781
1879
}
1782
1880
1783
1881
1784
1882
<ST_DOUBLE_QUOTES,ST_BACKQUOTE,ST_HEREDOC>" ${" {
1785
1883
yy_push_state (ST_LOOKING_FOR_VARNAME);
1884
+ enter_nesting (yytext);
1786
1885
RETURN_TOKEN (T_DOLLAR_OPEN_CURLY_BRACES);
1787
1886
}
1788
1887
1789
-
1790
1888
<ST_IN_SCRIPTING>" }" {
1791
1889
RESET_DOC_COMMENT ();
1792
1890
if (!zend_stack_is_empty (&SCNG (state_stack))) {
1793
1891
yy_pop_state ();
1794
1892
}
1795
- RETURN_TOKEN (' }' );
1893
+ RETURN_EXIT_NESTING_TOKEN (' }' );
1796
1894
}
1797
1895
1798
1896
@@ -2088,7 +2186,7 @@ string:
2088
2186
2089
2187
<INITIAL>{ANY_CHAR} {
2090
2188
if (YYCURSOR > YYLIMIT) {
2091
- RETURN_TOKEN (END) ;
2189
+ RETURN_END_TOKEN ;
2092
2190
}
2093
2191
2094
2192
inline_char_handler:
@@ -2165,7 +2263,7 @@ inline_char_handler:
2165
2263
RETURN_TOKEN (' ]' );
2166
2264
}
2167
2265
2168
- <ST_VAR_OFFSET>{TOKENS}|[{}" `] {
2266
+ <ST_VAR_OFFSET>{TOKENS}|[[() {}" `] {
2169
2267
/* Only '[' or '-' can be valid, but returning other tokens will allow a more explicit parse error */
2170
2268
RETURN_TOKEN(yytext[0]);
2171
2269
}
@@ -2569,6 +2667,7 @@ skip_escape_conversion:
2569
2667
<ST_DOUBLE_QUOTES,ST_BACKQUOTE,ST_HEREDOC>"{$" {
2570
2668
yy_push_state(ST_IN_SCRIPTING);
2571
2669
yyless(1);
2670
+ enter_nesting(yytext);
2572
2671
RETURN_TOKEN(T_CURLY_OPEN);
2573
2672
}
2574
2673
@@ -2593,7 +2692,7 @@ skip_escape_conversion:
2593
2692
}
2594
2693
2595
2694
if (YYCURSOR > YYLIMIT) {
2596
- RETURN_TOKEN(END) ;
2695
+ RETURN_END_TOKEN ;
2597
2696
}
2598
2697
if (yytext[0] == '\\ ' && YYCURSOR < YYLIMIT) {
2599
2698
YYCURSOR++;
@@ -2640,7 +2739,7 @@ double_quotes_scan_done:
2640
2739
2641
2740
<ST_BACKQUOTE>{ANY_CHAR} {
2642
2741
if (YYCURSOR > YYLIMIT) {
2643
- RETURN_TOKEN(END) ;
2742
+ RETURN_END_TOKEN ;
2644
2743
}
2645
2744
if (yytext[0] == '\\ ' && YYCURSOR < YYLIMIT) {
2646
2745
YYCURSOR++;
@@ -2689,7 +2788,7 @@ double_quotes_scan_done:
2689
2788
int newline = 0, indentation = 0, spacing = 0;
2690
2789
2691
2790
if (YYCURSOR > YYLIMIT) {
2692
- RETURN_TOKEN(END) ;
2791
+ RETURN_END_TOKEN ;
2693
2792
}
2694
2793
2695
2794
YYCURSOR--;
@@ -2813,7 +2912,7 @@ heredoc_scan_done:
2813
2912
int newline = 0, indentation = 0, spacing = -1;
2814
2913
2815
2914
if (YYCURSOR > YYLIMIT) {
2816
- RETURN_TOKEN(END) ;
2915
+ RETURN_END_TOKEN ;
2817
2916
}
2818
2917
2819
2918
YYCURSOR--;
@@ -2901,7 +3000,7 @@ nowdoc_scan_done:
2901
3000
2902
3001
<ST_IN_SCRIPTING,ST_VAR_OFFSET>{ANY_CHAR} {
2903
3002
if (YYCURSOR > YYLIMIT) {
2904
- RETURN_TOKEN(END) ;
3003
+ RETURN_END_TOKEN ;
2905
3004
}
2906
3005
2907
3006
RETURN_TOKEN(T_BAD_CHARACTER);
0 commit comments