Skip to content

Commit 695da63

Browse files
committed
Zend: Resolve "self" and "parent" types when using traits
Achieved with the help of Arnaud
1 parent f102735 commit 695da63

13 files changed

Lines changed: 255 additions & 30 deletions

Zend/tests/type_declarations/relative_types/relative_type_in_evaled_class_using_trait.phpt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Eval Class definition should not leak memory when using compiled traits
44
<?php
55

66
trait TraitCompiled {
7+
public self $foo;
78
public function bar(): self { return new self; }
89
}
910

@@ -19,9 +20,24 @@ $a1 = new A();
1920
$a2 = $a1->bar();
2021
var_dump($a2);
2122

23+
$methodInfo = ReflectionMethod::createFromMethodName("A::bar");
24+
var_dump($methodInfo->getReturnType()->getName());
25+
$property = new ReflectionProperty('A', 'foo');
26+
var_dump($property->getType()->getName());
27+
28+
$methodInfo = ReflectionMethod::createFromMethodName("TraitCompiled::bar");
29+
var_dump($methodInfo->getReturnType()->getName());
30+
$property = new ReflectionProperty('TraitCompiled', 'foo');
31+
var_dump($property->getType()->getName());
2232
?>
2333
DONE
2434
--EXPECT--
2535
object(A)#2 (0) {
36+
["foo"]=>
37+
uninitialized(A)
2638
}
39+
string(1) "A"
40+
string(1) "A"
41+
string(4) "self"
42+
string(4) "self"
2743
DONE

Zend/tests/type_declarations/relative_types/relative_type_in_evaled_trait.phpt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Eval code should not leak memory when using traits
55

66
const EVAL_CODE = <<<'CODE'
77
trait TraitEval {
8+
public self $foo;
89
public function bar(): self { return new self; }
910
}
1011
CODE;
@@ -19,9 +20,25 @@ $a1 = new A();
1920
$a2 = $a1->bar();
2021
var_dump($a2);
2122

23+
$methodInfo = ReflectionMethod::createFromMethodName("A::bar");
24+
var_dump($methodInfo->getReturnType()->getName());
25+
$property = new ReflectionProperty('A', 'foo');
26+
var_dump($property->getType()->getName());
27+
28+
$methodInfo = ReflectionMethod::createFromMethodName("TraitEval::bar");
29+
var_dump($methodInfo->getReturnType()->getName());
30+
$property = new ReflectionProperty('TraitEval', 'foo');
31+
var_dump($property->getType()->getName());
32+
2233
?>
2334
DONE
2435
--EXPECT--
2536
object(A)#2 (0) {
37+
["foo"]=>
38+
uninitialized(A)
2639
}
40+
string(1) "A"
41+
string(1) "A"
42+
string(4) "self"
43+
string(4) "self"
2744
DONE
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
--TEST--
2+
Relative class types from traits should be resolved when used inside a class
3+
--FILE--
4+
<?php
5+
6+
trait TraitCompiled {
7+
public self $foo;
8+
public function bar(): self { return new self; }
9+
}
10+
11+
class A {
12+
use TraitCompiled;
13+
}
14+
15+
$a1 = new A();
16+
$a2 = $a1->bar();
17+
var_dump($a2);
18+
19+
$methodInfo = ReflectionMethod::createFromMethodName("A::bar");
20+
var_dump($methodInfo->getReturnType()->getName());
21+
$property = new ReflectionProperty('A', 'foo');
22+
var_dump($property->getType()->getName());
23+
24+
$methodInfo = ReflectionMethod::createFromMethodName("TraitCompiled::bar");
25+
var_dump($methodInfo->getReturnType()->getName());
26+
$property = new ReflectionProperty('TraitCompiled', 'foo');
27+
var_dump($property->getType()->getName());
28+
29+
?>
30+
DONE
31+
--EXPECT--
32+
object(A)#2 (0) {
33+
["foo"]=>
34+
uninitialized(A)
35+
}
36+
string(1) "A"
37+
string(1) "A"
38+
string(4) "self"
39+
string(4) "self"
40+
DONE
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
Relative class types from traits MUST NOT be resolved when used inside a trait
3+
--FILE--
4+
<?php
5+
6+
trait TraitCompiled {
7+
public self $foo;
8+
public function bar(): self { return new self; }
9+
}
10+
11+
trait SecondTrait {
12+
use TraitCompiled;
13+
}
14+
15+
$methodInfo = ReflectionMethod::createFromMethodName("TraitCompiled::bar");
16+
var_dump($methodInfo->getReturnType()->getName());
17+
$property = new ReflectionProperty('TraitCompiled', 'foo');
18+
var_dump($property->getType()->getName());
19+
20+
$methodInfo = ReflectionMethod::createFromMethodName("SecondTrait::bar");
21+
var_dump($methodInfo->getReturnType()->getName());
22+
$property = new ReflectionProperty('SecondTrait', 'foo');
23+
var_dump($property->getType()->getName());
24+
25+
?>
26+
DONE
27+
--EXPECT--
28+
string(4) "self"
29+
string(4) "self"
30+
string(4) "self"
31+
string(4) "self"
32+
DONE

