Skip to content

Commit 5b8ee00

Browse files
committed
Merge branch 'PHP-8.4' of https://github.com/php/php-src
2 parents 5a06842 + b13347b commit 5b8ee00

File tree

10 files changed

+241
-5
lines changed

10 files changed

+241
-5
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
GH-19044: Protected properties must be scoped according to their prototype (protected(set) on non-hooked property)
3+
--FILE--
4+
<?php
5+
6+
class P {
7+
public mixed $foo { get => 42; }
8+
}
9+
10+
class C1 extends P {
11+
public protected(set) mixed $foo = 1;
12+
}
13+
14+
class C2 extends P {
15+
public protected(set) mixed $foo;
16+
17+
static function foo($c) { return $c->foo += 1; }
18+
}
19+
20+
var_dump(C2::foo(new C1));
21+
22+
?>
23+
--EXPECT--
24+
int(43)

Zend/tests/gh19044.phpt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
GH-19044: Protected properties must be scoped according to their prototype
3+
--FILE--
4+
<?php
5+
6+
abstract class P {
7+
protected $foo;
8+
}
9+
10+
class C1 extends P {
11+
protected $foo = 1;
12+
}
13+
14+
class C2 extends P {
15+
protected $foo = 2;
16+
17+
static function foo($c) { return $c->foo; }
18+
}
19+
20+
var_dump(C2::foo(new C2));
21+
var_dump(C2::foo(new C1));
22+
23+
?>
24+
--EXPECT--
25+
int(2)
26+
int(1)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
GH-19044: Protected properties must be scoped according to their prototype (common ancestor has a protected setter)
3+
--FILE--
4+
<?php
5+
6+
abstract class P {
7+
abstract public mixed $foo { get; }
8+
}
9+
10+
class C1 extends P {
11+
public protected(set) mixed $foo { get => 1; set {} }
12+
}
13+
14+
class GrandC1 extends C1 {
15+
public protected(set) mixed $foo { get => 2; set {} }
16+
}
17+
18+
class C2 extends C1 {
19+
static function foo($c) { return $c->foo += 1; }
20+
}
21+
22+
var_dump(C2::foo(new GrandC1));
23+
24+
?>
25+
--EXPECT--
26+
int(3)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
GH-19044: Protected properties must be scoped according to their prototype (common ancestor does not have a setter)
3+
--FILE--
4+
<?php
5+
6+
abstract class P {
7+
abstract public mixed $foo { get; }
8+
}
9+
10+
class C1 extends P {
11+
public mixed $foo { get => 1; }
12+
}
13+
14+
class GrandC1 extends C1 {
15+
public protected(set) mixed $foo { get => 2; set {} }
16+
}
17+
18+
class C2 extends C1 {
19+
static function foo($c) { return $c->foo += 1; }
20+
}
21+
22+
var_dump(C2::foo(new GrandC1));
23+
24+
?>
25+
--EXPECT--
26+
int(3)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
GH-19044: Protected properties must be scoped according to their prototype (abstract parent defining visibility only takes precedence)
3+
--FILE--
4+
<?php
5+
6+
abstract class P {
7+
abstract protected(set) mixed $foo { get; set; }
8+
}
9+
10+
class C1 extends P {
11+
public protected(set) mixed $foo { get => 2; set {} }
12+
}
13+
14+
class C2 extends P {
15+
public mixed $foo = 1;
16+
17+
static function foo($c) { return $c->foo += 1; }
18+
}
19+
20+
var_dump(C2::foo(new C1));
21+
22+
?>
23+
--EXPECT--
24+
int(3)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
GH-19044: Protected properties must be scoped according to their prototype (abstract parent sets protected(set) with not having grandparent a setter - both inherit from parent)
3+
--FILE--
4+
<?php
5+
6+
abstract class GP {
7+
abstract mixed $foo { get; }
8+
}
9+
10+
abstract class P extends GP {
11+
abstract protected(set) mixed $foo { get; set; }
12+
}
13+
14+
class C1 extends P {
15+
public protected(set) mixed $foo { get => 2; set {} }
16+
}
17+
18+
class C2 extends P {
19+
public mixed $foo = 1;
20+
21+
static function foo($c) { return $c->foo += 1; }
22+
}
23+
24+
var_dump(C2::foo(new C1));
25+
26+
?>
27+
--EXPECT--
28+
int(3)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
GH-19044: Protected properties must be scoped according to their prototype (abstract parent sets protected(set) with not having grandparent a setter - one inherits from grandparent)
3+
--FILE--
4+
<?php
5+
6+
abstract class GP {
7+
abstract mixed $foo { get; }
8+
}
9+
10+
abstract class P extends GP {
11+
abstract protected(set) mixed $foo { get; set; }
12+
}
13+
14+
class C1 extends P {
15+
public protected(set) mixed $foo { get => 2; set {} }
16+
}
17+
18+
class C2 extends GP {
19+
public mixed $foo = 1;
20+
21+
static function foo($c) { return $c->foo += 1; }
22+
}
23+
24+
var_dump(C2::foo(new C1));
25+
26+
?>
27+
--EXPECT--
28+
int(3)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
GH-19044: Protected properties must be scoped according to their prototype (abstract parent has implicit set hook)
3+
--FILE--
4+
<?php
5+
6+
abstract class GP {
7+
public abstract mixed $foo { get; }
8+
}
9+
10+
class P extends GP {
11+
public protected(set) mixed $foo { get => $this->foo; }
12+
}
13+
14+
class C1 extends P {
15+
public protected(set) mixed $foo = 1;
16+
}
17+
18+
class C2 extends P {
19+
public protected(set) mixed $foo;
20+
21+
static function foo($c) { return $c->foo += 1; }
22+
}
23+
24+
var_dump(C2::foo(new C1));
25+
26+
?>
27+
--EXPECT--
28+
int(2)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
GH-19044: Protected properties must be scoped according to their prototype (hooks variation)
3+
--FILE--
4+
<?php
5+
6+
abstract class P {
7+
abstract protected $foo { get; }
8+
}
9+
10+
class C1 extends P {
11+
protected $foo = 1;
12+
}
13+
14+
class C2 extends P {
15+
protected $foo = 2;
16+
17+
static function foo($c) { return $c->foo; }
18+
}
19+
20+
var_dump(C2::foo(new C2));
21+
var_dump(C2::foo(new C1));
22+
23+
?>
24+
--EXPECT--
25+
int(2)
26+
int(1)

