Re: PHP True Async RFC - Stage 2

From: Date: Wed, 19 Mar 2025 07:07:36 +0000
Subject: Re: PHP True Async RFC - Stage 2
References: 1 2 3 4 5 6  Groups: php.internals 
Request: Send a blank email to internals+get-126834@lists.php.net to get a copy of this message
Continuing the discussion from [[PHP-DEV] PHP True Async RFC - Stage 2]( https://discourse.thephp.foundation/t/php-dev-php-true-async-rfc-stage-2/1573/24 ): [quote="Rowan_Tommins_IMSoP, post:24, topic:1573"] I'm still confused why you started talking about how to implement "defer". Are you saying that "onExit" and "defer" are different things? Or that giving it dedicated syntax changes the implementation in some fundamental way? [/quote] I meant that the defer operator is needed in the language not only in the context of coroutines but in functions in general. In essence, defer is a shortened version of try-finally, which generates more readable code. Since that's the case, I shouldn't describe this operator in this RFC. However, onExit() and defer are essentially almost the same. [quote="Rowan_Tommins_IMSoP, post:24, topic:1573"] As with any other callable, it would be called with no arguments. It was just an illustration, because the function_name(...) syntax is one of many ways of creating a callable in PHP. [/quote] I think I understand your point. spawn <callable>, where callable is literally any expression that is considered callable. OK. Let's transform that to rules: The general syntax of the spawn operator: ```php spawn [with <scope>] <callable>[(<parameters>)]; ``` **where:** - **callable** a valid expression whose result can be invoked. Examples: ```php spawn "function_name"; // With variable $callable = fn() => sleep(5); spawn $callable; // With array expression spawn ([$this, "method"]); // With condition spawn ($condition ? $fun1 : $fun2); // As function result spawn (getClosure()); // Closures: spawn (function() use(): string { return "string"; }); spawn (fn() => sleep(5)); // with closure expression spawn (sleep(...))(5); // Simplified forms spawn sleep(5); spawn function() use(): string { return "string"; }; ``` - **parameters** a list of parameters that will be passed to the callable expression. - **scope** an expression that should resolve to an object of class Async\Scope. Examples: ```php spawn with $scope file_get_contents("http://localhost"); spawn with $this->scope file_get_contents("http://localhost"); spawn with $this->getScope() file_get_contents("http://localhost"); spawn with getScope() file_get_contents("http://localhost"); spawn with ($flag ? $scope1 : $scope2) file_get_contents("http://localhost "); ``` As you may have noticed, there is still a special form to avoid using "()". However, overall, this syntax looks quite cohesive. Did I get the idea right? [quote="Rowan_Tommins_IMSoP, post:24, topic:1573"] The only caveat is that calls using the nullsafe ?-> operator are forbidden - for reasons directly related to the feature, not because Nikita thought they were ugly. [/quote] Yes, that's exactly what we'd like to avoid. [quote="Rowan_Tommins_IMSoP, post:24, topic:1573"] But most likely you'd still want a separate syntax for a direct function call, however you want to spell it: [/quote] **Exactly!** It turns out that the expression spawn something(); can be interpreted as if something is a PHP constant rather than a function. This creates an ambiguous situation in the language: ```php const MY_CONST = "somefunction"; spawn MY_CONST(); // What happens here??? :) ``` With your help, we seem to have identified all the pitfalls. Now we can put them together and come up with the best solution.

« previous php.internals (#126834) next »