Skip to content

Commit

Permalink
Merge pull request #124 from PHPCSStandards/universal/whitespace-prec…
Browse files Browse the repository at this point in the history
…isionalignment-bugfix-flexible-heredoc-nowdoc

WhiteSpace/PrecisionAlignment: bug fix - improved handling of heredoc/nowdoc closers
  • Loading branch information
jrfnl committed Jul 29, 2022
2 parents 906b06d + 5ee7851 commit 82daa02
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 9 deletions.
45 changes: 38 additions & 7 deletions Universal/Sniffs/WhiteSpace/PrecisionAlignmentSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -270,12 +270,10 @@ public function process(File $phpcsFile, $stackPtr)
case \T_PHPCS_SET:
case \T_PHPCS_IGNORE:
case \T_PHPCS_IGNORE_FILE:
case \T_END_HEREDOC:
case \T_END_NOWDOC:
/*
* Indentation is included in the contents of the token for:
* - inline HTML
* - PHP 7.3+ flexible heredoc/nowdoc closer identifiers;
* - PHP 7.3+ flexible heredoc/nowdoc closer identifiers (see below);
* - subsequent lines of multi-line comments;
* - PHPCS native annotations when part of a multi-line comment.
*/
Expand Down Expand Up @@ -303,6 +301,24 @@ public function process(File $phpcsFile, $stackPtr)
--$spaces;
}
break;

case \T_END_HEREDOC:
case \T_END_NOWDOC:
/*
* PHPCS does not execute tab replacement in heredoc/nowdoc closer
* tokens (last checked: PHPCS 3.7.1), so handle this ourselves.
*/
$content = $tokens[$i]['content'];
if (\strpos($tokens[$i]['content'], "\t") !== false) {
$origContent = $content;
$content = \str_replace("\t", \str_repeat(' ', $this->tabWidth), $content);
}

$closer = \ltrim($content);
$whitespace = \str_replace($closer, '', $content);
$length = \strlen($whitespace);
$spaces = ($length % $indent);
break;
}

if ($spaces === 0) {
Expand All @@ -317,7 +333,13 @@ public function process(File $phpcsFile, $stackPtr)
);

