Hi Ilija,
Thanks for having a close look at the RFC.
Le ven. 8 mai 2026 à 14:56, Ilija Tovilo <tovilo.ilija@gmail.com> a écrit :
Hi Nicolas
Thanks for your proposal. I have some unpolished thoughts.
On Fri, May 1, 2026 at 11:38 AM Nicolas Grekas
<nicolas.grekas+php@gmail.com> wrote:
https://wiki.php.net/rfc/exists-magic-method
From what I can tell there are two primary objectives stated in the RFC:
- Solve the “null or undefined” ambiguity problem.
- Allow materializing properties within the “property existence
check” magic method, whether that be __isset() or __exists(), and
avoiding the __get() call that normally follows.
As for objective 1, it seems quite odd to me that __exists() indicates
“the property is declared and may or may not be null”, when is then
used for all the constructs that ask the distinct question “is the
property defined and not null?”, namely for isset(), empty() (negated)
and ??. That’s the question already precisely answered by __isset().
The behavior for disagreeing __isset() and __get() methods is odd, but
IMO this is a clear userland bug.
The other stated benefit is having the ability to check whether a
property exists even if it may be null by calling __exists() directly.
IMO the use-cases are narrow. For declared properties, there’s a
slightly unconventional solution: array_key_exists('property', (array)$object). For virtual properties; fair enough. Though these
could also use hooks nowadays.
As for objective 2, you’ve mentioned this is possible to implement for
__isset() but was rejected in GH-12695 due to complexity and
performance concerns. IMO a new method does not address these
concerns, it just moves them. If the __exists() method is intended to
replace __isset() long-term, then, once all code has migrated,
performance will not differ from adding the check to __isset(). [1]
Similarly, complexity for implementing the __isset() and __exists()
should be roughly equivalent. You solved the soundness issues
mentioned in the RFC by avoiding repetition of the entire function and
copying the relevant paths. I believe something similar should work
for __isset().
It’s also worth noting the main objection in GH-12695 was to treat it
as a bug and change behavior in stable versions.
To summarize, in my personal opinion it’s better to go back to the
original idea and add another property existence check after the
__isset() call that avoids calling __get() if the property has
materialized. From what I can tell, the has_property handler will also
need the same adjustments. I don’t think the stated benefits are
enough to warrant a full migration to a new method.
Ilija
[1] We also concluded in the issue that performance isn’t a real issue
anyway. __exists() would also perform worse due to having to call
__get() to verify the value is not null. This does make sure the
functions are in agreement, at the cost of an unnecessary call for
correct code.
I would say that the two goals of the RFC are:
- to indeed solve the “null or undefined” ambiguity problem.
- to provide a solid primitive for true equivalence between magic properties and regular ones.
About 1. it’s a long standing issue for PHP objects: libs like symfony/property-access / property-info have weird edge cases both in the implementation and in the behavior that are unfixable unless PHP provides a true way to separate between “null” and “undefined”. You mention hooks: those are great when the name of the properties are known ahead of time. But that doesn’t help when dealing with properties that are known only at runtime. I nowadays see magic accessors as generic property hooks. It’s a powerful capability offered by the engine and it should work just great - which it isn’t at the moment.
About 2. the canonical broken behavior is that an expression like “$foo ?? 123” MAY provide a NULL result. That’s a big WTH for the type-system. I agree that it should be considered a bug if one implements such a behavior by mistake. Yet it’s a foot-gun that the engine offered in the first place - and it also opens for hacks (if not cracks) of the language semantics. Something scary to me as a person engaged in making PHP better for years now. A flaw from the past worth fixing, like many others we already fixed along the years. Then yes, deprecating __isset may be considered later, separately - it’s just the cost for the community that needs to be considered separately, and in a later version of PHP.
You also wrote that we can fix GH-12695 by implementing the materialization check I added to __exists. It wasn’t obvious to me that this would be an acceptable move. That’s good to read. Yet this alone wouldn’t fix my main concerns above.
About the double call to __get when using isset($o->x) ? $o->x : $y that’s indeed true. Yet it’s not an issue to me: ?? is free from this issue, has exactly the same semantics and is what ppl prefer using anyway for this exact construct. In addition, __get calls usually start with a hashtable check and are thus super quick - the double-call can be made negligible in practice. Trading perf vs correctness in the case we discuss should clearly balance towards correctness, that’s the point of the RFC.
About array_key_exists('prop', (array)$object) that’s of no use for magic properties so that’s unrelated.
I could certainly reformulate the RFC with a wording that aligns more closely with this rationale. That wouldn’t change what I propose at the technical level, but that might help getting the points. Let me know.
Cheers,
Nicolas