In PHP, what can simple $var interpolation embed in a double-quoted string, and when do you need the {$expr} curly syntax?
answer
- one level only in simple syntax
- unquoted key: $arr[key]
- { must touch the $
- curly: chains, quoted keys, method calls
- no constants or static calls
basics
~10 sSimple 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 sIn 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
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 propertygo deeper
Recall that "$name" and "{$name}" both interpolate, and that method calls and quoted keys need the braces.
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 $.
Standardise on braces in templates, catch one-level interpolation bugs in review, and keep interpolation away from SQL, HTML and shell contexts.
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