Skip to content

Commit fc1e7a2

Browse files
committed
Only use information from visible properties in opcache
Based on the descision that we do not want to enforce __get() return types for inaccessible propreties.
1 parent bd95ef2 commit fc1e7a2

File tree

3 files changed

+164
-3
lines changed

3 files changed

+164
-3
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
Property type not enforced for __get if the property is not visible
3+
--FILE--
4+
<?php
5+
6+
class Test {
7+
private int $prop;
8+
9+
public function __get($name) {
10+
return "foobar";
11+
}
12+
}
13+
14+
$test = new Test;
15+
var_dump($test->prop);
16+
17+
?>
18+
--EXPECT--
19+
string(6) "foobar"

ext/opcache/Optimizer/zend_inference.c

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2280,6 +2280,32 @@ static uint32_t zend_fetch_arg_info(const zend_script *script, zend_arg_info *ar
22802280
return tmp;
22812281
}
22822282

2283+
static zend_property_info *lookup_prop_info(zend_class_entry *ce, zend_string *name, zend_class_entry *scope) {
2284+
zend_property_info *prop_info;
2285+
2286+
/* If the class is linked, reuse the precise runtime logic. */
2287+
if (ce->ce_flags & ZEND_ACC_LINKED) {
2288+
zend_class_entry *prev_scope = EG(fake_scope);
2289+
EG(fake_scope) = scope;
2290+
prop_info = zend_get_property_info(ce, name, 1);
2291+
EG(fake_scope) = prev_scope;
2292+
if (prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO) {
2293+
return prop_info;
2294+
}
2295+
return NULL;
2296+
}
2297+
2298+
/* Otherwise, handle only some safe cases */
2299+
prop_info = zend_hash_find_ptr(&ce->properties_info, name);
2300+
if (prop_info &&
2301+
((prop_info->ce == scope) ||
2302+
(!scope && (prop_info->flags & ZEND_ACC_PUBLIC)))
2303+
) {
2304+
return prop_info;
2305+
}
2306+
return NULL;
2307+
}
2308+
22832309
static zend_property_info *zend_fetch_prop_info(const zend_op_array *op_array, zend_ssa *ssa, zend_op *opline, int i)
22842310
{
22852311
zend_property_info *prop_info = NULL;
@@ -2292,8 +2318,9 @@ static zend_property_info *zend_fetch_prop_info(const zend_op_array *op_array, z
22922318
ce = ssa->var_info[ssa->ops[i].op1_use].ce;
22932319
}
22942320
if (ce) {
2295-
prop_info = zend_hash_find_ptr(&ce->properties_info,
2296-
Z_STR_P(CRT_CONSTANT_EX(op_array, opline, opline->op2, ssa->rt_constants)));
2321+
prop_info = lookup_prop_info(ce,
2322+
Z_STR_P(CRT_CONSTANT_EX(op_array, opline, opline->op2, ssa->rt_constants)),
2323+
op_array->scope);
22972324
if (prop_info && (prop_info->flags & ZEND_ACC_STATIC)) {
22982325
prop_info = NULL;
22992326
}
@@ -2329,7 +2356,7 @@ static zend_property_info *zend_fetch_static_prop_info(const zend_script *script
23292356

23302357
if (ce) {
23312358
zval *zv = CRT_CONSTANT_EX(op_array, opline, opline->op1, ssa->rt_constants);
2332-
prop_info = zend_hash_find_ptr(&ce->properties_info, Z_STR_P(zv));
2359+
prop_info = lookup_prop_info(ce, Z_STR_P(zv), op_array->scope);
23332360
if (prop_info && !(prop_info->flags & ZEND_ACC_STATIC)) {
23342361
prop_info = NULL;
23352362
}

ext/opcache/tests/opt/prop_types.phpt

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
--TEST--
2+
Property types in inference
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
opcache.optimization_level=-1
7+
opcache.opt_debug_level=0x200000
8+
--SKIPIF--
9+
<?php require_once('skipif.inc'); ?>
10+
--FILE--
11+
<?php
12+
13+
class Test {
14+
public bool $public;
15+
protected int $protected;
16+
private float $private;
17+
18+
public function inTest() {
19+
var_dump($this->public, $this->protected, $this->private);
20+
}
21+
22+
public function inTestWithTest2(Test2 $test2) {
23+
var_dump($test2->public, $test2->protected, $test2->private);
24+
}
25+
}
26+
27+
class Test2 extends Test {
28+
private array $private;
29+
30+
public function inTest2() {
31+
var_dump($this->public, $this->protected, $this->private);
32+
}
33+
}
34+
35+
function noScope(Test $test) {
36+
var_dump($test->public, $test->protected, $test->private);
37+
}
38+
39+
?>
40+
--EXPECTF--
41+
$_main: ; (lines=1, args=0, vars=0, tmps=0, ssa_vars=0, no_loops)
42+
; (before dfa pass)
43+
; %s
44+
; return [long] RANGE[1..1]
45+
BB0: start exit lines=[0-0]
46+
; level=0
47+
RETURN int(1)
48+
49+
noScope: ; (lines=10, args=1, vars=1, tmps=1, ssa_vars=5, no_loops)
50+
; (before dfa pass)
51+
; %s
52+
; return [null] RANGE[0..0]
53+
; #0.CV0($test) NOVAL [undef]
54+
BB0: start exit lines=[0-9]
55+
; level=0
56+
#1.CV0($test) [object (instanceof Test)] = RECV 1
57+
INIT_FCALL 3 128 string("var_dump")
58+
#2.T1 [bool] = FETCH_OBJ_R #1.CV0($test) [object (instanceof Test)] string("public")
59+
SEND_VAL #2.T1 [bool] 1
60+
#3.T1 [any] = FETCH_OBJ_R #1.CV0($test) [object (instanceof Test)] string("protected")
61+
SEND_VAL #3.T1 [any] 2
62+
#4.T1 [any] = FETCH_OBJ_R #1.CV0($test) [object (instanceof Test)] string("private")
63+
SEND_VAL #4.T1 [any] 3
64+
DO_ICALL
65+
RETURN null
66+
67+
Test::inTest: ; (lines=9, args=0, vars=0, tmps=1, ssa_vars=3, no_loops)
68+
; (before dfa pass)
69+
; %s
70+
; return [null] RANGE[0..0]
71+
BB0: start exit lines=[0-8]
72+
; level=0
73+
INIT_FCALL 3 128 string("var_dump")
74+
#0.T0 [bool] = FETCH_OBJ_R THIS string("public")
75+
SEND_VAL #0.T0 [bool] 1
76+
#1.T0 [long] = FETCH_OBJ_R THIS string("protected")
77+
SEND_VAL #1.T0 [long] 2
78+
#2.T0 [double] = FETCH_OBJ_R THIS string("private")
79+
SEND_VAL #2.T0 [double] 3
80+
DO_ICALL
81+
RETURN null
82+
83+
Test::inTestWithTest2: ; (lines=10, args=1, vars=1, tmps=1, ssa_vars=5, no_loops)
84+
; (before dfa pass)
85+
; %s
86+
; return [null] RANGE[0..0]
87+
; #0.CV0($test2) NOVAL [undef]
88+
BB0: start exit lines=[0-9]
89+
; level=0
90+
#1.CV0($test2) [object (instanceof Test2)] = RECV 1
91+
INIT_FCALL 3 128 string("var_dump")
92+
#2.T1 [bool] = FETCH_OBJ_R #1.CV0($test2) [object (instanceof Test2)] string("public")
93+
SEND_VAL #2.T1 [bool] 1
94+
#3.T1 [long] = FETCH_OBJ_R #1.CV0($test2) [object (instanceof Test2)] string("protected")
95+
SEND_VAL #3.T1 [long] 2
96+
#4.T1 [double] = FETCH_OBJ_R #1.CV0($test2) [object (instanceof Test2)] string("private")
97+
SEND_VAL #4.T1 [double] 3
98+
DO_ICALL
99+
RETURN null
100+
101+
Test2::inTest2: ; (lines=9, args=0, vars=0, tmps=1, ssa_vars=3, no_loops)
102+
; (before dfa pass)
103+
; %s
104+
; return [null] RANGE[0..0]
105+
BB0: start exit lines=[0-8]
106+
; level=0
107+
INIT_FCALL 3 128 string("var_dump")
108+
#0.T0 [bool] = FETCH_OBJ_R THIS string("public")
109+
SEND_VAL #0.T0 [bool] 1
110+
#1.T0 [long] = FETCH_OBJ_R THIS string("protected")
111+
SEND_VAL #1.T0 [long] 2
112+
#2.T0 [array of [any, ref]] = FETCH_OBJ_R THIS string("private")
113+
SEND_VAL #2.T0 [array of [any, ref]] 3
114+
DO_ICALL
115+
RETURN null

0 commit comments

Comments
 (0)