Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions Zend/tests/asymmetric_visibility/gh24250.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
--TEST--
GH-24250: protected(set)/private(set) not enforced after a write at the same call site was routed to __set()
--FILE--
<?php

class A {
public protected(set) int $x = 1;
public private(set) int $y = 1;
public function __construct(bool $unset) {
if ($unset) {
unset($this->x, $this->y);
}
}
public function __set($name, $value) {
echo "__set($name)\n";
}
}

function writeX(A $a, int $v) { $a->x = $v; }
function writeY(A $a, int $v) { $a->y = $v; }

writeX(new A(true), 1);
writeY(new A(true), 1);

$a = new A(false);
try {
writeX($a, 42);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
writeY($a, 42);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($a->x, $a->y);

class P {
public private(set) int $x = 1;
public private(set) readonly int $r;
public function __construct(bool $unset) {
if ($unset) {
unset($this->x, $this->r);
} else {
$this->r = 1;
}
}
public function __set($name, $value) {
echo "__set($name)\n";
}
}

class C extends P {
public function writeX(int $v) { $this->x = $v; }
public function __clone() { $this->r = 42; }
}

(new C(true))->writeX(1);
$c = new C(false);
try {
$c->writeX(42);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($c->x);

clone new C(true);
try {
clone $c;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
__set(x)
__set(y)
Cannot modify protected(set) property A::$x from global scope
Cannot modify private(set) property A::$y from global scope
int(1)
int(1)
__set(x)
Cannot modify private(set) property P::$x from scope C
int(1)
__set(r)
Cannot modify private(set) property P::$r from scope C
9 changes: 9 additions & 0 deletions Zend/zend_object_handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,15 @@ ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zva
} else {
guard = zend_get_property_guard(zobj, name);
error = (*guard) & IN_SET;
if (!error
&& cache_slot
&& (prop_info->flags & ZEND_ACC_PPP_SET_MASK)
&& !zend_asymmetric_property_has_set_access(prop_info)) {
/* The write is forwarded to __set() without checking set visibility.
* Reset cache slot to dodge fast path in next execution. */
CACHE_POLYMORPHIC_PTR_EX(cache_slot, NULL, NULL);
CACHE_PTR_EX(cache_slot + 2, NULL);
}
}
if (error) {
if ((prop_info->flags & ZEND_ACC_READONLY)
Expand Down
Loading