diff --git a/src/Standards/Squiz/Sniffs/WhiteSpace/MemberVarSpacingSniff.php b/src/Standards/Squiz/Sniffs/WhiteSpace/MemberVarSpacingSniff.php index ccd48995e7..4a847ab16f 100644 --- a/src/Standards/Squiz/Sniffs/WhiteSpace/MemberVarSpacingSniff.php +++ b/src/Standards/Squiz/Sniffs/WhiteSpace/MemberVarSpacingSniff.php @@ -55,87 +55,172 @@ protected function processMemberVar(File $phpcsFile, $stackPtr) $endOfStatement = $phpcsFile->findNext(T_SEMICOLON, ($stackPtr + 1), null, false, null, true); - $ignore = $validPrefixes; - $ignore[T_WHITESPACE] = T_WHITESPACE; + $ignore = $validPrefixes; + $ignore[] = T_WHITESPACE; $start = $startOfStatement; - for ($prev = ($startOfStatement - 1); $prev >= 0; $prev--) { - if (isset($ignore[$tokens[$prev]['code']]) === true) { - continue; - } + $prev = $phpcsFile->findPrevious($ignore, ($startOfStatement - 1), null, true); + + if ($tokens[$prev]['code'] === T_ATTRIBUTE_END) { + do { + if ($tokens[$start]['code'] === T_ATTRIBUTE_END) { + $nextContent = $tokens[$start]['attribute_opener']; + } else { + $nextContent = $start; + } - if ($tokens[$prev]['code'] === T_ATTRIBUTE_END - && isset($tokens[$prev]['attribute_opener']) === true - ) { - $prev = $tokens[$prev]['attribute_opener']; - continue; - } + if ($tokens[$prev]['line'] === $tokens[$nextContent]['line']) { + $error = 'Each attribute must be on a line by itself'; + $fix = $phpcsFile->addFixableError($error, $prev, 'NoNewLineBeforeAttribute'); + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); - break; - } + /* + * Remove redundant whitespaces. + */ + + $cur = $prev; + while (($cur = $phpcsFile->findNext(T_WHITESPACE, ($cur + 1), $nextContent)) !== false) { + $phpcsFile->fixer->replaceToken($cur, ''); + } + + /* + * Fix indent. + */ + + $indent = ''; + $cur = $prev; + + do { + $cur = $phpcsFile->findPrevious(T_WHITESPACE, ($cur - 1)); + if ($tokens[$cur]['line'] !== $tokens[$prev]['line']) { + break; + } + + $indent = str_repeat(' ', $tokens[$cur]['length']); + } while (true); + + $phpcsFile->fixer->addContentBefore($nextContent, $indent); + + $phpcsFile->fixer->addNewlineBefore($nextContent); + $phpcsFile->fixer->endChangeset(); + }//end if + }//end if + + $foundLines = ($tokens[$nextContent]['line'] - $tokens[$prev]['line'] - 1); + if ($foundLines > 0) { + $error = 'Expected 0 blank lines after attribute; %s found'; + $data = [$foundLines]; + $fix = $phpcsFile->addFixableError($error, $prev, 'AfterAttribute', $data); + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + + for ($i = ($prev + 1); $i <= $nextContent; $i++) { + if ($tokens[$i]['line'] === $tokens[$nextContent]['line']) { + break; + } + + $phpcsFile->fixer->replaceToken($i, ''); + } + + $phpcsFile->fixer->addNewline($prev); + $phpcsFile->fixer->endChangeset(); + } + } + + $start = $prev; + $prev = $phpcsFile->findPrevious($ignore, ($tokens[$prev]['attribute_opener'] - 1), null, true); + } while ($tokens[$prev]['code'] === T_ATTRIBUTE_END); + }//end if if (isset(Tokens::$commentTokens[$tokens[$prev]['code']]) === true) { // Assume the comment belongs to the member var if it is on a line by itself. $prevContent = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prev - 1), null, true); if ($tokens[$prevContent]['line'] !== $tokens[$prev]['line']) { + if ($tokens[$start]['code'] === T_ATTRIBUTE_END) { + $nextContent = $tokens[$start]['attribute_opener']; + } else { + $nextContent = $start; + } + // Check the spacing, but then skip it. - $foundLines = ($tokens[$startOfStatement]['line'] - $tokens[$prev]['line'] - 1); + $foundLines = ($tokens[$nextContent]['line'] - $tokens[$prev]['line'] - 1); if ($foundLines > 0) { - for ($i = ($prev + 1); $i < $startOfStatement; $i++) { - if ($tokens[$i]['column'] !== 1) { - continue; + $error = 'Expected 0 blank lines after member var comment; %s found'; + $data = [$foundLines]; + $fix = $phpcsFile->addFixableError($error, $prev, 'AfterComment', $data); + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + // Inline comments have the newline included in the content but + // docblock do not. + if ($tokens[$prev]['code'] === T_COMMENT) { + $phpcsFile->fixer->replaceToken($prev, rtrim($tokens[$prev]['content'])); } - if ($tokens[$i]['code'] === T_WHITESPACE - && $tokens[$i]['line'] !== $tokens[($i + 1)]['line'] - ) { - $error = 'Expected 0 blank lines after member var comment; %s found'; - $data = [$foundLines]; - $fix = $phpcsFile->addFixableError($error, $prev, 'AfterComment', $data); - if ($fix === true) { - $phpcsFile->fixer->beginChangeset(); - // Inline comments have the newline included in the content but - // docblocks do not. - if ($tokens[$prev]['code'] === T_COMMENT) { - $phpcsFile->fixer->replaceToken($prev, rtrim($tokens[$prev]['content'])); - } - - for ($i = ($prev + 1); $i <= $startOfStatement; $i++) { - if ($tokens[$i]['line'] === $tokens[$startOfStatement]['line']) { - break; - } - - // Remove the newline after the docblock, and any entirely - // empty lines before the member var. - if ($tokens[$i]['code'] === T_WHITESPACE - && $tokens[$i]['line'] === $tokens[$prev]['line'] - || ($tokens[$i]['column'] === 1 - && $tokens[$i]['line'] !== $tokens[($i + 1)]['line']) - ) { - $phpcsFile->fixer->replaceToken($i, ''); - } - } - - $phpcsFile->fixer->addNewline($prev); - $phpcsFile->fixer->endChangeset(); - }//end if + for ($i = ($prev + 1); $i <= $nextContent; $i++) { + if ($tokens[$i]['line'] === $tokens[$start]['line']) { + break; + } - break; - }//end if - }//end for + $phpcsFile->fixer->replaceToken($i, ''); + } + + $phpcsFile->fixer->addNewline($prev); + $phpcsFile->fixer->endChangeset(); + } }//end if $start = $prev; }//end if + + if ($tokens[$prevContent]['code'] === T_ATTRIBUTE_END) { + $error = 'Member var comment must be before attributes'; + $fix = $phpcsFile->addFixableError($error, $prev, 'AttributeAfterComment'); + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + + /* + * Fix indent. + */ + + $indent = ''; + $cur = $tokens[$prevContent]['attribute_opener']; + + do { + $cur = $phpcsFile->findPrevious(T_WHITESPACE, ($cur - 1)); + + if ($tokens[$cur]['line'] !== $tokens[$tokens[$prevContent]['attribute_opener']]['line']) { + break; + } + + $indent = str_repeat(' ', $tokens[$cur]['length']); + } while (true); + + $phpcsFile->fixer->addContentBefore($tokens[$prevContent]['attribute_opener'], $indent); + + $commentContent = ''; + for ($i = $tokens[$prev]['comment_opener']; $i <= $prev; $i++) { + $commentContent .= $phpcsFile->fixer->getTokenContent($i); + $phpcsFile->fixer->replaceToken($i, ''); + } + + $phpcsFile->fixer->addNewlineBefore($tokens[$prevContent]['attribute_opener']); + $phpcsFile->fixer->addContentBefore($tokens[$prevContent]['attribute_opener'], $commentContent); + + $phpcsFile->fixer->endChangeset(); + }//end if + }//end if }//end if - // There needs to be n blank lines before the var, not counting comments. + // There needs to be n blank lines before the var, not counting comments and attributes. if ($start === $startOfStatement) { // No comment found. $first = $phpcsFile->findFirstOnLine(Tokens::$emptyTokens, $start, true); if ($first === false) { $first = $start; } + } else if ($tokens[$start]['code'] === T_ATTRIBUTE_END) { + $first = $tokens[$start]['attribute_opener']; } else if ($tokens[$start]['code'] === T_DOC_COMMENT_CLOSE_TAG) { $first = $tokens[$start]['comment_opener']; } else { diff --git a/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.inc b/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.inc index ff467bb2df..3ef6209cdb 100644 --- a/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.inc +++ b/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.inc @@ -343,7 +343,7 @@ class HasAttributes #[ORM\Id]#[ORM\Column("integer")] - private $id; + private $var1; /** @@ -354,5 +354,62 @@ class HasAttributes #[ORM\GeneratedValue] #[ORM\Column(ORM\Column::T_INTEGER)] - protected $height; + protected $var2; + + #[Attribute1] + public $var3 = 'value'; + + #[Attribute1] + + + #[Attribute2] + + public string $var4 = 'value'; + + /** + * Description. + */ + + #[Attribute1] + protected $var5 = 'value'; + + /** + * Description. + */ + + #[Attribute1] + + #[Attribute2] + + + private $var6 = 'value'; + + #[Attribute1] + + #[Attribute2] + + /** + * Description. + */ + private $var7 = 'value'; + + + /** + * Description. + */ + #[Attribute1] #[Attribute2] private $var8 = 'value'; + + /** + * Description. + */ + #[Attribute1] + #[Attribute2]private $var9 = 'value'; + + /** + * Description. + */ + #[Attribute1]#[MultiLineAttribute( + multi: 'line1', + line: 'line2' + )]private $var10 = 'value'; } diff --git a/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.inc.fixed b/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.inc.fixed index 5c42b1eda1..3c955853a5 100644 --- a/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.inc.fixed +++ b/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.inc.fixed @@ -328,8 +328,9 @@ class HasAttributes * * @var array */ - #[ORM\Id]#[ORM\Column("integer")] - private $id; + #[ORM\Id] + #[ORM\Column("integer")] + private $var1; /** * Short description of the member variable. @@ -338,5 +339,56 @@ class HasAttributes */ #[ORM\GeneratedValue] #[ORM\Column(ORM\Column::T_INTEGER)] - protected $height; + protected $var2; + + #[Attribute1] + public $var3 = 'value'; + + #[Attribute1] + #[Attribute2] + public string $var4 = 'value'; + + /** + * Description. + */ + #[Attribute1] + protected $var5 = 'value'; + + /** + * Description. + */ + #[Attribute1] + #[Attribute2] + private $var6 = 'value'; + + /** + * Description. + */ + #[Attribute1] + #[Attribute2] + private $var7 = 'value'; + + /** + * Description. + */ + #[Attribute1] + #[Attribute2] + private $var8 = 'value'; + + /** + * Description. + */ + #[Attribute1] + #[Attribute2] + private $var9 = 'value'; + + /** + * Description. + */ + #[Attribute1] + #[MultiLineAttribute( + multi: 'line1', + line: 'line2' + )] + private $var10 = 'value'; } diff --git a/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.php b/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.php index b43a972b76..20d37b7c3c 100644 --- a/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.php +++ b/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.php @@ -58,9 +58,21 @@ public function getErrorList() 292 => 1, 333 => 1, 342 => 1, + 344 => 2, 346 => 1, - 353 => 1, + 354 => 1, 357 => 1, + 362 => 1, + 365 => 1, + 371 => 1, + 378 => 1, + 380 => 1, + 382 => 1, + 393 => 1, + 400 => 3, + 406 => 1, + 411 => 1, + 414 => 1, ]; }//end getErrorList()