Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:130812 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by lists.php.net (Postfix) with ESMTPS id EEE851A00BC for ; Sat, 9 May 2026 13:14:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1778332500; bh=kbj0EI5R/MMQkjorWTxYCkBwjdzq0yllI+uCDBr0lGU=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=k3WpZuQ4x6BJRthbF9EnjRZKGqBfea735GKSeVbwRwOg//JasNMsS367b2SN2s5N5 0Wrgwj4mrnXs6hcISmWjElo51775zCHHbkKfSz11e7U3r1jOIxLfdc6luzprzK0jSM 6/4+1XY/1soeeami+1NaJwlX94cYhMj2VTaWhgdoV8ITyi6o/rPkN26D2TMFrVzzpu Yo3CeYxOAoVt7KIsU0/a3Baiv5hrIhmBLBlQyYPEVfdqH9hR9svCZcU5JTeTu+jsgz NOpkbqDWTGD+eEfblj/ju6GLVfXV7YuF75s0HAiq/LNv32fFyWqeM60+tJjzmdbAvL 7qmZtAJ7F9qGw== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 371B4180077 for ; Sat, 9 May 2026 13:14:59 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on php-smtp4.php.net X-Spam-Level: * X-Spam-Status: No, score=1.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, FREEMAIL_REPLY,HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2, SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from mail-qv1-f42.google.com (mail-qv1-f42.google.com [209.85.219.42]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Sat, 9 May 2026 13:14:59 +0000 (UTC) Received: by mail-qv1-f42.google.com with SMTP id 6a1803df08f44-8b74b460d77so27588366d6.3 for ; Sat, 09 May 2026 06:14:53 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1778332493; cv=none; d=google.com; s=arc-20240605; b=EruJMI1hxffNy/Om1skXvtYmdWaMm7lV0YxSVlOICSI6FJlYFDYtsaO3QIwdgG4+Ts QV1PNbX+ZQaisVlTnt3BXDym5iRBxIsYQQxCdAiOm18QlazAfuQknQqWu7jQTx1C8HVV IdcMG0MceZXdNm2RTB3sy1CtISeX2v5OcmBECVnLzP5BEniugmHwOpGIv/TU9k2sOEYw GFMQuvrryMkh+STmCKQP9O5xHgZ1CTaTAW10jZ4PYkxZ7n06tbkoTIy+cQcPwDkpMV9q U+qxMHCzNJ3tgNXHOoqzlI1zN2UGThwV0qLSw4phAif4jSym38Swb6+y8RRg3pe+T30R SJDw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=kbj0EI5R/MMQkjorWTxYCkBwjdzq0yllI+uCDBr0lGU=; fh=+NLZrHMy4sRH65aY1iSif+khmD47Yd5y2czo1CxEPR4=; b=MET8Wq3DzlhkHiB/3+s1cZcEd7ZfbUTdgp2jsjEce24hMAOM0mn0u2KVdPXcd5sS0M ndk4hX282/21vtk2IuC/nsIQUQ5eaf6+WNXOhJd0UyyAIfueo7wyssI3EFLaU/ONUHTq ZFIkpiNTAjV73WXgTgnKyFUCaj1hpttstLjkHXx2JWX0m2jWNWIlMxQp1l/VRqMeyxvk /Y3FhG2AhRS1fmCf/BrS0sURxFdj1lLlrvtpWNtzL8/9P5xnWorWua4CHYUOkzcwtb3k s/4bKkhocxDb3AVucHjVzF3C5h9bGaokYtFm2BqA2d31w9i3UZwsadjkJgERG6aKKKtY v/zw==; darn=lists.php.net ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778332493; x=1778937293; darn=lists.php.net; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=kbj0EI5R/MMQkjorWTxYCkBwjdzq0yllI+uCDBr0lGU=; b=XcV7R+0uUdB+h0qizB48csA/SFi3wS2kAFeR/6Ud+RoWtKbDxtzRGnuefJyvITFp6D mEquBKmsNmAZrdt6TQYaBh4uLUUjxY3qL0zw2bKam2pMUthC1M2Df/97VFS2G9wSc+Wi iTe6U8k+Z9JJuKIyaLPubxo2mCm0eqvq7AbIsTAEbNyhba2ZQXn98qkLhLvfXSPzv8y4 iHm3CDW0/zW1VlvYSDddFG6WkFCpt5CzaRpKhZuL8NJWXQ4npzCuL2lIvUWbZyLPSKy4 Fa/G315I4OvjlovHx1+ZZ63pjrT7qwXGTU1v0oy1223YqQWbKGgFM2KcHfP9tKXiG7wu SxUw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778332493; x=1778937293; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=kbj0EI5R/MMQkjorWTxYCkBwjdzq0yllI+uCDBr0lGU=; b=iI2dpsD7yKomtui5GSUYu+5Lac80RsPtixfrl086difS0dWe9jokpQgLZ+Ptl1rLPN J75d36l+3roAd9rCUDcfdRbOC40KyjMIFogoZwFtroDzkHk+zL8xsO/+t1po4hONkSkP /vFPFWUcCMrBRZha7aDRBkjm6uqd9jntU6OFlSBU7K8//gb3FE9Gm+C0U31n7P56Pb2i TwOV1/M/aYYujG4VDnbX3v/t6dm9f7PZtU9CEoX0YkuvuAwyJip0d2xTaM6qc3ow4jvj QmqhgTbESvzy98w2G+934dn5kYw8MObXspRghGiIm37OkiiXYxrqVCOhGgoS3+ZVshvH rPsw== X-Gm-Message-State: AOJu0YyBt2Wsi1ZyQshK2OF2NFxPnv4RupDDLRaNfIfBl3+xnqg1PhQG lSowrE7S09uD3Qcb9PV7wXCzPYg4sLnEwZ6S3/JJs/64KiEIBkto0cer4anZlViKEGx0G2tblrK mbZFYy18PgqcPWDVHNnJbHTZB4+jTWdc= X-Gm-Gg: Acq92OHJYKkII6RLAAg1SMSPm/oML2xHxLlnHApKrpKPVvdBJoZVmMdm0JWwQmOs4sa WBibqzaoUNJGRu688DwjsPuhieH1nZu+7+4TYQUNtiSfL63yePwTGbVXqttTzmCVM0EmHQo7zIq SHDHEJWYu0OjwMOH5nWcuAZdmmWsCGYlLhF8GdcvWV/sZn+nTqUxLcsuStewc3V4uvNTZhD21K+ 18Hw/OtplJOxmquK38XDExcfJBFUyVbNzfS9V8ORZf6iKAP1dkIAhX2qpG0oy3XTbFHCFfUDAKT QRoQ9oIOtSROHxrav72WhlKRcvYibL6heid0vaSgrmyPjHS9QDXJjQl9zo7hlkW/WgTsEY7j68e qqrlM1aw+NzUDJ5bo+WEhRjIVEJ2ZiRuIpWjkSoUwvMZ9srw9O/n/vqowEgtxSACjbfEzcGGVEK sG3J4/G+84nSEeoMIxhy1ui2Ve+2Z2mHRY6sRd X-Received: by 2002:a05:620a:1720:b0:900:7247:f1a3 with SMTP id af79cd13be357-9090ecbdf6cmr319664785a.24.1778332493090; Sat, 09 May 2026 06:14:53 -0700 (PDT) Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 References: In-Reply-To: Date: Sat, 9 May 2026 15:14:42 +0200 X-Gm-Features: AVHnY4LZkh1wsAqnbv4JBE_Z5ymn9LT_3yx-WdT8E4FL56LkVirJ-Q2s0os5AsA Message-ID: Subject: Re: [PHP-DEV] [RFC] __exists(), a magic method for distinguishing "missing" from "set to null" To: Ilija Tovilo Cc: PHP Internals List Content-Type: multipart/alternative; boundary="0000000000008b11480651624cd4" From: nicolas.grekas+php@gmail.com (Nicolas Grekas) --0000000000008b11480651624cd4 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi Ilija, Thanks for having a close look at the RFC. Le ven. 8 mai 2026 =C3=A0 14:56, Ilija Tovilo a = =C3=A9crit : > Hi Nicolas > > Thanks for your proposal. I have some unpolished thoughts. > > On Fri, May 1, 2026 at 11:38=E2=80=AFAM Nicolas Grekas > wrote: > > > > https://wiki.php.net/rfc/exists-magic-method > > From what I can tell there are two primary objectives stated in the RFC: > > 1. Solve the "null or undefined" ambiguity problem. > 2. 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: 1. to indeed solve the "null or undefined" ambiguity problem. 2. 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 --0000000000008b11480651624cd4 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi Ilija,

Thanks for h= aving a close look at the RFC.

Le=C2=A0ven. 8 ma= i 2026 =C3=A0=C2=A014:56, Ilija Tovilo <tovilo.ilija@gmail.com> a =C3=A9crit=C2=A0:
Hi Nicolas

Thanks for your proposal. I have some unpolished thoughts.

On Fri, May 1, 2026 at 11:38=E2=80=AFAM Nicolas Grekas
<nic= olas.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:
1. Solve the "null or undefined" ambiguity problem.
2. 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 th= en
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<= br> __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<= br> 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:

1. to indeed solve the "null or undefined"= ; ambiguity problem.
2. to provide a solid primitive for true equivalen= ce between magic properties and regular ones.

About 1. it's a lo= ng standing issue for PHP objects: libs like symfony/property-access / prop= erty-info have weird edge cases both in the implementation and in the behav= ior that are unfixable unless PHP provides a true way to separate between &= quot;null" and "undefined". You mention hooks: those are gre= at when the name of the properties are known ahead of time. But that doesn&= #39;t help when dealing with properties that are known only at runtime. I n= owadays see magic accessors as generic property hooks. It's a powerful = capability offered by the engine and it should work just great - which it i= sn't at the moment.

About 2. the canonical broken behavior is th= at an expression like "$foo ?? 123" MAY provide a NULL result. Th= at's a big WTH for the type-system. I agree that it should be considere= d a bug if one implements such a behavior by mistake. Yet it's a foot-g= un 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 pers= on engaged in making PHP=C2=A0better for years now. A flaw from the past wo= rth fixing, like many others we already fixed along the years. Then yes, de= precating __isset may be considered later, separately - it's just the c= ost for the community that needs to be considered separately, and in a late= r version of PHP.

You also wrote that we can fix GH-12695 by impleme= nting 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 t= his alone wouldn't fix my main concerns above.

About the double = call to __get when using `isset($o->x) ? $o->x : $y` that's indee= d 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 exa= ct construct. In addition, __get calls usually=C2=A0start 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&#= 39;s unrelated.

I could certainly reformulate the RFC with a wo= rding that aligns more closely with this rationale. That wouldn't chang= e what I propose at the technical level, but that might help getting the po= ints. Let me know.=C2=A0

Cheers,
Nicolas=
--0000000000008b11480651624cd4--