skip to content

In PHP, what can simple $var interpolation embed in a double-quoted string, and when do you need the {$expr} curly syntax?

level: middleimportance: must knowfreq 50%

answer

  1. one level only in simple syntax
  2. unquoted key: $arr[key]
  3. { must touch the $
  4. curly: chains, quoted keys, method calls
  5. no constants or static calls

basics

~10 s

Simple syntax embeds a variable, one array element with an unquoted key or number, or one property: "$name", "$arr[0]", "$obj->prop". Anything deeper, quoted keys or method calls need curly syntax: "{$obj->a->b}", "{$arr['key']}".

solid answer

~40 s

In double quotes and heredocs, PHP recognises **simple** interpolation: `$name`, one array access with an unquoted key, a number or a variable (`$row[id]`, `$parts[0]`, `$parts[-1]`), or one property (`$user->email`). It stops after that one step, so `"$user->address->city"` interpolates only `$user->address` and prints `->city` literally, and a quoted key such as `"$row['id']"` is a parse error. The **curly** syntax `{$…}` accepts any variable expression that starts with `$`: nested arrays, chained properties, quoted keys, constants as keys and method calls, as in `"{$slot->start->format('H:i')}"`. The `{` must be immediately followed by `$`, otherwise the braces are literal. Class constants and plain function calls cannot be interpolated; concatenate them or use `sprintf()`.

code

php · 12 lines
php
<?php
declare(strict_types=1);

$slot = new DateTimeImmutable('2026-10-06 14:30');
$patient = new stdClass();
$patient->firstName = 'Ada';
$row = ['room' => 'B12', 'floor' => 2];

echo "Hi $patient->firstName, room $row[room] on floor $row[floor].\n";
echo "Your visit: {$slot->format('l, j F')} at {$slot->format('H:i')}.\n";
echo "Room code {$row['room']}-{$row['floor']}\n";
// "$row['room']" would be a parse error; "$slot->format()" would read a property

go deeper

for a junior

Recall that "$name" and "{$name}" both interpolate, and that method calls and quoted keys need the braces.

for a middle

Explain where simple syntax stops (one array level, one property, unquoted keys) and what the curly syntax adds, including the rule that { must touch the $.

for a senior

Standardise on braces in templates, catch one-level interpolation bugs in review, and keep interpolation away from SQL, HTML and shell contexts.

for a principal

Decide when string templates outgrow interpolation, moving to a template engine with context-aware escaping for anything users will see.

## Where interpolation happens **Interpolation** replaces variables written inside a string literal with their values. It happens in double-quoted strings and heredocs only. PHP offers two syntaxes, and knowing where the simple one stops is the whole question. ## Simple syntax When PHP meets a `$` followed by a valid name start, it reads the longest possible variable name, then optionally **one** of these accessors: | Written in the string | Meaning | |---|---| | `$name` | the variable | | `$parts[0]`, `$parts[-1]` | one element by integer offset (negative counts from the end) | | `$row[id]` | one element with the **unquoted** string key `id` | | `$row[$key]` | one element keyed by another variable | | `$user->email` | one property | | `$user?->email` | one property through the nullsafe operator | That is all. The consequences are the classic interview traps: 1. **Only one level.** `"$user->address->city"` interpolates `$user->address` and then prints `->city` as text; if `address` is an object without `__toString()`, PHP throws an `Error` ("Object of class … could not be converted to string"). `"$grid[0][1]"` interpolates `$grid[0]`, an array, which gives an "Array to string conversion" warning and the text `Array[1]`. 2. **Quoted keys fail.** `"$row['id']"` is a parse error; in simple syntax the key must be unquoted. 3. **Methods are not called.** `"$slot->format()"` reads a *property* called `format` (an "Undefined property" warning) and prints `()` literally. 4. **Names are greedy.** `"$hourh"` looks for a variable named `$hourh`. Letters, digits, underscores and bytes `0x80`–`0xFF` all continue a name, so even a non-ASCII character right after a variable can be swallowed. ## Curly syntax The **curly** (advanced) syntax wraps a normal PHP variable expression in `{` and `}`. It is recognised only when `{` is **immediately** followed by `$`: - `"{$user->address->city}"` — chained properties; - `"{$row['id']}"`, `"{$grid[0][1]}"` — quoted keys and nested arrays; - `"{$settings[DEFAULT_TZ]}"` — a constant used as a key; - `"{$slot->start->format('l, j F')}"` — method calls, with arguments; - `"{$hour}h"` — a clear end to the variable name. Some details: - `"{ $name}"` with a space prints literal braces around the value. - `"{\$name}"` prints the literal text `{$name}`. - `"{{$name}}"` prints the value wrapped in one pair of braces. - The expression must start with `$`. Class constants (`Clinic::PHONE`), static calls on a class name (`Clinic::phone()`) and plain function calls such as `strtoupper($x)` cannot be interpolated; assign them to a variable, concatenate, or use `sprintf()`. ## The same rules in an email template An appointment-reminder body mixes both styles: ```php $body = "Hi $patient->firstName,\n" . "your visit with {$doctor->title} {$doctor->lastName} is on " . "{$slot->start->format('l, j F')} at {$slot->start->format('H:i')}.\n"; ``` Simple syntax is enough for `$patient->firstName`; the method calls need braces. Many teams simply use braces for every interpolation, which removes the need to remember where simple syntax stops. ## Heredoc follows the same rules A heredoc body interpolates exactly like a double-quoted string, so every rule above applies inside it: one level for simple syntax, braces for chains, quoted keys and method calls. Longer templates make the one-level trap more likely, which is another reason to brace every value in a heredoc template. ## A quick checklist - Does the value come from a method call or a chained property? Use `{$…}`. - Does the key need quotes or a constant? Use `{$…}`. - Is the variable followed directly by letters, digits or `_`? Use `{$…}` to end the name. - Is the value a class constant or a function result? Assign it to a variable or use `sprintf()`. - Is the output HTML, SQL or a shell command? Escape or bind the value for that context first. ## Interpolation is not escaping Interpolated values are inserted exactly as they are. Putting user input into an HTML email body, a SQL string or a shell command this way creates an injection bug; each context needs its own escaping or parameter binding before the value reaches the string.

  • Why does "$row[room]" work in a string when $row[room] outside a string is an error?
    Inside a string, simple syntax treats an unquoted key as a string literal, so `"$row[room]"` reads `$row['room']`. Outside a string, `room` is parsed as a constant name, and an undefined constant throws an `Error` in PHP 8. Inside `{$…}` the normal rules apply again, so write `{$row['room']}` there.
  • How do you interpolate a class constant or the result of a function?
    You cannot directly, because curly syntax must start with `$`. Assign the value to a variable first, concatenate with `.`, or use `sprintf()` with a placeholder. A callable stored in a variable is the exception: `"{$format($date)}"` works because the expression starts with `$`.

saying these in an interview costs you the question

  • "$user->address->city" interpolates the whole property chain
  • "$row['id']" is the correct way to interpolate a string key
  • "$slot->format()" calls the method inside the string
  • "{ $name}" with a space works like "{$name}"
  • Interpolation escapes values, so it is safe in HTML