Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:119397 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 55609 invoked from network); 23 Jan 2023 16:08:21 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 23 Jan 2023 16:08:21 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 5BB53180511 for ; Mon, 23 Jan 2023 08:08:20 -0800 (PST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS, T_SCC_BODY_TEXT_LINE 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-yw1-f179.google.com (mail-yw1-f179.google.com [209.85.128.179]) (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, 23 Jan 2023 08:08:19 -0800 (PST) Received: by mail-yw1-f179.google.com with SMTP id 00721157ae682-4fda31c3351so149339917b3.11 for ; Mon, 23 Jan 2023 08:08:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=CSWkyP0JlI2vn9erMyfdZ2Mr5dddgM+qbsHhkAXzmVE=; b=HoVzOr+Er8O3E1Az2m2eJ+1SLzBLIsHd3utCGmc0LMKISz5/2pohI+d36wA/6+R5Nx FWSxbTjbTVDjOx6ThUuctY7Gq5qSez9J6zFwBBT9ffZ03OtCWfsCZzXhqR3VRo+oWhC7 PjPWwws08KvV/audWw0JMBfqgaul7K444fcifidwXWed3C+sxSIrZyUXG7T1o4M1RU11 5HIBiSB8zlgEVyFW5GTqGMEG5eSGoMiG60h/MfmAFiwTe38GXSUkACxi4f7Cy9gcHatT V8lGvkDswZl4KDSfRg4cowHLRFXge5b0nEhQYKiteCVXWRwqxV9z8OmEAnpw044Azxz4 muDg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding: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=CSWkyP0JlI2vn9erMyfdZ2Mr5dddgM+qbsHhkAXzmVE=; b=coFoZEgOtU8k6gipsdxSQauVaXrQ0QfD5gFl7/ZhMnjzMaolcm1ntE0gvAAlJXC+N/ 6QFFqaWT3iwxC/7krKckTg/3nJ6T0HNGgzvI4O3m8DxU7uPMihfwPv6NeC3EnL4l96Lj dJeXCA148qUpxzEfiLTrNH+U34a9iLnWSAZel2RHN73ZKnyv1VgKocfqC5aG63dH8njZ kj3X1AkQHqiR8LnvjQgzp6ot7b9BiUx4icsAWzf5i8tyqm9dihk0ZpzrhHyq64wmmtez 9gb+jcd6uGOkcoxa+HXLJoEURau8TCQU5dCoDt6erTpDtyldEleBmkeTdXsh8YUORNOz xzhA== X-Gm-Message-State: AFqh2krLm1Z17uudHwQstYR6BTpedcQ7I8UBHd3/FHFwH+mMRNADDWvV cS1jvNo1ViUS2vkmeW8aPLOuXt87gDfQFl6QkBxDRadFU4dsQg== X-Google-Smtp-Source: AMrXdXuOfcI8FsvtLMEfEELjxI8zJYCSgQKobBiz6bdk6hpn+GoQq0a3QCsngzREFDLpUCsdNDjoAYzGgVSL1+JQ7zc= X-Received: by 2002:a81:4e4a:0:b0:4d6:3171:18e1 with SMTP id c71-20020a814e4a000000b004d6317118e1mr3007990ywb.230.1674490099051; Mon, 23 Jan 2023 08:08:19 -0800 (PST) MIME-Version: 1.0 References: <8f530a4a-bf48-4259-877e-16ebf8082936@app.fastmail.com> <88a59c46-9261-43f1-874d-ffc2f9caff32@app.fastmail.com> <99bc38dc-3d62-4a1e-a99c-e5daab3d779f@app.fastmail.com> In-Reply-To: <99bc38dc-3d62-4a1e-a99c-e5daab3d779f@app.fastmail.com> Date: Mon, 23 Jan 2023 17:08:08 +0100 Message-ID: To: Ollie Read Cc: =?UTF-8?Q?Bj=C3=B6rn_Larsson?= Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Subject: Re: [PHP-DEV] Introduce the abiltiy to use the first-call-callable syntax on non-static methods, statically From: landers.robert@gmail.com (Robert Landers) On Mon, Jan 23, 2023 at 4:39 PM Ollie Read wrote: > > You are absolutely correct. I guess the solution would be to handle it di= fferently in this case. > > Creating a closure from a static method would be fine, as it creates a st= atic closure, but when attempting to create a static closure from a non-sta= tic method, it would instead return a closure that errors if it isn't bound= to an appropriate object. You'd most likely want to restrict this to publi= c methods only, which would help with the security issues. > > There's already a check there that throws an error, so we can already tel= l the difference there, but the tricky part will be in the returned closure= . Perhaps something like "BindingClosure" that throws the static error when= attempting to call it unbound, or better yet, a more descriptive error abo= ut it requiring binding. > > Would that be feasible? > > On Sun, Jan 22, 2023, at 8:36 PM, Larry Garfield wrote: > > On Sun, Jan 22, 2023, at 11:45 AM, Ollie Read wrote: > > > Hello all, > > > > > > I've created a feature request issue on GitHub (here: > > > https://github.com/php/php-src/issues/10414), but I have been advised > > > that it's best to post here. > > > > > > What I would like to introduce/suggest, is the ability to create a > > > closure from a method using the first-class-callable syntax (eg: > > > MyClass::aMethod(...)), for a non-static method, statically. > > > > > > Currently, the following code causes an error. > > > > > > ``` > > > class Test { > > > public function test(): string { return 'test'; } > > > } > > > > > > $closure =3D Test::test(...); > > > ``` > > > > > > I understand why the error is thrown, but, and I'm unsure of the > > > specifics regarding this, I think we could delay the error until the > > > closure was called. The reason for this, is that closures can be boun= d, > > > so if you followed on from the code above, you could do the following= : > > > > > > ``` > > > $closure->bindTo(new Test); > > > $closure(); > > > ``` > > > > > > The above would bind the closure in $closure to the scope of an objec= t, > > > which in this case, is the class that the method belongs to. > > > > > > The best example I can think, for this, would be when filter a > > > collection of instances. If you were using a collection library, you > > > would currently have something like the following: > > > > > > ``` > > > $collection->filter(function (Str $string) { > > > return !$string->empty(); > > > }); > > > ``` > > > > > > Whereas it would be much nicer to have the following: > > > > > > ``` > > > $collection->filter(Str::empty(...)); > > > ``` > > > > > > In this situation, the collection library would be responsible for > > > binding the closure to the value it is iterating. > > > > So you'd implement this yourself elsewhere? > > > > class Str { > > public function empty(): bool { ... } > > } > > > > I don't see in this example how this is any better than what is already= currently possible: > > > > class Str { > > public static function empty(Str $s): bool { ... } > > } > > > > $collection->filter(Str::empty(...)); > > > > > I have limited experience with PHPs source, and C in general, but my > > > understanding would be that if we were creating a closure, we would > > > skip the check for the static method. The code responsible for handli= ng > > > the closure call would most require some additional functionality to > > > check if it was bound to a valid instance, returning an error if it > > > isn't, and then returning an error if it isn't bound at all and the > > > method isn't static. > > > > > > The more I think about it, the more I think this may require a new ty= pe > > > of Closure, or at least a runtime applied interface, to help develope= rs > > > determine whether a closure was created using first-class-callable > > > syntax. > > > > This is, I think, the important part here, and would be a prerequisite.= Right now there's no way (as far as I know) to differentiate a closure th= at is callable from one that would be callable if it were bound to an objec= t. That's generally not a huge deal in practice as unbound closures are no= t often used, but what you're suggesting would make them much more likely. = Also, a static closure cannot be bound, so you cannot just blindly bind wh= atever callable you're passed to $this, in your example. (Besides, blindly= binding a closure to $this sounds like a great security hole.) > > > > So for some variant of this to work, I think you'd first need to think = through how to (easily and without dipping into reflection) determine if a = closure object is bindable (static or not) and if it's already bound. Once= that's figured out, then we can see what, if any, short-hand way to make a= not-yet-bound closure makes sense. (Which could be FCC syntax or not, I d= on't know.) > > > > --Larry Garfield > > > > -- > > PHP Internals - PHP Runtime Development Mailing List > > To unsubscribe, visit: https://www.php.net/unsub.php > > > > > > --- > Best Regards, > *Ollie Read* FWIW, I think we can "throw out" any automatic binding, that just complicates things. In my mind, Test::Func(...) should be treated the same as ['Test', 'Func'] or 'Test::Func' until it is called and if some fancy framework wants to do something special with the closure, it can do so. FWIW, I didn't even know this syntax checked anything until today. I can think of a number of cases where delaying the error to actual execution is beneficial. 1. Just because the method/object doesn't exist, doesn't mean it won't exist by the time it is called -- this is PHP after all. 2. I can have a file with 100 million of these things without triggering any autoloading. 3. PHPStorm currently doesn't trigger this syntax as an error.