summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2016-04-21 10:15:57 +0300
committerDmitry Stogov <dmitry@zend.com>2016-04-21 10:15:57 +0300
commitc026ca6cdccc74ca78d8b0ab203281c19b93cdde (patch)
treeb74b4d46fba2974ac44b5d4492912e8f6b797cbe
parent650c1c0a7d94d3bb052a93407b6e280df9c265a4 (diff)
parentecf6392243b90e6c8f5597076b11b4959bac9dc1 (diff)
downloadphp-git-c026ca6cdccc74ca78d8b0ab203281c19b93cdde.tar.gz
Merge branch 'master' into safe_timeout
* master: Fixed bug #72059 - Reference the bug id directly in NEWS Fix magic constants (__LINE__) with ?? for constant scalar exprs Add NEWS entry for ?? in constant scalar expressions allow null coalescing (??) on constant expressions Fix intl tests for ICU < 52 Fix ZTS builds by adding a missing TSRMLS_FETCH(). Fix bug #71737 Update NEWS Fix RECV opcode to handle all kinds of exceptions Fix RECV opcode to handle all kinds of exceptions
-rw-r--r--Zend/tests/bug41117_1.phpt3
-rw-r--r--Zend/tests/bug71737.phpt16
-rw-r--r--Zend/tests/bug72057.phpt21
-rw-r--r--Zend/tests/constant_expressions_coalesce.phpt47
-rw-r--r--Zend/tests/constant_expressions_coalesce_empty_dim.phpt11
-rw-r--r--Zend/tests/constant_expressions_dynamic.phpt5
-rw-r--r--Zend/zend_ast.c39
-rw-r--r--Zend/zend_compile.c39
-rw-r--r--Zend/zend_compile.h2
-rw-r--r--Zend/zend_execute.c6
-rw-r--r--Zend/zend_execute.h1
-rw-r--r--Zend/zend_vm_def.h4
-rw-r--r--Zend/zend_vm_execute.h4
-rw-r--r--ext/intl/tests/timezone_IDforWindowsID_basic.phpt2
-rw-r--r--ext/intl/tests/timezone_windowsID_basic.phpt2
15 files changed, 186 insertions, 16 deletions
diff --git a/Zend/tests/bug41117_1.phpt b/Zend/tests/bug41117_1.phpt
index f555b637ad..a612f07fed 100644
--- a/Zend/tests/bug41117_1.phpt
+++ b/Zend/tests/bug41117_1.phpt
@@ -10,5 +10,4 @@ class foo {
$obj = new foo("Hello world");
?>
--EXPECTF--
-Fatal error: Cannot re-assign $this in %sbug41117_1.php on line 3
-
+Fatal error: Cannot use $this as parameter in %s on line %d
diff --git a/Zend/tests/bug71737.phpt b/Zend/tests/bug71737.phpt
new file mode 100644
index 0000000000..b44de8e78e
--- /dev/null
+++ b/Zend/tests/bug71737.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Bug #71737: Memory leak in closure with parameter named $this
+--FILE--
+<?php
+
+class Test {
+ public function method() {
+ return function($this) {};
+ }
+}
+
+(new Test)->method()(new stdClass);
+
+?>
+--EXPECTF--
+Fatal error: Cannot use $this as parameter in %s on line %d
diff --git a/Zend/tests/bug72057.phpt b/Zend/tests/bug72057.phpt
new file mode 100644
index 0000000000..e1a129bbc2
--- /dev/null
+++ b/Zend/tests/bug72057.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Bug #72057 (PHP hangs when user error handler throws exception after Notice from type coercion)
+--FILE--
+<?php
+
+set_error_handler(
+ function() {
+ throw new Exception("My custom error");
+ }
+);
+
+(function (int $i) { bar(); })("7as");
+
+--EXPECTF--
+
+Fatal error: Uncaught Exception: My custom error in %s:%d
+Stack trace:
+#0 %s(%d): {closure}(8, 'A non well form...', '%s', %d, Array)
+#1 %s(%d): {closure}('7as')
+#2 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/constant_expressions_coalesce.phpt b/Zend/tests/constant_expressions_coalesce.phpt
new file mode 100644
index 0000000000..425aba69c4
--- /dev/null
+++ b/Zend/tests/constant_expressions_coalesce.phpt
@@ -0,0 +1,47 @@
+--TEST--
+Constant expressions with null coalescing operator ??
+--FILE--
+<?php
+
+const A = [1 => [[]]];
+
+const T_1 = null ?? A[1]['undefined']['index'] ?? 1;
+const T_2 = null ?? A['undefined']['index'] ?? 2;
+const T_3 = null ?? A[1][0][2] ?? 3;
+const T_4 = A[1][0][2] ?? 4;
+const T_5 = null ?? __LINE__;
+const T_6 = __LINE__ ?? "bar";
+
+var_dump(T_1);
+var_dump(T_2);
+var_dump(T_3);
+var_dump(T_4);
+var_dump(T_5);
+var_dump(T_6);
+
+var_dump((function(){ static $var = null ?? A[1]['undefined']['index'] ?? 1; return $var; })());
+var_dump((function(){ static $var = null ?? A['undefined']['index'] ?? 2; return $var; })());
+var_dump((function(){ static $var = null ?? A[1][0][2] ?? 3; return $var; })());
+var_dump((function(){ static $var = A[1][0][2] ?? 4; return $var; })());
+
+var_dump((new class { public $var = null ?? A[1]['undefined']['index'] ?? 1; })->var);
+var_dump((new class { public $var = null ?? A['undefined']['index'] ?? 2; })->var);
+var_dump((new class { public $var = null ?? A[1][0][2] ?? 3; })->var);
+var_dump((new class { public $var = A[1][0][2] ?? 4; })->var);
+
+?>
+--EXPECTF--
+int(1)
+int(2)
+int(3)
+int(4)
+int(%d)
+int(%d)
+int(1)
+int(2)
+int(3)
+int(4)
+int(1)
+int(2)
+int(3)
+int(4)
diff --git a/Zend/tests/constant_expressions_coalesce_empty_dim.phpt b/Zend/tests/constant_expressions_coalesce_empty_dim.phpt
new file mode 100644
index 0000000000..56ee43b789
--- /dev/null
+++ b/Zend/tests/constant_expressions_coalesce_empty_dim.phpt
@@ -0,0 +1,11 @@
+--TEST--
+Constant expressions with empty dimension fetch on coalesce
+--FILE--
+<?php
+
+const A = [][] ?? 1;
+
+?>
+--EXPECTF--
+Fatal error: Cannot use [] for reading in %s.php on line %d
+
diff --git a/Zend/tests/constant_expressions_dynamic.phpt b/Zend/tests/constant_expressions_dynamic.phpt
index b0ba3a5b19..686dcc1d11 100644
--- a/Zend/tests/constant_expressions_dynamic.phpt
+++ b/Zend/tests/constant_expressions_dynamic.phpt
@@ -35,10 +35,12 @@ const T_19 = [
false => false,
true => true,
];
+eval("const T_20x = 'a';");
+const T_20 = null ?: (T_20x . 'bc');
var_dump(
T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9, T_10,
- T_11, T_12, T_13, T_14, T_15, T_16, T_17, T_18, T_19
+ T_11, T_12, T_13, T_14, T_15, T_16, T_17, T_18, T_19, T_20
);
?>
@@ -77,3 +79,4 @@ array(6) {
[1]=>
bool(true)
}
+string(3) "abc"
diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c
index cfcd636269..4d3678f0b6 100644
--- a/Zend/zend_ast.c
+++ b/Zend/zend_ast.c
@@ -337,6 +337,30 @@ ZEND_API int zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *sc
zval_dtor(&op1);
}
break;
+ case ZEND_AST_COALESCE:
+ if (ast->child[0]->kind == ZEND_AST_DIM) {
+ ast->child[0]->attr = ZEND_DIM_IS;
+ }
+
+ if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
+ ret = FAILURE;
+ break;
+ }
+ if (Z_TYPE(op1) > IS_NULL) {
+ *result = op1;
+ } else {
+ if (ast->child[1]->kind == ZEND_AST_DIM) {
+ ast->child[1]->attr = ZEND_DIM_IS;
+ }
+
+ if (UNEXPECTED(zend_ast_evaluate(result, ast->child[1], scope) != SUCCESS)) {
+ zval_dtor(&op1);
+ ret = FAILURE;
+ break;
+ }
+ zval_dtor(&op1);
+ }
+ break;
case ZEND_AST_UNARY_PLUS:
if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
@@ -385,6 +409,14 @@ ZEND_API int zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *sc
}
break;
case ZEND_AST_DIM:
+ if (ast->child[1] == NULL) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use [] for reading");
+ }
+
+ if (ast->attr == ZEND_DIM_IS && ast->child[0]->kind == ZEND_AST_DIM) {
+ ast->child[0]->attr = ZEND_DIM_IS;
+ }
+
if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
} else if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[1], scope) != SUCCESS)) {
@@ -393,7 +425,12 @@ ZEND_API int zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *sc
} else {
zval tmp;
- zend_fetch_dimension_by_zval(&tmp, &op1, &op2);
+ if (ast->attr == ZEND_DIM_IS) {
+ zend_fetch_dimension_by_zval_is(&tmp, &op1, &op2, IS_CONST);
+ } else {
+ zend_fetch_dimension_by_zval(&tmp, &op1, &op2);
+ }
+
if (UNEXPECTED(Z_ISREF(tmp))) {
ZVAL_DUP(result, Z_REFVAL(tmp));
} else {
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index fe9139b609..af2d2e95cb 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -4903,8 +4903,9 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
zend_error_noreturn(E_COMPILE_ERROR, "Redefinition of parameter $%s",
ZSTR_VAL(name));
} else if (zend_string_equals_literal(name, "this")) {
- if (op_array->scope && (op_array->fn_flags & ZEND_ACC_STATIC) == 0) {
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this");
+ if ((op_array->scope || (op_array->fn_flags & ZEND_ACC_CLOSURE))
+ && (op_array->fn_flags & ZEND_ACC_STATIC) == 0) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as parameter");
}
op_array->this_var = var_node.u.op.var;
}
@@ -7313,7 +7314,7 @@ zend_bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */
|| kind == ZEND_AST_CONDITIONAL || kind == ZEND_AST_DIM
|| kind == ZEND_AST_ARRAY || kind == ZEND_AST_ARRAY_ELEM
|| kind == ZEND_AST_CONST || kind == ZEND_AST_CLASS_CONST
- || kind == ZEND_AST_MAGIC_CONST;
+ || kind == ZEND_AST_MAGIC_CONST || kind == ZEND_AST_COALESCE;
}
/* }}} */
@@ -7447,14 +7448,16 @@ void zend_const_expr_to_zval(zval *result, zend_ast *ast) /* {{{ */
zend_compile_const_expr(&ast);
if (ast->kind == ZEND_AST_ZVAL) {
ZVAL_COPY_VALUE(result, zend_ast_get_zval(ast));
-
- /* Kill this branch of the original AST, as it was already destroyed.
- * It would be nice to find a better solution to this problem in the
- * future. */
- orig_ast->kind = 0;
} else {
ZVAL_NEW_AST(result, zend_ast_copy(ast));
+ /* destroy the ast here, it might have been replaced */
+ zend_ast_destroy(ast);
}
+
+ /* Kill this branch of the original AST, as it was already destroyed.
+ * It would be nice to find a better solution to this problem in the
+ * future. */
+ orig_ast->kind = 0;
}
/* }}} */
@@ -7865,6 +7868,26 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
return;
}
break;
+ case ZEND_AST_COALESCE:
+ zend_eval_const_expr(&ast->child[0]);
+
+ if (ast->child[0]->kind != ZEND_AST_ZVAL) {
+ /* ensure everything was compile-time evaluated at least once */
+ zend_eval_const_expr(&ast->child[1]);
+ return;
+ }
+
+ if (Z_TYPE_P(zend_ast_get_zval(ast->child[0])) == IS_NULL) {
+ zend_eval_const_expr(&ast->child[1]);
+ *ast_ptr = ast->child[1];
+ ast->child[1] = NULL;
+ zend_ast_destroy(ast);
+ } else {
+ *ast_ptr = ast->child[0];
+ ast->child[0] = NULL;
+ zend_ast_destroy(ast);
+ }
+ return;
case ZEND_AST_CONDITIONAL:
{
zend_ast **child, *child_ast;
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index 8fe291f443..20a622f4d8 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -896,6 +896,8 @@ ZEND_API void zend_assert_valid_class_name(const zend_string *const_name);
#define ZEND_SEND_BY_REF 1
#define ZEND_SEND_PREFER_REF 2
+#define ZEND_DIM_IS 1
+
static zend_always_inline int zend_check_arg_send_type(const zend_function *zf, uint32_t arg_num, uint32_t mask)
{
arg_num--;
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index be4b039d89..68fc5e7f5d 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -1938,6 +1938,12 @@ ZEND_API void zend_fetch_dimension_by_zval(zval *result, zval *container, zval *
zend_fetch_dimension_address_read_R(result, container, dim, IS_TMP_VAR);
}
+ZEND_API void zend_fetch_dimension_by_zval_is(zval *result, zval *container, zval *dim, int dim_type)
+{
+ zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_IS, 1);
+}
+
+
static zend_always_inline void zend_fetch_property_address(zval *result, zval *container, uint32_t container_op_type, zval *prop_ptr, uint32_t prop_op_type, void **cache_slot, int type)
{
if (container_op_type != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) {
diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h
index 738895e73c..b2163c7d25 100644
--- a/Zend/zend_execute.h
+++ b/Zend/zend_execute.h
@@ -294,6 +294,7 @@ ZEND_API zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, con
void zend_verify_abstract_class(zend_class_entry *ce);
ZEND_API void zend_fetch_dimension_by_zval(zval *result, zval *container, zval *dim);
+ZEND_API void zend_fetch_dimension_by_zval_is(zval *result, zval *container, zval *dim, int dim_type);
ZEND_API zval* zend_get_compiled_variable_value(const zend_execute_data *execute_data_ptr, uint32_t var);
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index 49d6b20516..99494cff2a 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -4670,7 +4670,7 @@ ZEND_VM_HANDLER(63, ZEND_RECV, NUM, ANY)
zval *param = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->result.var);
SAVE_OPLINE();
- if (UNEXPECTED(!zend_verify_arg_type(EX(func), arg_num, param, NULL, CACHE_ADDR(opline->op2.num)))) {
+ if (UNEXPECTED(!zend_verify_arg_type(EX(func), arg_num, param, NULL, CACHE_ADDR(opline->op2.num)) || EG(exception))) {
HANDLE_EXCEPTION();
}
}
@@ -4707,7 +4707,7 @@ ZEND_VM_HANDLER(64, ZEND_RECV_INIT, NUM, CONST)
zval *default_value = EX_CONSTANT(opline->op2);
SAVE_OPLINE();
- if (UNEXPECTED(!zend_verify_arg_type(EX(func), arg_num, param, default_value, CACHE_ADDR(Z_CACHE_SLOT_P(default_value))))) {
+ if (UNEXPECTED(!zend_verify_arg_type(EX(func), arg_num, param, default_value, CACHE_ADDR(Z_CACHE_SLOT_P(default_value))) || EG(exception))) {
HANDLE_EXCEPTION();
}
}
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 99e2333644..4aa47ba299 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -1498,7 +1498,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_SPEC_HANDLER(ZEND_OPCODE_
zval *param = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->result.var);
SAVE_OPLINE();
- if (UNEXPECTED(!zend_verify_arg_type(EX(func), arg_num, param, NULL, CACHE_ADDR(opline->op2.num)))) {
+ if (UNEXPECTED(!zend_verify_arg_type(EX(func), arg_num, param, NULL, CACHE_ADDR(opline->op2.num)) || EG(exception))) {
HANDLE_EXCEPTION();
}
}
@@ -2308,7 +2308,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_INIT_SPEC_CONST_HANDLER(Z
zval *default_value = EX_CONSTANT(opline->op2);
SAVE_OPLINE();
- if (UNEXPECTED(!zend_verify_arg_type(EX(func), arg_num, param, default_value, CACHE_ADDR(Z_CACHE_SLOT_P(default_value))))) {
+ if (UNEXPECTED(!zend_verify_arg_type(EX(func), arg_num, param, default_value, CACHE_ADDR(Z_CACHE_SLOT_P(default_value))) || EG(exception))) {
HANDLE_EXCEPTION();
}
}
diff --git a/ext/intl/tests/timezone_IDforWindowsID_basic.phpt b/ext/intl/tests/timezone_IDforWindowsID_basic.phpt
index 2fab10fdd7..4127d8e31c 100644
--- a/ext/intl/tests/timezone_IDforWindowsID_basic.phpt
+++ b/ext/intl/tests/timezone_IDforWindowsID_basic.phpt
@@ -4,6 +4,8 @@ IntlTimeZone::getIDForWindowsID basic test
<?php
if (!extension_loaded('intl'))
die('skip intl extension not enabled');
+if (version_compare(INTL_ICU_VERSION, '52') < 0)
+ die('skip for ICU >= 52');
--FILE--
<?php
diff --git a/ext/intl/tests/timezone_windowsID_basic.phpt b/ext/intl/tests/timezone_windowsID_basic.phpt
index 8a9fcfe95f..dd48f016e2 100644
--- a/ext/intl/tests/timezone_windowsID_basic.phpt
+++ b/ext/intl/tests/timezone_windowsID_basic.phpt
@@ -4,6 +4,8 @@ IntlTimeZone::getWindowsID basic test
<?php
if (!extension_loaded('intl'))
die('skip intl extension not enabled');
+if (version_compare(INTL_ICU_VERSION, '52') < 0)
+ die('skip for ICU >= 52');
--FILE--
<?php