Double quotes and heredocs interpolate a variable, one array element ($book[title], key unquoted) or one property ($user->name). Anything deeper, such as a quoted key, a nested index, a method call or a variable followed by letters, needs braces: {$book['tags'][1]}, {$n}th. Braces cannot hold arbitrary expressions; compute first, or concatenate with ..
<?php
$book = ['title' => 'Dune', 'tags' => ['sf', 'classic']];
$user = (object) ['name' => 'Grace', 'cart' => new ArrayObject([1, 2])];
$n = 4;
echo "Simple: $book[title] by $user->name, the {$n}th\n"; // one level only
echo "Braces: {$book['tags'][1]}, {$user->cart->count()} items\n";
echo 'Concat: ' . $book['title'] . ' x' . $n * 2 . "\n"; // * binds before .Output
Simple: Dune by Grace, the 4th Braces: classic, 2 items Concat: Dune x8
The "${name}" form was deprecated in PHP 8.2 and is due for removal in 9.0; write "{$name}". In a loop, .= grows the buffer in place: a million appends took 18-20 ms on the test machine, against 43-49 ms for filling an array and calling implode(). Keep implode() for separators.