From 16a9bc1ec20533c76ba992bfc64dd69e7b7d9001 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 27 Apr 2015 21:14:58 +0200 Subject: [PATCH] Disallow self etc outside classes at compile-time Also fix a bug with return types where "self" was rejected inside a class, but not on a method. Fallout: A couple of tests changed to more generic error messages. --- .../tests/class_name_as_scalar_error_005.phpt | 2 +- .../tests/class_name_as_scalar_error_006.phpt | 2 +- .../tests/class_name_as_scalar_error_007.phpt | 2 +- Zend/tests/return_types/024.phpt | 2 +- Zend/tests/return_types/025.phpt | 2 +- Zend/tests/return_types/026.phpt | 2 +- Zend/tests/return_types/027.phpt | 2 +- .../tests/self_class_const_outside_class.phpt | 2 +- .../typehints/self_on_closure_in_method.phpt | 19 ++++++ Zend/zend_compile.c | 67 ++++++++++--------- 10 files changed, 63 insertions(+), 39 deletions(-) create mode 100644 Zend/tests/typehints/self_on_closure_in_method.phpt diff --git a/Zend/tests/class_name_as_scalar_error_005.phpt b/Zend/tests/class_name_as_scalar_error_005.phpt index 39de69ffb3..27700fd646 100644 --- a/Zend/tests/class_name_as_scalar_error_005.phpt +++ b/Zend/tests/class_name_as_scalar_error_005.phpt @@ -7,4 +7,4 @@ $x = static::class; ?> --EXPECTF-- -Fatal error: Cannot access static::class when no class scope is active in %s on line %d \ No newline at end of file +Fatal error: Cannot use "static" when no class scope is active in %s on line 3 diff --git a/Zend/tests/class_name_as_scalar_error_006.phpt b/Zend/tests/class_name_as_scalar_error_006.phpt index a4cc9a528b..a1078d0c7e 100644 --- a/Zend/tests/class_name_as_scalar_error_006.phpt +++ b/Zend/tests/class_name_as_scalar_error_006.phpt @@ -7,4 +7,4 @@ $x = parent::class; ?> --EXPECTF-- -Fatal error: Cannot access parent::class when no class scope is active in %s on line %d +Fatal error: Cannot use "parent" when no class scope is active in %s on line 3 diff --git a/Zend/tests/class_name_as_scalar_error_007.phpt b/Zend/tests/class_name_as_scalar_error_007.phpt index 2bfa5f38f8..684470d211 100644 --- a/Zend/tests/class_name_as_scalar_error_007.phpt +++ b/Zend/tests/class_name_as_scalar_error_007.phpt @@ -7,4 +7,4 @@ var_dump(self::class); ?> --EXPECTF-- -Fatal error: Cannot access self::class when no class scope is active in %s on line %d +Fatal error: Cannot use "self" when no class scope is active in %s on line 3 diff --git a/Zend/tests/return_types/024.phpt b/Zend/tests/return_types/024.phpt index 9f2691f214..0579973fbc 100644 --- a/Zend/tests/return_types/024.phpt +++ b/Zend/tests/return_types/024.phpt @@ -7,4 +7,4 @@ Return type of self is not allowed in function function test(): self {} --EXPECTF-- -Fatal error: Cannot declare a return type of self outside of a class scope in %s on line 3 +Fatal error: Cannot use "self" when no class scope is active in %s on line 3 diff --git a/Zend/tests/return_types/025.phpt b/Zend/tests/return_types/025.phpt index 650fb08574..16a608bacd 100644 --- a/Zend/tests/return_types/025.phpt +++ b/Zend/tests/return_types/025.phpt @@ -7,4 +7,4 @@ Return type of self is not allowed in closure $c = function(): self {}; --EXPECTF-- -Fatal error: Cannot declare a return type of self outside of a class scope in %s on line 3 +Fatal error: Cannot use "self" when no class scope is active in %s on line 3 diff --git a/Zend/tests/return_types/026.phpt b/Zend/tests/return_types/026.phpt index 44ae82a356..5693e95c7e 100644 --- a/Zend/tests/return_types/026.phpt +++ b/Zend/tests/return_types/026.phpt @@ -6,4 +6,4 @@ Return type of parent is not allowed in function function test(): parent {} --EXPECTF-- -Fatal error: Cannot declare a return type of parent outside of a class scope in %s on line 3 +Fatal error: Cannot use "parent" when no class scope is active in %s on line %d diff --git a/Zend/tests/return_types/027.phpt b/Zend/tests/return_types/027.phpt index 4d615b4bf6..1abc7f733a 100644 --- a/Zend/tests/return_types/027.phpt +++ b/Zend/tests/return_types/027.phpt @@ -6,4 +6,4 @@ Return type of parent is not allowed in closure $c = function(): parent {}; --EXPECTF-- -Fatal error: Cannot declare a return type of parent outside of a class scope in %s on line 3 +Fatal error: Cannot use "parent" when no class scope is active in %s on line 3 diff --git a/Zend/tests/self_class_const_outside_class.phpt b/Zend/tests/self_class_const_outside_class.phpt index 0ef03c286f..541f3da381 100644 --- a/Zend/tests/self_class_const_outside_class.phpt +++ b/Zend/tests/self_class_const_outside_class.phpt @@ -5,4 +5,4 @@ Accessing self::FOO outside a class var_dump(self::FOO); ?> --EXPECTF-- -Fatal error: Cannot access self:: when no class scope is active in %s on line %d +Fatal error: Cannot use "self" when no class scope is active in %s on line %d diff --git a/Zend/tests/typehints/self_on_closure_in_method.phpt b/Zend/tests/typehints/self_on_closure_in_method.phpt new file mode 100644 index 0000000000..2d5bd82ef2 --- /dev/null +++ b/Zend/tests/typehints/self_on_closure_in_method.phpt @@ -0,0 +1,19 @@ +--TEST-- +self return type on closure in a method +--FILE-- +test()()); + +?> +--EXPECT-- +object(A)#1 (0) { +} diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index d4a9df36aa..c92a25a705 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1612,6 +1612,27 @@ uint32_t zend_get_class_fetch_type(zend_string *name) /* {{{ */ } /* }}} */ +static uint32_t zend_get_class_fetch_type_ast(zend_ast *name_ast) /* {{{ */ +{ + /* Fully qualified names are always default refs */ + if (name_ast->attr == ZEND_NAME_FQ) { + return ZEND_FETCH_CLASS_DEFAULT; + } + + return zend_get_class_fetch_type(zend_ast_get_str(name_ast)); +} +/* }}} */ + +void zend_ensure_valid_class_fetch_type(uint32_t fetch_type) /* {{{ */ +{ + if (fetch_type != ZEND_FETCH_CLASS_DEFAULT && !CG(active_class_entry)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use \"%s\" when no class scope is active", + fetch_type == ZEND_FETCH_CLASS_SELF ? "self" : + fetch_type == ZEND_FETCH_CLASS_PARENT ? "parent" : "static"); + } +} +/* }}} */ + ZEND_API zend_string *zend_get_compiled_variable_name(const zend_op_array *op_array, uint32_t var) /* {{{ */ { return op_array->vars[EX_VAR_TO_NUM(var)]; @@ -2018,19 +2039,11 @@ static inline zend_bool zend_can_write_to_variable(zend_ast *ast) /* {{{ */ static inline zend_bool zend_is_const_default_class_ref(zend_ast *name_ast) /* {{{ */ { - zend_string *name; - if (name_ast->kind != ZEND_AST_ZVAL) { return 0; } - /* Fully qualified names are always default refs */ - if (!name_ast->attr) { - return 1; - } - - name = zend_ast_get_str(name_ast); - return ZEND_FETCH_CLASS_DEFAULT == zend_get_class_fetch_type(name); + return ZEND_FETCH_CLASS_DEFAULT == zend_get_class_fetch_type_ast(name_ast); } /* }}} */ @@ -2077,6 +2090,8 @@ static zend_op *zend_compile_class_ref(znode *result, zend_ast *name_ast, int th opline->op2_type = IS_CONST; opline->op2.constant = zend_add_class_name_literal(CG(active_op_array), zend_resolve_class_name(name, type)); + } else { + zend_ensure_valid_class_fetch_type(fetch_type); } zend_string_release(name); @@ -4147,7 +4162,7 @@ ZEND_API void zend_set_function_arg_flags(zend_function *func) /* {{{ */ } /* }}} */ -void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, zend_bool is_method) /* {{{ */ +void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ { zend_ast_list *list = zend_ast_get_list(ast); uint32_t i; @@ -4173,15 +4188,13 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, zend_bool is_ if (type != 0) { arg_infos->type_hint = type; } else { - if (zend_is_const_default_class_ref(return_type_ast)) { + uint32_t fetch_type = zend_get_class_fetch_type_ast(return_type_ast); + if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) { class_name = zend_resolve_class_name_ast(return_type_ast); zend_assert_valid_class_name(class_name); } else { + zend_ensure_valid_class_fetch_type(fetch_type); zend_string_addref(class_name); - if (!is_method) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare a return type of %s outside of a class scope", class_name->val); - return; - } } arg_infos->type_hint = IS_OBJECT; @@ -4302,10 +4315,12 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, zend_bool is_ if (type != 0) { arg_info->type_hint = type; } else { - if (zend_is_const_default_class_ref(type_ast)) { + uint32_t fetch_type = zend_get_class_fetch_type_ast(type_ast); + if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) { class_name = zend_resolve_class_name_ast(type_ast); zend_assert_valid_class_name(class_name); } else { + zend_ensure_valid_class_fetch_type(fetch_type); zend_string_addref(class_name); } @@ -4626,7 +4641,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */ zend_stack_push(&CG(loop_var_stack), (void *) &dummy_var); } - zend_compile_params(params_ast, return_type_ast, is_method); + zend_compile_params(params_ast, return_type_ast); if (uses_ast) { zend_compile_closure_uses(uses_ast); } @@ -6329,13 +6344,10 @@ void zend_compile_resolve_class_name(znode *result, zend_ast *ast) /* {{{ */ { zend_ast *name_ast = ast->child[0]; uint32_t fetch_type = zend_get_class_fetch_type(zend_ast_get_str(name_ast)); + zend_ensure_valid_class_fetch_type(fetch_type); switch (fetch_type) { case ZEND_FETCH_CLASS_SELF: - if (!CG(active_class_entry)) { - zend_error_noreturn(E_COMPILE_ERROR, - "Cannot access self::class when no class scope is active"); - } if (CG(active_class_entry)->ce_flags & ZEND_ACC_TRAIT) { zval class_str_zv; zend_ast *class_str_ast, *class_const_ast; @@ -6354,11 +6366,7 @@ void zend_compile_resolve_class_name(znode *result, zend_ast *ast) /* {{{ */ break; case ZEND_FETCH_CLASS_STATIC: case ZEND_FETCH_CLASS_PARENT: - if (!CG(active_class_entry)) { - zend_error_noreturn(E_COMPILE_ERROR, - "Cannot access %s::class when no class scope is active", - fetch_type == ZEND_FETCH_CLASS_STATIC ? "static" : "parent"); - } else { + { zval class_str_zv; zend_ast *class_str_ast, *class_const_ast; @@ -6611,15 +6619,12 @@ void zend_compile_const_expr_resolve_class_name(zend_ast **ast_ptr) /* {{{ */ { zend_ast *ast = *ast_ptr; zend_ast *name_ast = ast->child[0]; - uint32_t fetch_type = zend_get_class_fetch_type(zend_ast_get_str(name_ast)); zval result; + uint32_t fetch_type = zend_get_class_fetch_type(zend_ast_get_str(name_ast)); + zend_ensure_valid_class_fetch_type(fetch_type); switch (fetch_type) { case ZEND_FETCH_CLASS_SELF: - if (!CG(active_class_entry)) { - zend_error_noreturn(E_COMPILE_ERROR, - "Cannot access self::class when no class scope is active"); - } ZVAL_STR_COPY(&result, CG(active_class_entry)->name); break; case ZEND_FETCH_CLASS_STATIC: -- 2.40.0