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
