Skip to content

Commit 16a9bc1

Browse files
committed
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.
1 parent d880ead commit 16a9bc1

10 files changed

+63
-39
lines changed

Zend/tests/class_name_as_scalar_error_005.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ $x = static::class;
77

88
?>
99
--EXPECTF--
10-
Fatal error: Cannot access static::class when no class scope is active in %s on line %d
10+
Fatal error: Cannot use "static" when no class scope is active in %s on line 3

Zend/tests/class_name_as_scalar_error_006.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ $x = parent::class;
77

88
?>
99
--EXPECTF--
10-
Fatal error: Cannot access parent::class when no class scope is active in %s on line %d
10+
Fatal error: Cannot use "parent" when no class scope is active in %s on line 3

Zend/tests/class_name_as_scalar_error_007.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ var_dump(self::class);
77

88
?>
99
--EXPECTF--
10-
Fatal error: Cannot access self::class when no class scope is active in %s on line %d
10+
Fatal error: Cannot use "self" when no class scope is active in %s on line 3

Zend/tests/return_types/024.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ Return type of self is not allowed in function
77
function test(): self {}
88

99
--EXPECTF--
10-
Fatal error: Cannot declare a return type of self outside of a class scope in %s on line 3
10+
Fatal error: Cannot use "self" when no class scope is active in %s on line 3

Zend/tests/return_types/025.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ Return type of self is not allowed in closure
77
$c = function(): self {};
88

99
--EXPECTF--
10-
Fatal error: Cannot declare a return type of self outside of a class scope in %s on line 3
10+
Fatal error: Cannot use "self" when no class scope is active in %s on line 3

Zend/tests/return_types/026.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ Return type of parent is not allowed in function
66
function test(): parent {}
77

88
--EXPECTF--
9-
Fatal error: Cannot declare a return type of parent outside of a class scope in %s on line 3
9+
Fatal error: Cannot use "parent" when no class scope is active in %s on line %d

Zend/tests/return_types/027.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ Return type of parent is not allowed in closure
66
$c = function(): parent {};
77

88
--EXPECTF--
9-
Fatal error: Cannot declare a return type of parent outside of a class scope in %s on line 3
9+
Fatal error: Cannot use "parent" when no class scope is active in %s on line 3

Zend/tests/self_class_const_outside_class.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ Accessing self::FOO outside a class
55
var_dump(self::FOO);
66
?>
77
--EXPECTF--
8-
Fatal error: Cannot access self:: when no class scope is active in %s on line %d
8+
Fatal error: Cannot use "self" when no class scope is active in %s on line %d
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
self return type on closure in a method
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public function test() {
8+
return function() : self {
9+
return $this;
10+
};
11+
}
12+
}
13+
14+
var_dump((new A)->test()());
15+
16+
?>
17+
--EXPECT--
18+
object(A)#1 (0) {
19+
}

Zend/zend_compile.c

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1612,6 +1612,27 @@ uint32_t zend_get_class_fetch_type(zend_string *name) /* {{{ */
16121612
}
16131613
/* }}} */
16141614

1615+
static uint32_t zend_get_class_fetch_type_ast(zend_ast *name_ast) /* {{{ */
1616+
{
1617+
/* Fully qualified names are always default refs */
1618+
if (name_ast->attr == ZEND_NAME_FQ) {
1619+
return ZEND_FETCH_CLASS_DEFAULT;
1620+
}
1621+
1622+
return zend_get_class_fetch_type(zend_ast_get_str(name_ast));
1623+
}
1624+
/* }}} */
1625+
1626+
void zend_ensure_valid_class_fetch_type(uint32_t fetch_type) /* {{{ */
1627+
{
1628+
if (fetch_type != ZEND_FETCH_CLASS_DEFAULT && !CG(active_class_entry)) {
1629+
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use \"%s\" when no class scope is active",
1630+
fetch_type == ZEND_FETCH_CLASS_SELF ? "self" :
1631+
fetch_type == ZEND_FETCH_CLASS_PARENT ? "parent" : "static");
1632+
}
1633+
}
1634+
/* }}} */
1635+
16151636
ZEND_API zend_string *zend_get_compiled_variable_name(const zend_op_array *op_array, uint32_t var) /* {{{ */
16161637
{
16171638
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) /* {{{ */
20182039

20192040
static inline zend_bool zend_is_const_default_class_ref(zend_ast *name_ast) /* {{{ */
20202041
{
2021-
zend_string *name;
2022-
20232042
if (name_ast->kind != ZEND_AST_ZVAL) {
20242043
return 0;
20252044
}
20262045

2027-
/* Fully qualified names are always default refs */
2028-
if (!name_ast->attr) {
2029-
return 1;
2030-
}
2031-
2032-
name = zend_ast_get_str(name_ast);
2033-
return ZEND_FETCH_CLASS_DEFAULT == zend_get_class_fetch_type(name);
2046+
return ZEND_FETCH_CLASS_DEFAULT == zend_get_class_fetch_type_ast(name_ast);
20342047
}
20352048
/* }}} */
20362049

