[php-src] Issue #23075: Magic property access skips __get() when another Fiber is suspended in __get() on the same object
[email protected] (itsckl)
| Newsgroups | php.bugs |
|---|---|
| Message-ID | <[email protected]> |
Issue: https://github.com/php/php-src/issues/23075
Author: itsckl
### Description
### Description
When two Fibers access the same undefined property on the same object, and the
first Fiber suspends inside `__get()`, the second Fiber does not invoke
`__get()`.
Instead, PHP emits an "Undefined property" warning and returns `null`.
After the first Fiber is resumed, its property access returns the correct value.
The access from the second Fiber is not a recursive property access. It is an
independent access with its own Fiber execution stack.
The issue reproduces with `php -n`, without loading a php.ini or third-party
extensions.
### Steps to reproduce
Save the following script as `fiber-magic-get.php`:
```php
<?php
declare(strict_types=1);
final class YieldingProperty
{
public function __get(string $name): int
{
Fiber::suspend();
return 8;
}
}
$value = new YieldingProperty();
$first = new Fiber(static function () use ($value): void {
var_dump($value->property);
});
$second = new Fiber(static function () use ($value): void {
var_dump($value->property);
});
$first->start();
$second->start();
$first->resume();
```
Run it without loading configuration or extensions:
```shell
php -n fiber-magic-get.php
```
### Actual result
```text
Warning: Undefined property: YieldingProperty::$property ...
NULL
int(8)
```
`__get()` is skipped for the second Fiber and the property access returns
`null`.
### Expected result
```text
int(8)
int(8)
```
Both property accesses should invoke `__get()` independently and return `8`.
Suspending one Fiber inside `__get()` should not make an access from another
Fiber appear recursive.
### Reproducibility
Always reproducible.
Tested with:
- PHP 8.3.33 CLI on Linux, using `php -n`
- PHP 8.4.7 CLI on Windows, using `php -n`
### Additional information
The issue only occurs when the Fibers share the same object.
The following controls do not fail:
- Giving each Fiber its own object
- Replacing the magic property access with a normal method call on the shared
object
The same problem also affects `__isset()` and null-coalescing property
expressions such as:
```php
$value = $object->property ?? 0;
```
Under contention, these expressions can silently return the fallback value
even though `__isset()` returns `true` and `__get()` returns the expected value.
This behavior suggests that the recursion guard for overloaded properties is
associated with the shared object and remains visible to other Fibers while a
magic property handler is suspended.
### PHP Version
```plain
PHP 8.3.33 (cli) (built: Jul 31 2026 12:56:07) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.33, Copyright (c) Zend Technologies
with Zend OPcache v8.3.33, Copyright (c), by Zend Technologies
PHP 8.4.7 (cli) (built: May 6 2025 14:12:45) (ZTS Visual C++ 2022 x64)
Copyright (c) The PHP Group
Zend Engine v4.4.7, Copyright (c) Zend Technologies
```
### Operating System
Windows 11, Plesk Linux ... 6.8.0-111-generic #111-Ubuntu SMP PREEMPT_DYNAMIC Sat Apr 11 23:16:02 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux