summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikic@php.net>2016-12-11 13:31:37 +0100
committerNikita Popov <nikic@php.net>2016-12-11 13:33:25 +0100
commit2c70581338257cba48991e313309839737c94c64 (patch)
treef94198f694946c00f037b340b7cc0e857f57617e
parent8b82e2c2fe2d7fa232af5e32059037f5839e06e6 (diff)
downloadphp-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.phpt47
-rw-r--r--Zend/zend_compile.c22
-rw-r--r--Zend/zend_compile.h3
-rw-r--r--Zend/zend_language_parser.y8
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); }
;