if ($fix === true) {
$tabstops = (int) \round($spaces / $indent, 0);
if ($tokens[$i]['code'] === \T_END_HEREDOC || $tokens[$i]['code'] === \T_END_NOWDOC) {
// For heredoc/nowdoc, always round down to prevent introducing parse errors.
$tabstops = (int) \floor($spaces / $indent);
} else {
// For everything else, use "best guess".
$tabstops = (int) \round($spaces / $indent, 0);
}

switch ($tokens[$i]['code']) {
case \T_WHITESPACE:
Expand Down Expand Up @@ -357,8 +379,6 @@ public function process(File $phpcsFile, $stackPtr)
case \T_PHPCS_SET:
case \T_PHPCS_IGNORE:
case \T_PHPCS_IGNORE_FILE:
case \T_END_HEREDOC:
case \T_END_NOWDOC:
$replaceLength = (((int) ($length / $indent) + $tabstops) * $indent);
$replace = $this->getReplacement($replaceLength, $origContent);

Expand All @@ -376,6 +396,14 @@ public function process(File $phpcsFile, $stackPtr)

$phpcsFile->fixer->replaceToken($i, $newContent);
break;

case \T_END_HEREDOC:
case \T_END_NOWDOC:
$replaceLength = (((int) ($length / $indent) + $tabstops) * $indent);
$replace = $this->getReplacement($replaceLength, $origContent);

$phpcsFile->fixer->replaceToken($i, $replace . $closer);
break;
}
}
}
Expand All @@ -397,7 +425,10 @@ private function getReplacement($length, $origContent)
if ($origContent !== null) {
// Check whether tabs were part of the indent or inline alignment.
$content = \ltrim($origContent);
$whitespace = \str_replace($content, '', $origContent);
$whitespace = $origContent;
if ($content !== '') {
$whitespace = \str_replace($content, '', $origContent);
}

if (\strpos($whitespace, "\t") !== false) {
// Original indent used tabs. Use tabs in replacement too.
Expand Down
68 changes: 68 additions & 0 deletions Universal/Tests/WhiteSpace/PrecisionAlignmentUnitTest.11.inc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
* The text lines are not checked and the closer MUST be at the start of the line,
* as otherwise the code will result in a parse error.
*/

// Space indent.
$heredoc = <<<EOD
some text
some text
Expand All @@ -29,12 +31,33 @@ some text
some text
EOD;

// Tab indent.
$heredoc = <<<EOD
some text
some text
some text
EOD;

$nowdoc = <<<'EOD'
some text
some text
some text
EOD;

/*
* PHP >= 7.3: flexible heredoc/nowdocs.
*
* In this case, the indentation before the closing identifier will be checked.
* As before, the text lines will not be checked.
*
* Notes:
* - These tests also safeguard that for nowdoc/heredoc closers, the indent is always rounded DOWN
* as rounding up could cause a parse error (invalid body indentation level)!
* - These tests also verify correct handling of spaces vs tabs in tokens in which PHPCS
* may not have done the replacement natively.
*/

// Space indent.
$heredoc = <<<END
a
b
Expand All @@ -58,3 +81,48 @@ $nowdoc = <<<'END'
b
c
END; // Warning.

$heredoc = <<<END
a
b
END; // Warning.

$nowdoc = <<<'END'
a
b
END; // Warning.

// Tab indent.
$heredoc = <<<END
a
b
c
END; // OK.

$nowdoc = <<<'END'
a
b
c
END; // OK.

$heredoc = <<<"END"
a
b
c
END; // Warning.

$nowdoc = <<<'END'
a
b
c
END; // Warning.

$heredoc = <<<END
a
b
END; // Warning.

$nowdoc = <<<'END'
a
b
END; // Warning.
68 changes: 68 additions & 0 deletions Universal/Tests/WhiteSpace/PrecisionAlignmentUnitTest.11.inc.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
* The text lines are not checked and the closer MUST be at the start of the line,
* as otherwise the code will result in a parse error.
*/

// Space indent.
$heredoc = <<<EOD
some text
some text
Expand All @@ -29,12 +31,33 @@ some text
some text
EOD;

// Tab indent.
$heredoc = <<<EOD
some text
some text
some text
EOD;

$nowdoc = <<<'EOD'
some text
some text
some text
EOD;

/*
* PHP >= 7.3: flexible heredoc/nowdocs.
*
* In this case, the indentation before the closing identifier will be checked.
* As before, the text lines will not be checked.
*
* Notes:
* - These tests also safeguard that for nowdoc/heredoc closers, the indent is always rounded DOWN
* as rounding up could cause a parse error (invalid body indentation level)!
* - These tests also verify correct handling of spaces vs tabs in tokens in which PHPCS
* may not have done the replacement natively.
*/

// Space indent.
$heredoc = <<<END
a
b
Expand All @@ -58,3 +81,48 @@ $nowdoc = <<<'END'
b
c
END; // Warning.

$heredoc = <<<END
a
b
END; // Warning.

$nowdoc = <<<'END'
a
b
END; // Warning.

// Tab indent.
$heredoc = <<<END
a
b
c
END; // OK.

$nowdoc = <<<'END'
a
b
c
END; // OK.

$heredoc = <<<"END"
a
b
c
END; // Warning.

$nowdoc = <<<'END'
a
b
c
END; // Warning.

$heredoc = <<<END
a
b
END; // Warning.

$nowdoc = <<<'END'
a
b
END; // Warning.
11 changes: 9 additions & 2 deletions Universal/Tests/WhiteSpace/PrecisionAlignmentUnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ final class PrecisionAlignmentUnitTest extends AbstractSniffUnitTest
'PrecisionAlignmentUnitTest.7.inc' => 3,
'PrecisionAlignmentUnitTest.8.inc' => 2,
'PrecisionAlignmentUnitTest.10.inc' => 4,
'PrecisionAlignmentUnitTest.11.inc' => 4,
'PrecisionAlignmentUnitTest.15.inc' => 4,
'PrecisionAlignmentUnitTest.17.inc' => 4,
'PrecisionAlignmentUnitTest.2.css' => 4,
Expand Down Expand Up @@ -138,8 +139,14 @@ public function getWarningList($testFile = '')
case 'PrecisionAlignmentUnitTest.11.inc':
if (\PHP_VERSION_ID >= 70300) {
return [
54 => 1,
60 => 1,
77 => 1,
83 => 1,
88 => 1,
93 => 1,
112 => 1,
118 => 1,
123 => 1,
128 => 1,
];
}

Expand Down

0 comments on commit 82daa02

Please sign in to comment.