mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Fix GH-19044: Protected properties are not scoped according to their prototype (#19046)
* Fix GH-19044: Protected properties are not scoped according to their prototype * Adjust after review * Simplify to using prototype even for asymmetric visibility
This commit is contained in:
parent
4e1d3f8772
commit
b13347be38
11 changed files with 243 additions and 8 deletions
5
NEWS
5
NEWS
|
@ -7,6 +7,8 @@ PHP NEWS
|
||||||
(psumbera)
|
(psumbera)
|
||||||
. Fixed bug GH-19053 (Duplicate property slot with hooks and interface
|
. Fixed bug GH-19053 (Duplicate property slot with hooks and interface
|
||||||
property). (ilutov)
|
property). (ilutov)
|
||||||
|
. Fixed bug GH-19044 (Protected properties are not scoped according to their
|
||||||
|
prototype). (Bob)
|
||||||
|
|
||||||
- Hash:
|
- Hash:
|
||||||
. Fix crash on clone failure. (nielsdos)
|
. Fix crash on clone failure. (nielsdos)
|
||||||
|
@ -49,9 +51,6 @@ PHP NEWS
|
||||||
. Fixed bug GH-18907 (Leak when creating cycle in hook). (ilutov)
|
. Fixed bug GH-18907 (Leak when creating cycle in hook). (ilutov)
|
||||||
. Fix OSS-Fuzz #427814456. (nielsdos)
|
. Fix OSS-Fuzz #427814456. (nielsdos)
|
||||||
. Fix OSS-Fuzz #428983568 and #428760800. (nielsdos)
|
. Fix OSS-Fuzz #428983568 and #428760800. (nielsdos)
|
||||||
. Fixed bug GH-17204 (-Wuseless-escape warnings emitted by re2c). (Peter Kokot)
|
|
||||||
. Fixed bug GH-19064 (Undefined symbol 'execute_ex' on Windows ARM64).
|
|
||||||
(Demon)
|
|
||||||
|
|
||||||
- Curl:
|
- Curl:
|
||||||
. Fix memory leaks when returning refcounted value from curl callback.
|
. Fix memory leaks when returning refcounted value from curl callback.
|
||||||
|
|
24
Zend/tests/asymmetric_visibility/gh19044.phpt
Normal file
24
Zend/tests/asymmetric_visibility/gh19044.phpt
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
--TEST--
|
||||||
|
GH-19044: Protected properties must be scoped according to their prototype (protected(set) on non-hooked property)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class P {
|
||||||
|
public mixed $foo { get => 42; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class C1 extends P {
|
||||||
|
public protected(set) mixed $foo = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
class C2 extends P {
|
||||||
|
public protected(set) mixed $foo;
|
||||||
|
|
||||||
|
static function foo($c) { return $c->foo += 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(C2::foo(new C1));
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
int(43)
|
26
Zend/tests/gh19044.phpt
Normal file
26
Zend/tests/gh19044.phpt
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
--TEST--
|
||||||
|
GH-19044: Protected properties must be scoped according to their prototype
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class P {
|
||||||
|
protected $foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
class C1 extends P {
|
||||||
|
protected $foo = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
class C2 extends P {
|
||||||
|
protected $foo = 2;
|
||||||
|
|
||||||
|
static function foo($c) { return $c->foo; }
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(C2::foo(new C2));
|
||||||
|
var_dump(C2::foo(new C1));
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
int(2)
|
||||||
|
int(1)
|
26
Zend/tests/property_hooks/gh19044-1.phpt
Normal file
26
Zend/tests/property_hooks/gh19044-1.phpt
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
--TEST--
|
||||||
|
GH-19044: Protected properties must be scoped according to their prototype (common ancestor has a protected setter)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class P {
|
||||||
|
abstract public mixed $foo { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class C1 extends P {
|
||||||
|
public protected(set) mixed $foo { get => 1; set {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
class GrandC1 extends C1 {
|
||||||
|
public protected(set) mixed $foo { get => 2; set {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
class C2 extends C1 {
|
||||||
|
static function foo($c) { return $c->foo += 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(C2::foo(new GrandC1));
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
int(3)
|
26
Zend/tests/property_hooks/gh19044-2.phpt
Normal file
26
Zend/tests/property_hooks/gh19044-2.phpt
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
--TEST--
|
||||||
|
GH-19044: Protected properties must be scoped according to their prototype (common ancestor does not have a setter)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class P {
|
||||||
|
abstract public mixed $foo { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class C1 extends P {
|
||||||
|
public mixed $foo { get => 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class GrandC1 extends C1 {
|
||||||
|
public protected(set) mixed $foo { get => 2; set {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
class C2 extends C1 {
|
||||||
|
static function foo($c) { return $c->foo += 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(C2::foo(new GrandC1));
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
int(3)
|
24
Zend/tests/property_hooks/gh19044-3.phpt
Normal file
24
Zend/tests/property_hooks/gh19044-3.phpt
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
--TEST--
|
||||||
|
GH-19044: Protected properties must be scoped according to their prototype (abstract parent defining visibility only takes precedence)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class P {
|
||||||
|
abstract protected(set) mixed $foo { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class C1 extends P {
|
||||||
|
public protected(set) mixed $foo { get => 2; set {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
class C2 extends P {
|
||||||
|
public mixed $foo = 1;
|
||||||
|
|
||||||
|
static function foo($c) { return $c->foo += 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(C2::foo(new C1));
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
int(3)
|
28
Zend/tests/property_hooks/gh19044-4.phpt
Normal file
28
Zend/tests/property_hooks/gh19044-4.phpt
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
--TEST--
|
||||||
|
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)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class GP {
|
||||||
|
abstract mixed $foo { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class P extends GP {
|
||||||
|
abstract protected(set) mixed $foo { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class C1 extends P {
|
||||||
|
public protected(set) mixed $foo { get => 2; set {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
class C2 extends P {
|
||||||
|
public mixed $foo = 1;
|
||||||
|
|
||||||
|
static function foo($c) { return $c->foo += 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(C2::foo(new C1));
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
int(3)
|
28
Zend/tests/property_hooks/gh19044-5.phpt
Normal file
28
Zend/tests/property_hooks/gh19044-5.phpt
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
--TEST--
|
||||||
|
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)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class GP {
|
||||||
|
abstract mixed $foo { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class P extends GP {
|
||||||
|
abstract protected(set) mixed $foo { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class C1 extends P {
|
||||||
|
public protected(set) mixed $foo { get => 2; set {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
class C2 extends GP {
|
||||||
|
public mixed $foo = 1;
|
||||||
|
|
||||||
|
static function foo($c) { return $c->foo += 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(C2::foo(new C1));
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
int(3)
|
28
Zend/tests/property_hooks/gh19044-6.phpt
Normal file
28
Zend/tests/property_hooks/gh19044-6.phpt
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
--TEST--
|
||||||
|
GH-19044: Protected properties must be scoped according to their prototype (abstract parent has implicit set hook)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class GP {
|
||||||
|
public abstract mixed $foo { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class P extends GP {
|
||||||
|
public protected(set) mixed $foo { get => $this->foo; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class C1 extends P {
|
||||||
|
public protected(set) mixed $foo = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
class C2 extends P {
|
||||||
|
public protected(set) mixed $foo;
|
||||||
|
|
||||||
|
static function foo($c) { return $c->foo += 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(C2::foo(new C1));
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
int(2)
|
26
Zend/tests/property_hooks/gh19044-8.phpt
Normal file
26
Zend/tests/property_hooks/gh19044-8.phpt
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
--TEST--
|
||||||
|
GH-19044: Protected properties must be scoped according to their prototype (hooks variation)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class P {
|
||||||
|
abstract protected $foo { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class C1 extends P {
|
||||||
|
protected $foo = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
class C2 extends P {
|
||||||
|
protected $foo = 2;
|
||||||
|
|
||||||
|
static function foo($c) { return $c->foo; }
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(C2::foo(new C2));
|
||||||
|
var_dump(C2::foo(new C1));
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
int(2)
|
||||||
|
int(1)
|
|
@ -282,7 +282,7 @@ static zend_always_inline bool is_derived_class(const zend_class_entry *child_cl
|
||||||
static zend_never_inline int is_protected_compatible_scope(const zend_class_entry *ce, const zend_class_entry *scope) /* {{{ */
|
static zend_never_inline int is_protected_compatible_scope(const zend_class_entry *ce, const zend_class_entry *scope) /* {{{ */
|
||||||
{
|
{
|
||||||
return scope &&
|
return scope &&
|
||||||
(is_derived_class(ce, scope) || is_derived_class(scope, ce));
|
(ce == scope || is_derived_class(ce, scope) || is_derived_class(scope, ce));
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
@ -419,7 +419,7 @@ wrong:
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ZEND_ASSERT(flags & ZEND_ACC_PROTECTED);
|
ZEND_ASSERT(flags & ZEND_ACC_PROTECTED);
|
||||||
if (UNEXPECTED(!is_protected_compatible_scope(property_info->ce, scope))) {
|
if (UNEXPECTED(!is_protected_compatible_scope(property_info->prototype->ce, scope))) {
|
||||||
goto wrong;
|
goto wrong;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -514,7 +514,7 @@ wrong:
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ZEND_ASSERT(flags & ZEND_ACC_PROTECTED);
|
ZEND_ASSERT(flags & ZEND_ACC_PROTECTED);
|
||||||
if (UNEXPECTED(!is_protected_compatible_scope(property_info->ce, scope))) {
|
if (UNEXPECTED(!is_protected_compatible_scope(property_info->prototype->ce, scope))) {
|
||||||
goto wrong;
|
goto wrong;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -585,7 +585,7 @@ ZEND_API bool ZEND_FASTCALL zend_asymmetric_property_has_set_access(const zend_p
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return EXPECTED((prop_info->flags & ZEND_ACC_PROTECTED_SET)
|
return EXPECTED((prop_info->flags & ZEND_ACC_PROTECTED_SET)
|
||||||
&& is_protected_compatible_scope(prop_info->ce, scope));
|
&& is_protected_compatible_scope(prop_info->prototype->ce, scope));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void zend_property_guard_dtor(zval *el) /* {{{ */ {
|
static void zend_property_guard_dtor(zval *el) /* {{{ */ {
|
||||||
|
@ -2030,7 +2030,7 @@ ZEND_API zval *zend_std_get_static_property_with_info(zend_class_entry *ce, zend
|
||||||
zend_class_entry *scope = get_fake_or_executed_scope();
|
zend_class_entry *scope = get_fake_or_executed_scope();
|
||||||
if (property_info->ce != scope) {
|
if (property_info->ce != scope) {
|
||||||
if (UNEXPECTED(property_info->flags & ZEND_ACC_PRIVATE)
|
if (UNEXPECTED(property_info->flags & ZEND_ACC_PRIVATE)
|
||||||
|| UNEXPECTED(!is_protected_compatible_scope(property_info->ce, scope))) {
|
|| UNEXPECTED(!is_protected_compatible_scope(property_info->prototype->ce, scope))) {
|
||||||
if (type != BP_VAR_IS) {
|
if (type != BP_VAR_IS) {
|
||||||
zend_bad_property_access(property_info, ce, property_name);
|
zend_bad_property_access(property_info, ce, property_name);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue