Le ven. 29 mai 2026 à 14:42, Nicolas Grekas <[email protected]>
a écrit :

> Hi Tim,
>
> Le ven. 29 mai 2026 à 12:01, Tim Düsterhus <[email protected]> a écrit :
>
>> Hi
>>
>> Am 2026-05-09 15:14, schrieb Nicolas Grekas:
>> >> […]
>> >> need the same adjustments. I don't think the stated benefits are
>> >> enough to warrant a full migration to a new method.
>> >> […]
>> >
>> > […]
>>
>> I now finally got around to reading your RFC in-depth and have also read
>> through the related GH-12695 issue to get the full picture.
>>
>> I share Ilija's opinion in that this feels very much like a
>> “sledgehammer to crack a nut” approach to fix something that is
>> effectively the result of a userland implementation error in a feature
>> that nowadays has better replacements in many situations: Namely
>> forgetting to implement `__isset()` when `__get()` is implemented; and
>> not implementing a `null` check in `__isset()`. In fact the first issue
>> would not be solved by the proposal, since instead of forgetting to
>> implement `__isset()`, folks would just forget to implement
>> `__exists()`.
>>
>> Given that the RFC can naturally only ship in a new PHP version and
>> users will not upgrade right-away, it will take years until folks can
>> actually rely on the updated behavior and they will need to maintain
>> their existing workarounds until then. Specifically for the “lazy
>> proxies” use case that also was the motivation for GH-12695 my
>> understanding is that this is already solved in a much cleaner way since
>> PHP 8.4 with native lazy objects. Why would I want to wait for PHP 8.6
>> to be the baseline, when the better solution is already available with
>> 8.4? Similarly other “lazy initialization” use cases for individual
>> properties can already be solved with property hooks in a cleaner way.
>>
>> That leaves the “JsonRecord” use case, where the magic method doesn't do
>> anything by itself either, since folks would need to manually call it to
>> make the “exists but is null” distinction. It could just be a regular
>> method there - as already done in userland collection classes that just
>> have explicit `->has()` and `->get()` methods instead of relying on
>> magic methods. These notably also allow for much cleaner access to
>> fields that are not valid PHP identifiers (e.g. fields starting with a
>> digit or fields containing spaces).
>>
>> I would treat this as a documentation issue, where it's not clearly
>> specified that users are expected to implement a `null` check in
>> `__isset()` and that users are expected to also implement `__isset()`
>> when implementing `__get()`. Perhaps the latter could even be made a
>> warning. That leaves the issue in GH-12695 which could then be fixed as
>> a master-only bugfix. While this would still require folks to carry
>> their workarounds until they can rely on PHP 8.6, it would not make the
>> language any more complex by requiring folks to learn yet another
>> pattern.
>>
>
> Thanks for the review.
>
> Treating magic accessors as legacy conflicts with how widely-used PHP code
> actually works: Laravel Eloquent is built on __get/__set, and a long tail
> of infrastructure sits on the same primitive, with legitimate use of them
> (runtime-discovered properties typically).
> Also: Native lazy objects don't cover laziness on userland subclasses of
> internal classes, nor interface-based proxies or decorator patterns where
> the target shape is dynamic.
> Property hooks need declared property names, which is the constraint that
> will continue to drive users to magic accessors in the first place.
> If we agree magic accessors stay load-bearing (the alternative would be a
> deprecation path that nobody is proposing), fixing their broken documented
> semantics is maintainer responsibility.
>
> The underlying issue isn't a userland implementation error: even
> a perfectly null-checking __isset cannot distinguish "set to null"
> from "missing" on a magic property.
> The method returns one bit and is being asked two questions.
> The `??` RFC also explicitly defines `$x ?? $y` as `isset($x) ? $x : $y`.
> That contract is currently broken on magic properties. Documentation can't
> fix a contract; the engine has to.
> Patching __isset for GH-12695 alone doesn't restore it either; it would
> need to change isset() semantics, a universal BC break.
>
> On "people will just forget __exists": a class with only __get() today
> produces broken isset() (always false) and empty() (always true).
> Adding `__exists() { return true; }` fixes both for free. The "Recommended
> floor for classes with __get" section in the RFC walks through this.
> It is strictly less work than the existing __get + __isset pattern.
>
> The "->has() / ->get() methods" suggestion is missing the point by
> abandoning property syntax, which is the reason magic accessors exist in
> the first place.
>
> About __get-without-__isset (nor __exists), a warning could be fine to me
> as an adjacent diagnostic.
> I'd defer this to a follow up RFC, because this needs its own impact
> analysis and is something related yet separate.
>
> On the master-only property-materialization fix: as I noted to Ilija, I'm
> fine with decoupling it from this RFC.
> The remaining motivations (the structural ambiguity, the
> documented-contract violation, the early-adoption convention via
> method_exists()) stand on their own.
>
> On the timeline concern: on PHP 8.5 and earlier, __exists() is a regular
> method, so libraries can already probe method_exists($obj, '__exists') and
> dispatch through it as a convention today. Adoption doesn't have to wait
> for PHP 8.6 to be universally deployed.
>

On the materialization aspect, I just submitted
https://github.com/php/php-src/pull/22181
It'd be great to have it reviewed/merged ASAP as that'd simplify the
discussion a bit here.
[image: :pray:] and thanks to anyone who can help make this happen.

Nicolas

Reply via email to