skip to content

In PHP, what are variable variables such as $$name, what are their syntax rules, and why do most codebases avoid them?

level: middleimportance: nice to knowfreq 20%

answer

  1. value becomes a variable name
  2. ${$a[1]} vs ${$a}[1]
  3. no superglobals or $this inside functions
  4. not the deprecated "${var}" form
  5. prefer an array keyed by name

basics

~20 s

$$name uses the value of $name as the name of another variable, so $name = 'temp'; $$name = 21; sets $temp. Braces resolve array ambiguity. Codebases avoid them because tools cannot follow them and user-controlled names can overwrite any variable.

solid answer

~50 s

A **variable variable** takes the value of one variable and uses it as the **name** of another: after `$field = 'humidity'; $$field = 64;` the variable `$humidity` exists. Braces delimit the name: `${'wind speed'}` allows names that are not valid identifiers, and with arrays `${$a[1]}` means "the variable named by `$a[1]`" while `${$a}[1]` means "element 1 of the variable named by `$a`". The same idea works for properties, `$obj->$prop` and `$obj->{$a . $b}`. They cannot reach **superglobals** inside functions or methods, and `$this` cannot be referenced dynamically. This `${...}` code syntax is not the `"${var}"` **string interpolation** deprecated in PHP 8.2. Teams avoid them because static analysis and IDEs cannot resolve the names, and a name taken from user input can overwrite any local variable. An array keyed by name does the same job safely.

go deeper

for a junior

Recall that $$name uses the value of $name as a variable name, and that arrays are usually the better tool.

for a middle

Explain the brace rules for arrays and properties, the superglobal and $this limits, and the difference from the deprecated string interpolation.

for a senior

Flag variable variables fed by input as a vulnerability, and refactor dynamic names to arrays or explicit mappings that tools can check.

for a principal

Ban dynamic variable names through static-analysis rules and treat existing uses as debt to remove during upgrades.

## The mechanism A normal variable has a fixed name in the source: `$temp`. A **variable variable** computes the name at runtime from another variable's value: ```php <?php $field = 'humidity'; $$field = 64; // creates $humidity echo $humidity; // 64 echo "{$$field}"; // 64, inside a string with braces ``` The manual's example uses `$a = 'hello'; $$a = 'world';`, after which both `$a` and `$hello` exist in the symbol table. ## Syntax rules | Form | Meaning | |---|---| | `$$name` | the variable whose name is the value of `$name` | | `${'wind speed'}` | a variable whose name is not a valid identifier | | `${$a[1]}` | the variable named by the value of `$a[1]` | | `${$a}[1]` | element `1` of the variable named by `$a` | | `$obj->$prop` | the property named by `$prop`, resolved in the calling scope | | `$obj->{$start . $end}` | the property named by an expression | | `$obj->{$arr}[1]` vs `$obj->{$arr[1]}` | element of a dynamic property vs property named by an element | The array rows exist because `$$a[1]` is ambiguous: the braces tell the parser which part is the name. ## Limits The manual lists two restrictions: - Variable variables **cannot be used with superglobal arrays** such as `$_GET` or `$_SERVER` **inside functions or class methods**. - **`$this`** is special and cannot be referenced dynamically. Dynamic property names follow the ordinary property rules: writing a property that the class does not declare is a **dynamic property**, deprecated since PHP 8.2 unless the class allows it. ## Not the deprecated interpolation PHP 8.2 deprecated two **string interpolation** forms, `"${var}"` and `"${expr}"`, recommending `"{$var}"` and `"{${expr}}"` instead. That deprecation is about strings. The `${...}` syntax in code shown above is still valid in PHP 8.5, and inside a string `"{$$field}"` remains the supported way to interpolate a variable variable. ## Why codebases avoid them 1. **Tools cannot follow them.** Static analysers, IDE rename refactorings and "find usages" see `$$field`, not `$humidity`. Code that reads `$humidity` looks like it reads an undefined variable. 2. **Security.** If the name comes from input, for example `$$key = $value` in a loop over request data, a client can overwrite any variable in scope, including flags such as `$isAdmin`. This is the same class of bug as importing request data into local variables. 3. **Readability.** A reader has to execute the code in their head to learn which variables exist. 4. **A better structure exists.** An array keyed by name gives the same flexibility with explicit boundaries: ```php <?php $readings = []; foreach (['temp' => 21.5, 'humidity' => 64] as $name => $value) { $readings[$name] = $value; // instead of $$name = $value } ``` ## Where they still appear - Legacy templates and form handlers. - Metaprogramming that generates property names, where `$obj->$prop` is often replaced today by explicit arrays, `match` on the name, or reflection. In an interview, the strong answer shows the mechanism, the brace rules for arrays, the superglobal and `$this` limits, and a clear reason to prefer arrays.

  • Why is `foreach ($_POST as $k => $v) { $$k = $v; }` dangerous?
    It lets the client choose variable names. A request field named `isAdmin` or `userId` overwrites the variable of that name in the current scope, which can bypass checks that run afterwards. Read the fields you expect explicitly, validate them, and keep the rest in an array.
  • What is the difference between `${$a[1]}` and `${$a}[1]`?
    `${$a[1]}` takes the value of `$a[1]` and uses it as a variable name. `${$a}[1]` takes the value of `$a` as the variable name and then reads index `1` of that variable. The braces resolve the ambiguity of `$$a[1]`, which the parser cannot interpret without them.

saying these in an interview costs you the question

  • Variable variables were removed in PHP 8.2.
  • $$key can read $_GET inside a function when $key is '_GET'.
  • $$a[1] always means element 1 of the variable named by $a.
  • Static analysers can resolve variable variables like normal names.
  • Assigning request fields with $$key = $value is safe after trimming them.