-
Notifications
You must be signed in to change notification settings - Fork 154
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #212 from magento-l3/ACP2E-1868
ACP2E-1868: Move sniff PHPCompatibility.TextStrings.RemovedDollarBraceStringEmbeds from PHPCompatibility to Magento Coding Standards
- Loading branch information
Showing
5 changed files
with
521 additions
and
0 deletions.
There are no files selected for viewing
134 changes: 134 additions & 0 deletions
134
Magento2/Sniffs/PHPCompatibility/RemovedDollarBraceStringEmbedsSniff.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
<?php | ||
/** | ||
* PHPCompatibility, an external standard for PHP_CodeSniffer. | ||
* | ||
* @package PHPCompatibility | ||
* @copyright 2012-2022 PHPCompatibility Contributors | ||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3 | ||
* @link https://github.com/PHPCompatibility/PHPCompatibility | ||
*/ | ||
|
||
namespace Magento2\Sniffs\PHPCompatibility; | ||
|
||
use PHP_CodeSniffer\Files\File; | ||
use PHPCompatibility\Sniff; | ||
use PHPCSUtils\Utils\GetTokensAsString; | ||
use PHPCSUtils\Utils\TextStrings; | ||
|
||
/** | ||
* Detect use of select forms of variable embedding in heredocs and double strings as deprecated per PHP 8.2. | ||
* | ||
* > PHP allows embedding variables in strings with double-quotes (") and heredoc in various ways. | ||
* > 1. Directly embedding variables (`$foo`) | ||
* > 2. Braces outside the variable (`{$foo}`) | ||
* > 3. Braces after the dollar sign (`${foo}`) | ||
* > 4. Variable variables (`${expr}`, equivalent to `(string) ${expr}`) | ||
* > | ||
* > [...] to deprecate options 3 and 4 in PHP 8.2 and remove them in PHP 9.0. | ||
* | ||
* PHP version 8.2 | ||
* PHP version 9.0 | ||
* | ||
* @link https://wiki.php.net/rfc/deprecate_dollar_brace_string_interpolation | ||
* | ||
* @since 10.0.0 | ||
*/ | ||
class RemovedDollarBraceStringEmbedsSniff extends Sniff | ||
{ | ||
|
||
/** | ||
* Returns an array of tokens this test wants to listen for. | ||
* | ||
* @since 10.0.0 | ||
* | ||
* @return array | ||
*/ | ||
public function register() | ||
{ | ||
return [ | ||
\T_DOUBLE_QUOTED_STRING, | ||
\T_START_HEREDOC, | ||
\T_DOLLAR_OPEN_CURLY_BRACES, | ||
]; | ||
} | ||
|
||
/** | ||
* Processes this test, when one of its tokens is encountered. | ||
* | ||
* @since 10.0.0 | ||
* | ||
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. | ||
* @param int $stackPtr The position of the current token in the | ||
* stack passed in $tokens. | ||
* | ||
* @return void|int Void or a stack pointer to skip forward. | ||
*/ | ||
public function process(File $phpcsFile, $stackPtr) | ||
{ | ||
if ($this->supportsAbove('8.2') === false) { | ||
return; | ||
} | ||
|
||
$tokens = $phpcsFile->getTokens(); | ||
|
||
/* | ||
* Defensive coding, this code is not expected to ever actually be hit since PHPCS#3604 | ||
* (included in 3.7.0), but _will_ be hit if a file containing a PHP 7.3 indented heredoc/nowdocs | ||
* is scanned with PHPCS on PHP < 7.3. People shouldn't do that, but hey, we can't stop them. | ||
*/ | ||
if ($tokens[$stackPtr]['code'] === \T_DOLLAR_OPEN_CURLY_BRACES) { | ||
// @codeCoverageIgnoreStart | ||
if ($tokens[($stackPtr - 1)]['code'] === \T_DOUBLE_QUOTED_STRING) { | ||
--$stackPtr; | ||
} else { | ||
// Throw an error anyway, though it won't be very informative. | ||
$message = 'Using ${} in strings is deprecated since PHP 8.2, use {$var} or {${expr}} instead.'; | ||
$code = 'DeprecatedDollarBraceEmbed'; | ||
$phpcsFile->addWarning($message, $stackPtr, $code); | ||
return; | ||
} | ||
// @codeCoverageIgnoreEnd | ||
} | ||
|
||
$endOfString = TextStrings::getEndOfCompleteTextString($phpcsFile, $stackPtr); | ||
$startOfString = $stackPtr; | ||
if ($tokens[$stackPtr]['code'] === \T_START_HEREDOC) { | ||
$startOfString = ($stackPtr + 1); | ||
} | ||
|
||
$contents = GetTokensAsString::normal($phpcsFile, $startOfString, $endOfString); | ||
if (\strpos($contents, '${') === false) { | ||
// No interpolation found or only interpolations which are still supported. | ||
return ($endOfString + 1); | ||
} | ||
|
||
$embeds = TextStrings::getEmbeds($contents); | ||
foreach ($embeds as $offset => $embed) { | ||
if (\strpos($embed, '${') !== 0) { | ||
continue; | ||
} | ||
|
||
// Figure out the stack pointer to throw the warning on. | ||
$errorPtr = $startOfString; | ||
$length = 0; | ||
while (($length + $tokens[$errorPtr]['length']) < $offset) { | ||
$length += $tokens[$errorPtr]['length']; | ||
++$errorPtr; | ||
} | ||
|
||
// Type 4. | ||
$message = 'Using %s (variable variables) in strings is deprecated since PHP 8.2, use {${expr}} instead.'; | ||
$code = 'DeprecatedExpressionSyntax'; | ||
if (\preg_match('`^\$\{(?P<varname>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]+)(?:\[([\'"])?[^\$\{\}\]]+(?:\2)?\])?\}$`', $embed) === 1) { | ||
// Type 3. | ||
$message = 'Using ${var} in strings is deprecated since PHP 8.2, use {$var} instead. Found: %s'; | ||
$code = 'DeprecatedVariableSyntax'; | ||
} | ||
|
||
$phpcsFile->addWarning($message, $errorPtr, $code, [$embed]); | ||
|
||
} | ||
|
||
return ($endOfString + 1); | ||
} | ||
} |
90 changes: 90 additions & 0 deletions
90
Magento2/Tests/PHPCompatibility/RemovedDollarBraceStringEmbedsUnitTest.1.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
<?php | ||
|
||
/* | ||
* Embedded variables which are supported cross-version. | ||
*/ | ||
|
||
// Type 1: directly embedding variables. | ||
echo "$foo"; | ||
echo "$$foo"; | ||
echo "$foo[bar]"; | ||
echo "$foo->bar"; | ||
$text = "some text $var some text"; | ||
|
||
$heredoc = <<<EOD | ||
some text $var some text | ||
EOD; | ||
|
||
// Type 2: Braces around the variable/expression. | ||
echo "{$foo}"; | ||
echo "{$$foo}"; | ||
echo "{$foo['bar']}"; | ||
echo "{$foo->bar}"; | ||
echo "{$foo->bar()}"; | ||
echo "{$foo['bar']->baz()()}"; | ||
echo "{${$bar}}"; | ||
echo "{$foo()}"; | ||
echo "{${$object->getMethod()}}" | ||
$text = "some text {$var} some text"; | ||
|
||
$heredoc = <<<"EOD" | ||
some text {$var} some text | ||
EOD; | ||
|
||
/* | ||
* Not our target. | ||
*/ | ||
|
||
// Ordinary variable variables outside strings. | ||
$foo = ${'bar'}; | ||
|
||
// Heredoc without embeds. | ||
echo <<<EOD | ||
Some text | ||
EOD; | ||
|
||
// Not actually interpolated - $ is escaped. The second $foo is to force T_DOUBLE_QUOTED_STRING tokenization. | ||
echo "\${foo} and $foo"; | ||
echo "\${foo[\"bar\"]} and $foo"; | ||
echo "$\{foo} and $foo"; | ||
|
||
|
||
/* | ||
* PHP 8.2: deprecated forms of embedding variables. | ||
*/ | ||
|
||
// Type 3: Braces after the dollar sign. | ||
echo "${foo}"; | ||
echo "${foo['bar']}"; | ||
$text = "some text ${foo} some ${text}"; | ||
|
||
$heredoc = <<<EOD | ||
some text ${foo} some text | ||
EOD; | ||
|
||
echo "\\${foo}"; // Not actually escaped, the backslash escapes the backslash, not the dollar sign. | ||
|
||
// Type 4: Variable variables. | ||
echo "${$bar}"; | ||
echo "${(foo)}"; | ||
echo "${foo->bar}"; | ||
echo "${$object->getMethod()}" | ||
$text = "some text ${(foo)} some text"; | ||
echo "${substr('laruence', 0, 2)}"; | ||
|
||
echo "${foo["${bar}"]}"; | ||
echo "${foo["${bar['baz']}"]}"; | ||
echo "${foo->{$baz}}"; | ||
echo "${foo->{${'a'}}}"; | ||
echo "${foo->{"${'a'}"}}"; | ||
|
||
// Verify correct handling of stack pointers in multi-token code. | ||
$text = "Line without embed | ||
some text ${foo["${bar}"]} some text | ||
some text ${foo["${bar['baz']}"]} some text | ||
some text ${foo->{${'a'}}} some text | ||
"; | ||
|
||
$heredoc = <<<"EOD" | ||
some text ${(foo)} some text | ||
EOD; |
42 changes: 42 additions & 0 deletions
42
Magento2/Tests/PHPCompatibility/RemovedDollarBraceStringEmbedsUnitTest.2.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?php | ||
|
||
/** | ||
* The tests involving PHP 7.3+ indented heredocs are in a separate test case file | ||
* as any code after an indented heredoc will be tokenizer garbage on PHP < 7.3. | ||
*/ | ||
|
||
// No embeds. | ||
$php73IndentedHeredoc = <<<"EOD" | ||
some text some text | ||
EOD; | ||
|
||
/* | ||
* Embedded variables which are supported cross-version. | ||
*/ | ||
|
||
// Type 1: directly embedding variables. | ||
$php73IndentedHeredoc = <<<"EOD" | ||
some text $foo[bar] some text | ||
EOD; | ||
|
||
// Type 2: Braces around the variable/expression. | ||
$php73IndentedHeredoc = <<<EOD | ||
some text {${$bar}} some text | ||
EOD; | ||
|
||
/* | ||
* PHP 8.2: deprecated forms of embedding variables. | ||
*/ | ||
|
||
// Type 3: Braces after the dollar sign. | ||
$php73IndentedHeredoc = <<<"EOD" | ||
some text ${foo['bar']} some text | ||
EOD; | ||
|
||
// Type 4: Variable variables. | ||
$php73IndentedHeredoc = <<<EOD | ||
Line without embed | ||
some text ${$object->getMethod()} some text | ||
some text ${foo["${bar['baz']}"]} some text | ||
some text ${foo->{${'a'}}} some text | ||
EOD; |
Oops, something went wrong.