[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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.