Zend/zend_object_handlers.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ static zend_always_inline bool is_derived_class(const zend_class_entry *child_cl
285285
static zend_never_inline int is_protected_compatible_scope(const zend_class_entry *ce, const zend_class_entry *scope) /* {{{ */
286286
{
287287
return scope &&
288-
(is_derived_class(ce, scope) || is_derived_class(scope, ce));
288+
(ce == scope || is_derived_class(ce, scope) || is_derived_class(scope, ce));
289289
}
290290
/* }}} */
291291

@@ -422,7 +422,7 @@ static zend_always_inline uintptr_t zend_get_property_offset(zend_class_entry *c
422422
}
423423
} else {
424424
ZEND_ASSERT(flags & ZEND_ACC_PROTECTED);
425-
if (UNEXPECTED(!is_protected_compatible_scope(property_info->ce, scope))) {
425+
if (UNEXPECTED(!is_protected_compatible_scope(property_info->prototype->ce, scope))) {
426426
goto wrong;
427427
}
428428
}
@@ -517,7 +517,7 @@ ZEND_API zend_property_info *zend_get_property_info(const zend_class_entry *ce,
517517
}
518518
} else {
519519
ZEND_ASSERT(flags & ZEND_ACC_PROTECTED);
520-
if (UNEXPECTED(!is_protected_compatible_scope(property_info->ce, scope))) {
520+
if (UNEXPECTED(!is_protected_compatible_scope(property_info->prototype->ce, scope))) {
521521
goto wrong;
522522
}
523523
}
@@ -588,7 +588,7 @@ ZEND_API bool ZEND_FASTCALL zend_asymmetric_property_has_set_access(const zend_p
588588
return true;
589589
}
590590
return EXPECTED((prop_info->flags & ZEND_ACC_PROTECTED_SET)
591-
&& is_protected_compatible_scope(prop_info->ce, scope));
591+
&& is_protected_compatible_scope(prop_info->prototype->ce, scope));
592592
}
593593

594594
static void zend_property_guard_dtor(zval *el) /* {{{ */ {
@@ -2033,7 +2033,7 @@ ZEND_API zval *zend_std_get_static_property_with_info(zend_class_entry *ce, zend
20332033
const zend_class_entry *scope = get_fake_or_executed_scope();
20342034
if (property_info->ce != scope) {
20352035
if (UNEXPECTED(property_info->flags & ZEND_ACC_PRIVATE)
2036-
|| UNEXPECTED(!is_protected_compatible_scope(property_info->ce, scope))) {
2036+
|| UNEXPECTED(!is_protected_compatible_scope(property_info->prototype->ce, scope))) {
20372037
if (type != BP_VAR_IS) {
20382038
zend_bad_property_access(property_info, ce, property_name);
20392039
}

0 commit comments

Comments
 (0)