diff options
| author | Nikita Popov <nikic@php.net> | 2016-12-11 13:31:37 +0100 |
|---|---|---|
| committer | Nikita Popov <nikic@php.net> | 2016-12-11 13:33:25 +0100 |
| commit | 2c70581338257cba48991e313309839737c94c64 (patch) | |
| tree | f94198f694946c00f037b340b7cc0e857f57617e | |
| parent | 8b82e2c2fe2d7fa232af5e32059037f5839e06e6 (diff) | |
| download | php-git-2c70581338257cba48991e313309839737c94c64.tar.gz | |
Fix T_NUM_STRING negation
T_NUM_STRING follows the rules of symtable numeric string
conversion. If the offset isn't an integer under those rules, it
is treated as a string. This should apply to negated T_NUM_STRINGs
as well.
| -rw-r--r-- | Zend/tests/neg_num_string.phpt | 47 | ||||
| -rw-r--r-- | Zend/zend_compile.c | 22 | ||||
| -rw-r--r-- | Zend/zend_compile.h | 3 | ||||
| -rw-r--r-- | Zend/zend_language_parser.y | 8 |
4 files changed, 76 insertions, 4 deletions
diff --git a/Zend/tests/neg_num_string.phpt b/Zend/tests/neg_num_string.phpt new file mode 100644 index 0000000000..018568adea --- /dev/null +++ b/Zend/tests/neg_num_string.phpt @@ -0,0 +1,47 @@ +--TEST-- +Test edge-cases for negative num strings in interpolated string offsets +--FILE-- +<?php + +$a = [ + "0" => 1, + "-0" => 2, + "1" => 3, + "-1" => 4, + "0x0" => 5, + "-0x0" => 6, + "00" => 7, + "-00" => 8, + "9223372036854775808" => 9, + "-9223372036854775808" => 10, + "2147483648" => 11, + "-2147483648" => 12, +]; + +var_dump("$a[0]"); +var_dump("$a[-0]"); +var_dump("$a[1]"); +var_dump("$a[-1]"); +var_dump("$a[0x0]"); +var_dump("$a[-0x0]"); +var_dump("$a[00]"); +var_dump("$a[-00]"); +var_dump("$a[9223372036854775808]"); +var_dump("$a[-9223372036854775808]"); +var_dump("$a[2147483648]"); +var_dump("$a[-2147483648]"); + +?> +--EXPECT-- +string(1) "1" +string(1) "2" +string(1) "3" +string(1) "4" +string(1) "5" +string(1) "6" +string(1) "7" +string(1) "8" +string(1) "9" +string(2) "10" +string(2) "11" +string(2) "12" diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 2a8980f23b..c8f2eefa7d 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1830,6 +1830,28 @@ zend_ast *zend_ast_append_str(zend_ast *left_ast, zend_ast *right_ast) /* {{{ */ } /* }}} */ +zend_ast *zend_negate_num_string(zend_ast *ast) /* {{{ */ +{ + zval *zv = zend_ast_get_zval(ast); + if (Z_TYPE_P(zv) == IS_LONG) { + if (Z_LVAL_P(zv) == 0) { + ZVAL_NEW_STR(zv, zend_string_init("-0", sizeof("-0")-1, 0)); + } else { + ZEND_ASSERT(Z_LVAL_P(zv) > 0); + Z_LVAL_P(zv) *= -1; + } + } else if (Z_TYPE_P(zv) == IS_STRING) { + size_t orig_len = Z_STRLEN_P(zv); + zend_string_extend(Z_STR_P(zv), orig_len + 1, 0); + memmove(Z_STRVAL_P(zv) + 1, Z_STRVAL_P(zv), orig_len + 1); + Z_STRVAL_P(zv)[0] = '-'; + } else { + ZEND_ASSERT(0); + } + return ast; +} +/* }}} */ + void zend_verify_namespace(void) /* {{{ */ { if (FC(has_bracketed_namespaces) && !FC(in_namespace)) { diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 422404d0a3..976e79f7e2 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -726,7 +726,10 @@ ZEND_API binary_op_type get_binary_op(int opcode); void zend_stop_lexing(void); void zend_emit_final_return(int return_one); + +/* Used during AST construction */ zend_ast *zend_ast_append_str(zend_ast *left, zend_ast *right); +zend_ast *zend_negate_num_string(zend_ast *ast); uint32_t zend_add_class_modifier(uint32_t flags, uint32_t new_flag); uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag); void zend_handle_encoding_declaration(zend_ast *ast); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 3c7117930e..2373a7e3f3 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -1247,10 +1247,10 @@ encaps_var: ; encaps_var_offset: - T_STRING { $$ = $1; } - | T_NUM_STRING { $$ = $1; } - | '-' T_NUM_STRING { $$ = zend_ast_create(ZEND_AST_UNARY_MINUS, $2); } - | T_VARIABLE { $$ = zend_ast_create(ZEND_AST_VAR, $1); } + T_STRING { $$ = $1; } + | T_NUM_STRING { $$ = $1; } + | '-' T_NUM_STRING { $$ = zend_negate_num_string($2); } + | T_VARIABLE { $$ = zend_ast_create(ZEND_AST_VAR, $1); } ; |
