diff --git a/package.xml b/package.xml
index 0370ecb045..983391b5a9 100644
--- a/package.xml
+++ b/package.xml
@@ -182,6 +182,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
+
+
@@ -2153,6 +2155,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
+
+
@@ -2255,6 +2259,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
+
+
diff --git a/src/Tokenizers/PHP.php b/src/Tokenizers/PHP.php
index 4b0bf76550..5f2dc2c8c5 100644
--- a/src/Tokenizers/PHP.php
+++ b/src/Tokenizers/PHP.php
@@ -789,7 +789,8 @@ protected function tokenize($string)
if ($subTokenIsArray === true) {
$tokenContent .= $subToken[1];
- if ($subToken[1] === '{'
+ if (($subToken[1] === '{'
+ || $subToken[1] === '${')
&& $subToken[0] !== T_ENCAPSED_AND_WHITESPACE
) {
$nestedVars[] = $i;
diff --git a/tests/Core/Tokenizer/DoubleQuotedStringTest.inc b/tests/Core/Tokenizer/DoubleQuotedStringTest.inc
new file mode 100644
index 0000000000..62535b1e41
--- /dev/null
+++ b/tests/Core/Tokenizer/DoubleQuotedStringTest.inc
@@ -0,0 +1,52 @@
+bar";
+/* testProperty2 */
+"{$foo->bar}";
+
+/* testMethod1 */
+"{$foo->bar()}";
+
+/* testClosure1 */
+"{$foo()}";
+
+/* testChain1 */
+"{$foo['bar']->baz()()}";
+
+/* testVariableVar1 */
+"${$bar}";
+/* testVariableVar2 */
+"${(foo)}";
+/* testVariableVar3 */
+"${foo->bar}";
+
+/* testNested1 */
+"${foo["${bar}"]}";
+/* testNested2 */
+"${foo["${bar['baz']}"]}";
+/* testNested3 */
+"${foo->{$baz}}";
+/* testNested4 */
+"${foo->{${'a'}}}";
+/* testNested5 */
+"${foo->{"${'a'}"}}";
+
+/* testParseError */
+"${foo["${bar
diff --git a/tests/Core/Tokenizer/DoubleQuotedStringTest.php b/tests/Core/Tokenizer/DoubleQuotedStringTest.php
new file mode 100644
index 0000000000..cc9fe49ec4
--- /dev/null
+++ b/tests/Core/Tokenizer/DoubleQuotedStringTest.php
@@ -0,0 +1,136 @@
+
+ * @copyright 2022 Squiz Pty Ltd (ABN 77 084 670 600)
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Tokenizer;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+
+class DoubleQuotedStringTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test that double quoted strings contain the complete string.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param string $expectedContent The expected content of the double quoted string.
+ *
+ * @dataProvider dataDoubleQuotedString
+ * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
+ *
+ * @return void
+ */
+ public function testDoubleQuotedString($testMarker, $expectedContent)
+ {
+ $tokens = self::$phpcsFile->getTokens();
+
+ $target = $this->getTargetToken($testMarker, T_DOUBLE_QUOTED_STRING);
+ $this->assertSame($expectedContent, $tokens[$target]['content']);
+
+ }//end testDoubleQuotedString()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testDoubleQuotedString()
+ *
+ * @return array
+ */
+ public function dataDoubleQuotedString()
+ {
+ return [
+ [
+ 'testMarker' => '/* testSimple1 */',
+ 'expectedContent' => '"$foo"',
+ ],
+ [
+ 'testMarker' => '/* testSimple2 */',
+ 'expectedContent' => '"{$foo}"',
+ ],
+ [
+ 'testMarker' => '/* testSimple3 */',
+ 'expectedContent' => '"${foo}"',
+ ],
+ [
+ 'testMarker' => '/* testDIM1 */',
+ 'expectedContent' => '"$foo[bar]"',
+ ],
+ [
+ 'testMarker' => '/* testDIM2 */',
+ 'expectedContent' => '"{$foo[\'bar\']}"',
+ ],
+ [
+ 'testMarker' => '/* testDIM3 */',
+ 'expectedContent' => '"${foo[\'bar\']}"',
+ ],
+ [
+ 'testMarker' => '/* testProperty1 */',
+ 'expectedContent' => '"$foo->bar"',
+ ],
+ [
+ 'testMarker' => '/* testProperty2 */',
+ 'expectedContent' => '"{$foo->bar}"',
+ ],
+ [
+ 'testMarker' => '/* testMethod1 */',
+ 'expectedContent' => '"{$foo->bar()}"',
+ ],
+ [
+ 'testMarker' => '/* testClosure1 */',
+ 'expectedContent' => '"{$foo()}"',
+ ],
+ [
+ 'testMarker' => '/* testChain1 */',
+ 'expectedContent' => '"{$foo[\'bar\']->baz()()}"',
+ ],
+ [
+ 'testMarker' => '/* testVariableVar1 */',
+ 'expectedContent' => '"${$bar}"',
+ ],
+ [
+ 'testMarker' => '/* testVariableVar2 */',
+ 'expectedContent' => '"${(foo)}"',
+ ],
+ [
+ 'testMarker' => '/* testVariableVar3 */',
+ 'expectedContent' => '"${foo->bar}"',
+ ],
+ [
+ 'testMarker' => '/* testNested1 */',
+ 'expectedContent' => '"${foo["${bar}"]}"',
+ ],
+ [
+ 'testMarker' => '/* testNested2 */',
+ 'expectedContent' => '"${foo["${bar[\'baz\']}"]}"',
+ ],
+ [
+ 'testMarker' => '/* testNested3 */',
+ 'expectedContent' => '"${foo->{$baz}}"',
+ ],
+ [
+ 'testMarker' => '/* testNested4 */',
+ 'expectedContent' => '"${foo->{${\'a\'}}}"',
+ ],
+ [
+ 'testMarker' => '/* testNested5 */',
+ 'expectedContent' => '"${foo->{"${\'a\'}"}}"',
+ ],
+ [
+ 'testMarker' => '/* testParseError */',
+ 'expectedContent' => '"${foo["${bar
+',
+ ],
+ ];
+
+ }//end dataDoubleQuotedString()
+
+
+}//end class