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
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:
php -n fiber-magic-get.php
Actual result
Warning: Undefined property: YieldingProperty::$property ...
NULL
int(8)
__get() is skipped for the second Fiber and the property access returns
null.
Expected result
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:
$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
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
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-partyextensions.
Steps to reproduce
Save the following script as
fiber-magic-get.php:Run it without loading configuration or extensions:
Actual result
__get()is skipped for the second Fiber and the property access returnsnull.Expected result
Both property accesses should invoke
__get()independently and return8.Suspending one Fiber inside
__get()should not make an access from anotherFiber appear recursive.
Reproducibility
Always reproducible.
Tested with:
php -nphp -nAdditional information
The issue only occurs when the Fibers share the same object.
The following controls do not fail:
object
The same problem also affects
__isset()and null-coalescing propertyexpressions such as:
Under contention, these expressions can silently return the fallback value
even though
__isset()returnstrueand__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
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