Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:118934 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 65546 invoked from network); 31 Oct 2022 21:52:35 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 31 Oct 2022 21:52:35 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 944511804AC for ; Mon, 31 Oct 2022 14:52:34 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=0.6 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,FREEMAIL_REPLY, HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE, SPF_PASS,T_SCC_BODY_TEXT_LINE,URIBL_BLACK autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS15169 209.85.128.0/17 X-Spam-Virus: No X-Envelope-From: Received: from mail-ed1-f47.google.com (mail-ed1-f47.google.com [209.85.208.47]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Mon, 31 Oct 2022 14:52:34 -0700 (PDT) Received: by mail-ed1-f47.google.com with SMTP id v27so19404796eda.1 for ; Mon, 31 Oct 2022 14:52:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=gtpKTUkBNtK35hd0J6oboeYqPnSQBUbSHbPBzRRwJVw=; b=fGOULRSgBw+GrMcGOqeZyinGG0wHJUl98hnGshdgBrKJtgmBGHvrFuveo0IqbIqB2p zw5W+QhsTDy3k5VIeQiZjW6vBhDkJVInyxWVxLgkzuIQr05nER53wCFsa8FauODuvtQj gXgpWIlCiRPo8Fnb5EfhzURAcAAB1FDGWug87ydiVU10Zxk2eVW5tr74bibmlUwtu0CG qn0QGURQHx87eRZ5cpRnKaZMyRq24Q9k3u1eKjGgIzBkxT3AQsurDQKZ+WgYD4IJZgo7 rPBpi+kP2hzqCxjArIa8lQW/fzdWthTEQtHlgeDMwy/KLucnBVUSJU2UylNleGoj5XVf bfZQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=gtpKTUkBNtK35hd0J6oboeYqPnSQBUbSHbPBzRRwJVw=; b=Z8FkiLusgFU3/bo4azHA3Lgjns6PAyON2vxOWMvxHyzQ5b7jMAhfZcVazXODuTtnJb MpyNgbmdMEV+WpFjK/w4XDq1vvc4ApUwjwDLPsGNWhA20RS2wT1FxiHWEoCuZKE3QiwX 6vGA7GUkQ3LY3Yc2jP3+jDk2cdmm3sdPf4WzeeS5WZr/irTOkPHrAHSG+IxeX6nKZO7y errNx3aNdPmJ9eyDEPZGtiQg9/INfLcDtKBcmxNf+GHRywuBRwnJEU1KH3rEqYjE6YGw GUsFhYzfwJfKS4l9te6UslBCtti1zIqW7cFt/UywwGMYeNAI4V0neKSIiufb+ixwCvqS E/JQ== X-Gm-Message-State: ACrzQf1Umf4k6TvF/cEMp41kRf980KSCVGPEo+LbSAsEvmu9eDyg50eb Po7r+3AEf+ubPnhPFUMGg/P2W2lTwZLMc8912l0R+n5hIdlFDg== X-Google-Smtp-Source: AMsMyM4/3qhou7NzkOQ4SgbKyD5W8t8kT1LmONIw82xLt3s2X7fwDvj6WHkPZGJdRGxHCEwvkWf3N18eIuFihGxYv6E= X-Received: by 2002:aa7:d80a:0:b0:462:2c1c:8716 with SMTP id v10-20020aa7d80a000000b004622c1c8716mr16104982edq.185.1667253152766; Mon, 31 Oct 2022 14:52:32 -0700 (PDT) MIME-Version: 1.0 References: <21C5073D-3F39-49DA-8686-E027AE780793@joshbruce.dev> <1bc7759a-63b8-4b96-bb69-97b50f851307@www.fastmail.com> <94DC0839-E43B-4F3A-9105-8F3CB9D7BBC1@joshbruce.dev> In-Reply-To: Date: Mon, 31 Oct 2022 22:52:20 +0100 Message-ID: To: Josh Bruce Cc: php internals Content-Type: multipart/alternative; boundary="000000000000ebde0d05ec5b9f63" Subject: Re: [PHP-DEV] [RFC][Discussion] Objects can be declared falsifiable From: ocramius@gmail.com (Marco Pivetta) --000000000000ebde0d05ec5b9f63 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Since this topic fits like a glove, just dropping the "why I don't use `__toString()`" argument here: https://github.com/ShittySoft/symfony-live-berlin-2018-doctrine-tutorial/pu= ll/3#issuecomment-460085493 A falsifiable object is worse than `__toString()` in this context, IMO. Marco Pivetta https://twitter.com/Ocramius https://ocramius.github.io/ On Mon, 31 Oct 2022 at 20:38, Josh Bruce wrote: > Hello Interntals, > > Someone reached out to me outside of internals regarding the RFC I > submitted on being able to declare objects falsifiable, so, I decided to > update and re-enter the fray. > > I=E2=80=99ve updated the references section as many of the RFCs that were= under > discussion at the time have since been implemented. > > I still find myself in situations where having the capability would be > beneficial. Specifically, I=E2=80=99m noticing with API responses where I= want to > pass the body received from a ResponseInterface the object can check > itself. I currently use methods such as: > > ->isValid() > ->isInvalid() > ->toBool() > > And similar - the isValid and isInvalid() methods are just aliases of > toBool(), which speaks to the ability for adopters to make their interfac= es > compatible without breaking changes in their code. > > In the case of a conditional - looks like this: > > $obj =3D Object::init($response); > If ($obj->isValid()) { > // do something with $obj > } > > Or: > > If ( > $obj =3D Object::init($response) and > $obj->isValid() > ) { > // do something with $obj > } > > Would like to be able to do something like: > > If ($obj =3D Object::init($response)) { > // do something with $obj > } > > As of today, the last example is always true. You wouldn=E2=80=99t be abl= e to > create a guard clause for the negation: > > If ( > $obj =3D Object::init($response) and > $obj =3D=3D=3D false > ) { > // handle invalid object, which could include something like > $obj->failed() > } > > Cheers, > Josh > > > On Sep 26, 2020, at 5:20 PM, Josh Bruce wrote: > > > > Hey Internals, > > > > So, I received a message that said of the mail I should have received > from internals bounced; so, this is part test and part update. > > > > I=E2=80=99ve started mapping out call paths in the project that brought= this > concept to light. > > > > The project, Shoop, never uses is_bool() - instead it always uses empty= , > then reverses that for: > > > > 1. Booleans > > 2. Numbers > > 3. Arrays > > 4. Strings > > 5. stdClass or data-only classes > > 6. Json strings, which it treats as #5 > > > > I essentially get standard SPL behavior when I do this. > > > > For the object definition I can define two interfaces: > > > > 1. Falsifiable > > 2. Emptiable > > > > The checks go like this: > > > > 1. if the object implements Falsifiable: the __toBool method would be > called and would return the result. (This check would be inside is_bool() > and could use the empty() implementation) > > 2. if the object implements Emptiable: the __isempty method would be > called and return the result. (This check would be inside empty()) > > 3. else: standard output from is_bool() or empty() would be used when > passing an instance to either of those SPL functions, depending on which > the user is using. > > > > Because the concepts of emptiness and falsiness are so tightly coupled, > I=E2=80=99m wondering if it would be better to implement both with this R= FC?? > > > > Otherwise, Emptiable would be a future enhancement consideration. > > > > I=E2=80=99d like to hear what the rest of Internals thinks. > > > > Next for me: I=E2=80=99m going to finish solidifying the Shoop project = and make > sure my other projects can use latest and then continue going through the > tutorials from Nikita and others on doing development Internals. > > > > Cheers, > > Josh > > > >> On Aug 30, 2020, at 9:32 AM, Josh Bruce josh@joshbruce.dev>> wrote: > >> > >> Hey Tyson, > >> > >> This is great! Thank you so much, sincerely. > >> > >> Still slow goings, which is fine, we have at least a year. lol > >> > >> Static analyzers seem to be the biggest concern to date. > >> > >> Haven=E2=80=99t been able to get one running locally - though I=E2=80= =99ve only spent a > few minutes here and there; definitely on the list. > >> > >> A use case for this sort of thing is also a concern or question. I cam= e > across Pipeline from the PHP League and saw their interruptible processor > and was wondering if something like this would be helpful there - for > pipeline patterns in general: > https://github.com/thephpleague/pipeline/blob/master/src/InterruptiblePro= cessor.php > >> > >> While working on another project, saw this line from the PHP > array_filter docs: > >> > >> "If no callback is supplied, all entries of array equal to FALSE > (see converting to boolean) will be removed." > >> > >> I=E2=80=99m still field testing the latest iteration of my base projec= t, but > wanted to put a working (non-internals) implementation out there (note th= is > covers emptiness and falseness for the purposes of the project): > >> > >> Tests - > https://github.com/8fold/php-shoop/blob/master/tests/RfcObjectCanBeDeclar= edFalsifiableTest.php > >> > >> Interface - > https://github.com/8fold/php-shoop/blob/master/src/FilterContracts/Interf= aces/Falsifiable.php > - for our purposes the efToBool would be __toBool > >> > >> Default implementation - > https://github.com/8fold/php-shoop/blob/master/src/Shooped.php#L216 > >> > >> =E2=80=9CType system=E2=80=9D implementation - > https://github.com/8fold/php-shoop/blob/master/src/Filter/TypeAsBoolean.p= hp > >> > >> Cheers, > >> Josh > >> > >>> On Aug 9, 2020, at 3:59 PM, tyson andre > wrote: > >>> > >>> Hi Josh, > >>> > >>> I'd recommend looking at https://github.com/php/php-src/pull/5156 > (linked to in your RFC's reference) when implementing this PR. > >>> Additionally, I didn't see if this was brought up - PHP already has a > class which is falsifiable - SimpleXMLElement. > >>> > >>> You can see the source of ext/simplexml/simplexml.c for `_IS_BOOL` > >>> > >>> ``` > >>> static int sxe_object_cast_ex(zend_object *readobj, zval *writeobj, > int type) > >>> { > >>> php_sxe_object *sxe; > >>> xmlChar *contents =3D NULL; > >>> xmlNodePtr node; > >>> int rv; > >>> > >>> sxe =3D php_sxe_fetch_object(readobj); > >>> > >>> if (type =3D=3D _IS_BOOL) { > >>> node =3D php_sxe_get_first_node(sxe, NULL); > >>> if (node) { > >>> ZVAL_TRUE(writeobj); > >>> } else { > >>> ZVAL_BOOL(writeobj, !sxe_prop_is_empty(readobj)); > >>> } > >>> return SUCCESS; > >>> } > >>> ``` > >>> > >>> ``` > >>> static php_sxe_object* php_sxe_object_new(zend_class_entry *ce, > zend_function *fptr_count) > >>> intern->zo.handlers =3D &sxe_object_handlers; > >>> // ... elsewhere in ext/simplexml/simplexml.c > >>> sxe_object_handlers.cast_object =3D sxe_object_cast; > >>> ``` > >>> > >>> The default handlers would be overridden, so this would probably need > to fall back to the original handlers for types other than bool. > >>> Also, this might need to copy all of the handlers from the parent > class entry's zend_object_handlers, > >>> to meet the expected behavior of other cast types and other magic > behaviors from SimpleXMLElement and other classes continuing to work. > >>> > >>> From the perspective of static analysis, a few things to note: > >>> - Static analyzers such as psalm/phan currently deliberately don't > bother handling the possibility that `object` can be false even after it > was checked for falsiness. > >>> There's hundreds of other things that could be implemented, and > adding the special casing and performance overhead checking for FFI objec= ts > and SimpleXMLElement, subclasses of those is currently low priority > compared to those things. > >>> > >>> Code using SimpleXMLElement/FFI is generally limited to a few files > in practice. > >>> > >>> Definitely possible for analyzers to support this for known base > classes, though, and the priority would increase if the RFC passed. > >>> > >>> I am a maintainer of Phan. > >>> - For BC reasons, internal data structures such as ArrayObject > probably wouldn't get changed in any php release > >>> (e.g. could break code using falsiness check instead of null check)= . > >>> So this might lead to inconsistencies with newer extensions if half > of the data structures treat emptiness as false and half don't. > >>> - This feature may end up getting adopted in cases where it's > convenient but turns out prone to accidental bugs and later is deprecated > and removed. > >>> For example, > https://stackoverflow.com/questions/25031236/if-elem-vs-elem-is-not-none > was seen in python > >>> > >>> (e.g. `if (!$this->resultSetObject) { $this->resultSetObject =3D > slow_db_operation(); }` > >>> would not behave the way people would previously expect for most > objects (executed repeatedly instead of once)) > >>> > >>> ``` > >>> >>> function test(SimpleXMLElement $e) { > >>> // False positive RedundantCondition in psalm > >>> if ($e) { > >>> } > >>> } > >>> ``` > >>> > >>>> And I don=E2=80=99t know of a way to add an interface to new stdClas= s() - but > thought this might be a valid use case: > >>> > >>> `stdClass` isn't a final class. I assume they meant this > >>> > >>> ``` > >>> class EmptyStdClass extends stdClass implements Falsifiable { > >>> public function __toBool() { return false; } > >>> } > >>> function example(stdClass $s) { > >>> if (!$s) { throw new Exception("Previously impossible"); } > >>> } > >>> ``` > >>> > >>> Thanks, > >>> - Tyson > >>> -- > >>> PHP Internals - PHP Runtime Development Mailing List > >>> To unsubscribe, visit: https://www.php.net/unsub.php > >>> > >> > > > > --000000000000ebde0d05ec5b9f63--