Table of Contents

PHP RFC: Deprecate ${} string interpolation

Proposal

PHP allows embedding variables in strings with double-quotes (") and heredoc in various ways.

  1. Directly embedding variables (“$foo”)
  2. Braces outside the variable (“{$foo}”)
  3. Braces after the dollar sign (“${foo}”)
  4. Variable variables (“${expr}”, equivalent to (string) ${expr})

Options 1 and 2 have their pros and cons. Options 3 and 4 are easily confused due to overlapping syntax, 3 is strictly less capable than 1 and 2, and 4 has completely different semantics (variable variables) that are rarely useful in string interpolation.

This RFC proposes to deprecate options 3 and 4 in PHP 8.2 and remove them in PHP 9.0.

var_dump("${foo}");
// Deprecated: Using ${} in strings is deprecated
 
var_dump("${(foo)}");
// Deprecated: Using ${} (variable variables) in strings is deprecated

That leaves only two string interpolation options: direct (simple variable only) and powerful (the one that does the most now).

Status quo

Options 1, 2 and 3 roughly try to achieve the same thing, embedding simple variables or chains in a string. They all support a different subset of features.

Simple variable interpolation

Options 1, 2, and 3 support embedding basic variables.

$foo = 'foo';
 
var_dump("$foo");
var_dump("{$foo}");
var_dump("${foo}");

Array-offset interpolation

Options 1, 2, and 3 allow accessing an array offset. Unfortunately, the syntax is not consistent.

$foo = ['bar' => 'bar'];
 
var_dump("$foo[bar]");
var_dump("{$foo['bar']}");
var_dump("${foo['bar']}");

It's worth noting that options 2 and 3 support arbitrary expressions between [ and ], whereas option 1 only supports simple strings with no spaces or other special characters.

Object property interpolation

Only syntax 1 and 2 allow accessing properties.

$foo = (object) ['bar' => 'bar'];
 
var_dump("$foo->bar");
var_dump("{$foo->bar}");

Method call interpolation

Only syntax 2 allows calling methods.

class Foo {
    public function bar() {
        return 'bar';
    }
}
 
$foo = new Foo();
var_dump("{$foo->bar()}");

Compound interpolation

Only syntax 2 allows chaining all of the above.

class Bar {
    public function baz() {
        return 'baz';
    }
}
 
$foo = ['bar' => new Bar()];
var_dump("{$foo['bar']->baz()}");

Option 4

PHP has a feature called Variable variables. It allows you to get a variable by name. The name can be a string stored in another variable.

$foo = 'Hello world!';
$bar = 'foo';
var_dump(${$bar});

The same works within strings. This is the option 4 described above.

$foo = 'world!';
$bar = 'foo';
var_dump("Hello ${$bar}");

As you might notice, this syntax clashes with option 3. If the term between the two braces is not compatible with option 3 PHP will interpret it as option 4 which has completely different semantics.

const foo = 'bar';
$foo = 'foo';
$bar = 'bar';
 
var_dump("${foo}");
//> foo
 
var_dump("${(foo)}");
//> bar

The braces switch from option 3 to 4 because parentheses are not allowed in option 3. This means foo is no longer interpreted as a variable but as a constant, and option 4 will then try to find a local variable by the name of that constant. This is incredibly unintuitive. Even though I cannot imagine a scenario where variable variables in strings are useful it's worth noting that they can still be expressed with option 2 as “{${expr}}” with a more consistent syntax.

Conclusion

Option 1 offers a simple “base case” for the most common situation and is widely used.

Option 2 offers the most robust syntax currently supported, and is widely used.

Option 3 offers a subset of the functionality of option 1 and 2, and is less widely used.

Option 4 offers functionality that is rarely if ever useful, and easily confused with option 3.

For all of the reasons above this RFC proposes to deprecate option 3 and 4 in PHP 8.2 and remove them in PHP 9.

Impact

An analysis of the top 1000 packagist repositories yielded 267 usages of option 3, 0 of option 4.

https://gist.github.com/iluuu1994/05427dd74100af8e41ebff3d4201742c

Migration path

The migration of option 3 to 2 is straight forward. Simply move the dollar sign.

"${foo}" => "{$foo}"
"${foo[expr]}" => "{$foo[expr]}"

The migration of option 4 to 2 is also straight forward. All it needs is adding braces around the interpolation.

"${foo->bar}" => "{${foo->bar}}"

The main challenge is differentiating between option 3 and 4 (which is a motivation of this RFC). In practice, 4 should be almost non-existent. Nonetheless, the deprecation message will contain the hint (variable variables) if option 4 is encountered.

I have created a simple script to automate this process.

Unchanged behavior

These things caused confusion in the discussion, but will continue working as they are.

Comparison to other languages

A number of other languages use ${foo} style string interpolation, most notably bash and JavaScript (in template literals). However, its behavior is different from that in PHP. In PHP, that syntax means variable variables. In JavaScript, it supports arbitrary expressions 2). In its current form, options 3 and 4 are of limited use, and confusing for users from other nearby languages as they behave quite differently.

Future scope

Options 1 and 2 are not perfect either. They only allow simple expressions on variables. A different RFC might propose to allow embedding arbitrary expressions into strings.

var_dump("{$:func()}")

If we decide to do that it would make sense to remove the less useful options first to not further add to the confusion, that's what this RFC is trying to achieve.

Vote

Voting starts 2022-04-08 and ends 2022-04-22.

As this is a language change, a 2/3 majority is required.

Deprecate ${} string interpolation in PHP 8.2 and remove them in PHP 9.0?
Real name Yes No
alec (alec)  
asgrim (asgrim)  
ashnazg (ashnazg)  
bmajdak (bmajdak)  
brzuchal (brzuchal)  
bwoebi (bwoebi)  
crell (crell)  
derick (derick)  
dmitry (dmitry)  
galvao (galvao)  
girgias (girgias)  
ilutov (ilutov)  
imsop (imsop)  
kalle (kalle)  
kguest (kguest)  
kocsismate (kocsismate)  
marandall (marandall)  
mbeccati (mbeccati)  
mcmic (mcmic)  
nicolasgrekas (nicolasgrekas)  
ocramius (ocramius)  
petk (petk)  
reywob (reywob)  
santiagolizardo (santiagolizardo)  
sebastian (sebastian)  
sergey (sergey)  
svpernova09 (svpernova09)  
thekid (thekid)  
theodorejb (theodorejb)  
timwolla (timwolla)  
trowski (trowski)  
twosee (twosee)  
Final result: 31 1
This poll has been closed.