Change yield interpolation behaviour

php.internals

Кирилл Несмеянов

4 years ago
Hello Internals! At the moment, there is a feature/bug in the PHP that allows to use interpolation of generators. ``` $code = <<<EXAMPLE     Hello ${yield} EXAMPLE; ``` I suspect that initially this functionality was not thought out, but it partially works, which allows you to implement useful functionality. ``` [$query, $params] = sql(fn() => <<<SQL     SELECT * FROM users WHERE id = ${yield 42} OR id = ${yield 0xDEADBEEF} SQL); // Expected // $query = "SELECT * FROM users WHERE id = ? OR id = ?" // $params = [ 42, 0xDEADBEEF ] ``` When I say that the functionality was not thought out initially, I mean the behavior of generators within strings. For example, the following code, which should (seemingly) implement this functionality: ``` function sql(\Closure $expr) {     [$generator, $params] = [$expr(), $params];     while ($generator->valid()) {        $params[] = $generator->current(); // Get the value from "yield"        $generator->send('?'); // Insert placeholder     }     return [$generator->getReturn()]; } ``` Causes an error: ``` Warning: Undefined variable $? ``` That is, the expression "${yield 42}" expects back not the result of this expression, but the name of the variable. Therefore, a complete and workable implementation of such a functionality is as follows: https://gist.github.com/SerafimArts/2e7702620480fbce6c24bc87bfb9cb0e I think it makes sense to do something about it. I have two suggestions: 1) Forbid using "yield" inside strings 2) Expect not a variable name as a result of this expression, but a substitution value. What do you think?

Nikita Popov

4 years ago
On Fri, Oct 8, 2021 at 10:55 AM Nesmeyanov Kirill <nesk@xakep.ru> wrote:
> Hello Internals! > > > At the moment, there is a feature/bug in the PHP that allows to use > interpolation of generators. > > ``` > > $code = <<<EXAMPLE > > Hello ${yield} > > EXAMPLE; > > ``` > > I suspect that initially this functionality was not thought out, but it > partially works, which allows you to implement useful functionality. > > ``` > > [$query, $params] = sql(fn() => <<<SQL > > SELECT * FROM users WHERE id = ${yield 42} OR id = ${yield 0xDEADBEEF} > > SQL); > > // Expected > // $query = "SELECT * FROM users WHERE id = ? OR id = ?" > // $params = [ 42, 0xDEADBEEF ] > > ``` > > When I say that the functionality was not thought out initially, I mean > the behavior of generators within strings. For example, the following > code, which should (seemingly) implement this functionality: > > ``` > > function sql(\Closure $expr) > > { > > [$generator, $params] = [$expr(), $params]; > > while ($generator->valid()) { > > $params[] = $generator->current(); // Get the value from "yield" > > $generator->send('?'); // Insert placeholder > > } > > return [$generator->getReturn()]; > > } > > ``` > > Causes an error: > > ``` > > Warning: Undefined variable $? > > ``` > > > That is, the expression "${yield 42}" expects back not the result of > this expression, but the name of the variable. Therefore, a complete and > workable implementation of such a functionality is as follows: > https://gist.github.com/SerafimArts/2e7702620480fbce6c24bc87bfb9cb0e > > > I think it makes sense to do something about it. I have two suggestions: > > 1) Forbid using "yield" inside strings > > 2) Expect not a variable name as a result of this expression, but a > substitution value. >
This doesn't really have anything to do with yield. ${expr} is PHP's general variable-variable syntax, which looks up the variable with name returned by expr. The syntax also works inside strings in the form of "${expr}". Using "${yield $v}" is just a specific instance of the general pattern following the same rules. (This syntax has a special case that should be deprecated: "${label}" will be interpreted the same as "$label" instead, which is inconsistent with how it works everywhere else. This is also why ${yield} without argument will not perform a yield and instead look for a variable called $yield.) PHP unfortunately doesn't have a general expression interpolation syntax, you can only interpolate variables and certain variable-like constructs. Regards, Nikita

Кирилл Несмеянов

4 years ago
Yes you are right. The same thing happens if you put a function there. ``` function hi(): string {     return ‘World’; }   echo <<<MSG     Hello ${hi()} MSG; // Warning: Undefined variable $World! ```   For some reason, I did not take this case into account.   Although it seems to me such semantics are old and require removal from the language, I admit that there is code that uses it and changing the behavior will break a lot. Ideally, I would like to see a similar behavior: ``` echo "Hello ${ $var }"; // Hello World echo "Hello ${ 'Wo' . 'rld' }"; // same echo "Foo ${ some_foo(42) }"; echo "Foo ${ 23 + 42 }";  echo "Some ${ ClassName::method() >> 2 ?? $any }"; // etc ```     > >Пятница, 8 октября 2021, 12:22 +03:00 от Nikita Popov <nikita.ppv@gmail.com>: >  >On Fri, Oct 8, 2021 at 10:55 AM Nesmeyanov Kirill < nesk@xakep.ru > wrote: >  >> Hello Internals! >> >> >> At the moment, there is a feature/bug in the PHP that allows to use >> interpolation of generators. >> >> ``` >> >> $code = <<<EXAMPLE >> >> Hello ${yield} >> >> EXAMPLE; >> >> ``` >> >> I suspect that initially this functionality was not thought out, but it >> partially works, which allows you to implement useful functionality. >> >> ``` >> >> [$query, $params] = sql(fn() => <<<SQL >> >> SELECT * FROM users WHERE id = ${yield 42} OR id = ${yield 0xDEADBEEF} >> >> SQL); >> >> // Expected >> // $query = "SELECT * FROM users WHERE id = ? OR id = ?" >> // $params = [ 42, 0xDEADBEEF ] >> >> ``` >> >> When I say that the functionality was not thought out initially, I mean >> the behavior of generators within strings. For example, the following >> code, which should (seemingly) implement this functionality: >> >> ``` >> >> function sql(\Closure $expr) >> >> { >> >> [$generator, $params] = [$expr(), $params]; >> >> while ($generator->valid()) { >> >> $params[] = $generator->current(); // Get the value from "yield" >> >> $generator->send('?'); // Insert placeholder >> >> } >> >> return [$generator->getReturn()]; >> >> } >> >> ``` >> >> Causes an error: >> >> ``` >> >> Warning: Undefined variable $? >> >> ``` >> >> >> That is, the expression "${yield 42}" expects back not the result of >> this expression, but the name of the variable. Therefore, a complete and >> workable implementation of such a functionality is as follows: >> https://gist.github.com/SerafimArts/2e7702620480fbce6c24bc87bfb9cb0e >> >> >> I think it makes sense to do something about it. I have two suggestions: >> >> 1) Forbid using "yield" inside strings >> >> 2) Expect not a variable name as a result of this expression, but a >> substitution value. >> >This doesn't really have anything to do with yield. ${expr} is PHP's >general variable-variable syntax, which looks up the variable with name >returned by expr. The syntax also works inside strings in the form of >"${expr}". Using "${yield $v}" is just a specific instance of the general >pattern following the same rules. (This syntax has a special case that >should be deprecated: "${label}" will be interpreted the same as "$label" >instead, which is inconsistent with how it works everywhere else. This is >also why ${yield} without argument will not perform a yield and instead >look for a variable called $yield.) > >PHP unfortunately doesn't have a general expression interpolation syntax, >you can only interpolate variables and certain variable-like constructs. > >Regards, >Nikita     -- Kirill Nesmeyanov  

Christoph Becker

4 years ago
On 08.10.2021 at 12:57, Kirill Nesmeyanov wrote:
> Yes you are right. The same thing happens if you put a function there. > ``` > function hi(): string > { >     return ‘World’; > } >   > echo <<<MSG >     Hello ${hi()} > MSG; > // Warning: Undefined variable $World! > ``` >   > For some reason, I did not take this case into account. >   > Although it seems to me such semantics are old and require removal from the language, I admit that there is code that uses it and changing the behavior will break a lot. Ideally, I would like to see a similar behavior: > ``` > echo "Hello ${ $var }"; // Hello World > echo "Hello ${ 'Wo' . 'rld' }"; // same > echo "Foo ${ some_foo(42) }"; > echo "Foo ${ 23 + 42 }";  > echo "Some ${ ClassName::method() >> 2 ?? $any }"; > // etc > ```
See also <https://wiki.php.net/rfc/deprecate_dollar_brace_string_interpolation>.
-- Christoph M. Becker

Rowan Collins

4 years ago
On 08/10/2021 12:21, Christoph M. Becker wrote:
> See also > <https://wiki.php.net/rfc/deprecate_dollar_brace_string_interpolation>.
A couple of other relevant threads: * Adding whitespace inside the braces changes the interpretation (note that the mentioned behaviour of undefined constants has indeed changed in PHP 8.0): https://externals.io/message/92239 * A brief thread about adding a more general interpolation syntax: https://externals.io/message/111519 Regards,
-- Rowan Tommins [IMSoP]