@@ -2077,6 +2090,8 @@ static zend_op *zend_compile_class_ref(znode *result, zend_ast *name_ast, int th
20772090
opline->op2_type = IS_CONST;
20782091
opline->op2.constant = zend_add_class_name_literal(CG(active_op_array),
20792092
zend_resolve_class_name(name, type));
2093+
} else {
2094+
zend_ensure_valid_class_fetch_type(fetch_type);
20802095
}
20812096

20822097
zend_string_release(name);
@@ -4147,7 +4162,7 @@ ZEND_API void zend_set_function_arg_flags(zend_function *func) /* {{{ */
41474162
}
41484163
/* }}} */
41494164

4150-
void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, zend_bool is_method) /* {{{ */
4165+
void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
41514166
{
41524167
zend_ast_list *list = zend_ast_get_list(ast);
41534168
uint32_t i;
@@ -4173,15 +4188,13 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, zend_bool is_
41734188
if (type != 0) {
41744189
arg_infos->type_hint = type;
41754190
} else {
4176-
if (zend_is_const_default_class_ref(return_type_ast)) {
4191+
uint32_t fetch_type = zend_get_class_fetch_type_ast(return_type_ast);
4192+
if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) {
41774193
class_name = zend_resolve_class_name_ast(return_type_ast);
41784194
zend_assert_valid_class_name(class_name);
41794195
} else {
4196+
zend_ensure_valid_class_fetch_type(fetch_type);
41804197
zend_string_addref(class_name);
4181-
if (!is_method) {
4182-
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare a return type of %s outside of a class scope", class_name->val);
4183-
return;
4184-
}
41854198
}
41864199

41874200
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_
43024315
if (type != 0) {
43034316
arg_info->type_hint = type;
43044317
} else {
4305-
if (zend_is_const_default_class_ref(type_ast)) {
4318+
uint32_t fetch_type = zend_get_class_fetch_type_ast(type_ast);
4319+
if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) {
43064320
class_name = zend_resolve_class_name_ast(type_ast);
43074321
zend_assert_valid_class_name(class_name);
43084322
} else {
4323+
zend_ensure_valid_class_fetch_type(fetch_type);
43094324
zend_string_addref(class_name);
43104325
}
43114326

@@ -4626,7 +4641,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
46264641
zend_stack_push(&CG(loop_var_stack), (void *) &dummy_var);
46274642
}
46284643

4629-
zend_compile_params(params_ast, return_type_ast, is_method);
4644+
zend_compile_params(params_ast, return_type_ast);
46304645
if (uses_ast) {
46314646
zend_compile_closure_uses(uses_ast);
46324647
}
@@ -6329,13 +6344,10 @@ void zend_compile_resolve_class_name(znode *result, zend_ast *ast) /* {{{ */
63296344
{
63306345
zend_ast *name_ast = ast->child[0];
63316346
uint32_t fetch_type = zend_get_class_fetch_type(zend_ast_get_str(name_ast));
6347+
zend_ensure_valid_class_fetch_type(fetch_type);
63326348

63336349
switch (fetch_type) {
63346350
case ZEND_FETCH_CLASS_SELF:
6335-
if (!CG(active_class_entry)) {
6336-
zend_error_noreturn(E_COMPILE_ERROR,
6337-
"Cannot access self::class when no class scope is active");
6338-
}
63396351
if (CG(active_class_entry)->ce_flags & ZEND_ACC_TRAIT) {
63406352
zval class_str_zv;
63416353
zend_ast *class_str_ast, *class_const_ast;
@@ -6354,11 +6366,7 @@ void zend_compile_resolve_class_name(znode *result, zend_ast *ast) /* {{{ */
63546366
break;
63556367
case ZEND_FETCH_CLASS_STATIC:
63566368
case ZEND_FETCH_CLASS_PARENT:
6357-
if (!CG(active_class_entry)) {
6358-
zend_error_noreturn(E_COMPILE_ERROR,
6359-
"Cannot access %s::class when no class scope is active",
6360-
fetch_type == ZEND_FETCH_CLASS_STATIC ? "static" : "parent");
6361-
} else {
6369+
{
63626370
zval class_str_zv;
63636371
zend_ast *class_str_ast, *class_const_ast;
63646372

@@ -6611,15 +6619,12 @@ void zend_compile_const_expr_resolve_class_name(zend_ast **ast_ptr) /* {{{ */
66116619
{
66126620
zend_ast *ast = *ast_ptr;
66136621
zend_ast *name_ast = ast->child[0];
6614-
uint32_t fetch_type = zend_get_class_fetch_type(zend_ast_get_str(name_ast));
66156622
zval result;
6623+
uint32_t fetch_type = zend_get_class_fetch_type(zend_ast_get_str(name_ast));
6624+
zend_ensure_valid_class_fetch_type(fetch_type);
66166625

66176626
switch (fetch_type) {
66186627
case ZEND_FETCH_CLASS_SELF:
6619-
if (!CG(active_class_entry)) {
6620-
zend_error_noreturn(E_COMPILE_ERROR,
6621-
"Cannot access self::class when no class scope is active");
6622-
}
66236628
ZVAL_STR_COPY(&result, CG(active_class_entry)->name);
66246629
break;
66256630
case ZEND_FETCH_CLASS_STATIC:

0 commit comments

Comments
 (0)