Zend/tests/type_declarations/relative_types/traits/trait_parent_type_in_class_no_parent1.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ class A {
1111
}
1212
?>
1313
DONE
14-
--EXPECT--
15-
DONE
14+
--EXPECTF--
15+
Fatal error: Cannot use trait which has "parent" as a type when current class scope has no parent in %s on line %d

Zend/tests/type_declarations/relative_types/traits/trait_parent_type_in_class_no_parent2.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ class A {
1111
}
1212
?>
1313
DONE
14-
--EXPECT--
15-
DONE
14+
--EXPECTF--
15+
Fatal error: Cannot use trait which has "parent" as a type when current class scope has no parent in %s on line %d

Zend/tests/type_declarations/relative_types/traits/trait_parent_type_in_class_no_parent3.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ class A {
1111
}
1212
?>
1313
DONE
14-
--EXPECT--
15-
DONE
14+
--EXPECTF--
15+
Fatal error: Cannot use trait which has "parent" as a type when current class scope has no parent in %s on line %d
Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
--TEST--
2-
Cannot use a trait which references parent as a type in a class with no parent, DNF type
2+
Cannot use a trait which references parent as a type in a class with no parent, non-simple union type
33
--FILE--
44
<?php
5+
6+
class T {}
7+
58
trait TraitExample {
6-
public function bar(): (X&Y)|parent { return parent::class; }
9+
public function bar(): T|parent { return parent::class; }
710
}
811

912
class A {
1013
use TraitExample;
1114
}
1215
?>
1316
DONE
14-
--EXPECT--
15-
DONE
17+
--EXPECTF--
18+
Fatal error: Cannot use trait which has "parent" as a type when current class scope has no parent in %s on line %d
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Cannot use a trait which references parent as a type in a class with no parent, DNF type
3+
--FILE--
4+
<?php
5+
trait TraitExample {
6+
public function bar(): (X&Y)|parent { return parent::class; }
7+
}
8+
9+
class A {
10+
use TraitExample;
11+
}
12+
?>
13+
DONE
14+
--EXPECTF--
15+
Fatal error: Cannot use trait which has "parent" as a type when current class scope has no parent in %s on line %d

Zend/zend_compile.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1709,7 +1709,7 @@ static bool zend_try_ct_eval_const(zval *zv, zend_string *name, bool is_fully_qu
17091709
}
17101710
/* }}} */
17111711

1712-
static inline bool zend_is_scope_known(void) /* {{{ */
1712+
static inline bool zend_is_scope_known_ex(bool allow_traits) /* {{{ */
17131713
{
17141714
if (!CG(active_op_array)) {
17151715
/* This can only happen when evaluating a default value string. */
@@ -1728,10 +1728,14 @@ static inline bool zend_is_scope_known(void) /* {{{ */
17281728
}
17291729

17301730
/* For traits self etc refers to the using class, not the trait itself */
1731-
return (CG(active_class_entry)->ce_flags & ZEND_ACC_TRAIT) == 0;
1731+
return allow_traits || (CG(active_class_entry)->ce_flags & ZEND_ACC_TRAIT) == 0;
17321732
}
17331733
/* }}} */
17341734

1735+
static inline bool zend_is_scope_known(void) {
1736+
return zend_is_scope_known_ex(false);
1737+
}
1738+
17351739
static inline bool class_name_refers_to_active_ce(const zend_string *class_name, uint32_t fetch_type) /* {{{ */
17361740
{
17371741
if (!CG(active_class_entry)) {
@@ -7420,6 +7424,10 @@ static zend_type zend_compile_single_typename(zend_ast *ast)
74207424
ZEND_ASSERT(class_name && "must know class name when resolving parent type at compile time");
74217425
}
74227426
}
7427+
if (zend_is_scope_known_ex(true)) {
7428+
zend_op_array *op_array = CG(active_op_array);
7429+
op_array->fn_flags2 = ZEND_ACC_RESOLVE_RELATIVE_TYPE;
7430+
}
74237431
zend_string_addref(class_name);
74247432
}
74257433

0 commit comments

Comments
 (0)