After an upgrade to PHP 8.2 or later, logs fill with 'Using ${var} in strings is deprecated' notices; what triggers them and how do you fix each form?
answer
- deprecated since 8.2, still in 8.5
- ${name} and ${name['key']} become {$...}
- ${expr} is a variable variable
- {${expr}} keeps that behaviour
- raised at compile time
basics
~10 sPHP 8.2 deprecated the ${...} interpolation syntax. "${name}" and "${name['key']}" become "{$name}" and "{$name['key']}"; any other "${expr}" is a variable variable and becomes "{${expr}}". PHP 8.5 still only deprecates it.
solid answer
~40 sThe dollar-brace form has three shapes, and PHP 8.2 deprecated all of them in strings. `"${name}"` and `"${name[expr]}"` mean the same as `$name` and `$name[expr]`; the notice is "Using ${var} in strings is deprecated, use {$var} instead", and the fix is to move the dollar inside the brace. Anything else inside `${…}` is evaluated as an expression whose **result is a variable name**, a variable variable; that raises "Using ${expr} (variable variables) in strings is deprecated, use {${expr}} instead", and `{${expr}}` keeps the exact behaviour. The notices are emitted when the file is compiled, so they appear once per compile, not once per call. A regex search for `\$\{` in string literals, or an automated fixer, handles a codebase in one pass. In PHP 8.5 the syntax is still only deprecated.
code
php · 15 lines<?php
declare(strict_types=1);
$lastName = 'Lovelace';
$slot = ['time' => '14:30'];
$field = 'lastName';
$patient = ['lastName' => 'Lovelace'];
// Deprecated since PHP 8.2 (E_DEPRECATED at compile time):
// "Dear ${lastName}, see you at ${slot['time']}."
// "Dear ${$field}"
echo "Dear {$lastName}, see you at {$slot['time']}.\n"; // simple shapes fixed
echo "Dear {${$field}}\n"; // same variable-variable behaviour
echo "Dear {$patient[$field]}\n"; // better: explicit lookupgo deeper
Recall that "${name}" is deprecated since PHP 8.2 and that "{$name}" is the replacement.
Distinguish the ${var} and ${var[...]} shapes from the ${expr} variable-variable shape, and give the correct fix for each.
Plan the cleanup after an upgrade: find hits, fix simple shapes with a fixer in one commit, rewrite variable-variable lookups, and lock it in CI.
Use deprecation sweeps as scheduled upgrade work with tooling, so the codebase stays ready for the next major release instead of piling up notices.
## What was deprecated PHP has long accepted three ways to interpolate a variable in a double-quoted string or heredoc: plain `$name`, the curly syntax `{$name}`, and the **dollar-brace** syntax `${name}`. PHP 8.2 deprecated the dollar-brace syntax in strings because it has two confusingly different meanings depending on what is inside the braces. It remains deprecated, not removed, in PHP 8.5. The deprecation is raised by the compiler, so each notice appears when a file is compiled. With OPcache enabled that is typically once per file per cache fill, not on every request, because the default `opcache.record_warnings=0` does not replay compile-time diagnostics on cache hits; without OPcache, every request that compiles the file triggers it again. ## The three shapes and their fixes | Written | Means | Notice | Fix | |---|---|---|---| | `"${name}"` | the variable `$name` | Using ${var} in strings is deprecated, use {$var} instead | `"{$name}"` | | `"${name['key']}"` | `$name['key']` | same as above | `"{$name['key']}"` | | `"${expr}"`, e.g. `"${'na' . 'me'}"` | the variable whose **name** is the value of `expr` | Using ${expr} (variable variables) in strings is deprecated, use {${expr}} instead | `"{${expr}}"` | The first two are purely cosmetic: moving the `$` inside the brace gives identical output. The third is a **variable variable**: PHP evaluates the expression, takes the resulting string as a variable name and reads that variable. The suggested `{${expr}}` preserves that behaviour exactly, but it is usually a sign the code should be rewritten. ## Why the shapes are easy to confuse - In `"${name}"`, `name` is *not* a constant; it is the variable name. - In `"${$field}"`, the expression `$field` is evaluated first, so if `$field` is `'firstName'`, the string contains the value of `$firstName`. - In `"${name . 'Suffix'}"`, `name` *is* parsed as a constant inside an expression, which in PHP 8 throws an `Error` if no such constant exists. A mechanical search-and-replace that turns every `${` into `{$` is therefore correct for the first two shapes and wrong for the third. ## Fixing a large codebase A practical sequence for a team that has just upgraded: 1. **Find them.** Search string literals and heredocs for `${`. The deprecation notices themselves carry file and line numbers, and running the test suite with deprecations reported collects most of them. 2. **Classify each hit.** A bare name, or a name followed by one `[…]`, is the simple shape. Anything else is a variable variable. 3. **Fix the simple shapes mechanically** by moving the `$` inside the brace. Code-style fixers have rules for this, which makes the change a reviewable, behaviour-free commit. 4. **Rewrite the variable-variable cases.** Replace `"Hello ${$field}"` with an explicit array lookup such as `"Hello {$patient[$field]}"`, so the data lives in an array instead of in dynamically named variables. 5. **Keep it fixed.** Enable the fixer rule in CI so new `${` interpolations cannot come back. ## An email-template example Old reminder templates often looked like this: ```php $text = "Dear ${title} ${lastName}, see you at ${slot['time']}."; ``` All three are the simple shape and become `{$title}`, `{$lastName}` and `{$slot['time']}`. A template engine that picked fields by name, `"${$field}"`, is the variable-variable shape and is better replaced by `{$fields[$field]}`. ## Checking the fix After the cleanup, confirm nothing changed except the syntax: - Run the tests with deprecations reported as failures, so a missed `${` fails the build. - Diff rendered output for a few representative templates, such as a reminder email, before and after the change; the simple shapes must produce byte-identical text. - Review every variable-variable rewrite by hand, because those are the only ones where the fix can change behaviour. ## What is not affected - `{$name}` and plain `$name` interpolation are unchanged. - `${'name'}` **outside** a string, the variable-variable syntax in normal code, is not part of this deprecation. - Nowdoc and single-quoted strings never interpolated, so `${name}` inside them is just text.
- Why is replacing every ${ with {$ not always safe?It is correct for `${name}` and `${name[...]}`, which mean `$name` and `$name[...]`. Any other `${expr}` is a variable variable: `${$field}` reads the variable whose name is the value of `$field`. A blind replace happens to work for that one case, but `${'na' . 'me'}` would become `{$'na' . 'me'}`, which does not compile. Use `{${expr}}`, or rewrite the lookup explicitly.
- Do these deprecation notices cost anything per request?They are raised when a file is compiled. With OPcache holding the compiled script, a file is compiled rarely, so the notice appears rarely, unless `opcache.record_warnings` is enabled to replay compile warnings on cache hits. Without OPcache, or after cache resets, every compile repeats it. Either way the fix is cheap and mechanical, so the notices are best removed rather than silenced.
saying these in an interview costs you the question
- PHP 8.5 removed the ${var} syntax entirely
- "{$name}" interpolation is also deprecated
- "${name}" reads the constant called name
- Replacing every ${ with {$ is always behaviour-preserving
- The notice is raised on every call of the function