Skip to content

Commit

Permalink
DeclareStrictTypesSniff updates
Browse files Browse the repository at this point in the history
  • Loading branch information
michalbundyra committed Jan 31, 2018
1 parent bce7c95 commit 8330b5e
Show file tree
Hide file tree
Showing 12 changed files with 252 additions and 48 deletions.
215 changes: 180 additions & 35 deletions src/Standards/Generic/Sniffs/PHP/DeclareStrictTypesSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Checks that strict types are declared in the PHP file.
*
* @author Michał Bundyra <contact@webimpress.com>
* @copyright 2006-2017 Squiz Pty Ltd (ABN 77 084 670 600)
* @copyright 2006-2018 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/

Expand Down Expand Up @@ -78,13 +78,58 @@ public function process(File $phpcsFile, $stackPtr)

$tokens = $phpcsFile->getTokens();

if ($stackPtr > 0) {
$before = trim($phpcsFile->getTokensAsString(0, $stackPtr));

if ($before === '') {
$error = 'Unexpected whitespace before PHP opening tag';
$fix = $phpcsFile->addFixableError($error, 0, 'Whitespace');

if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
for ($i = 0; $i < $stackPtr; ++$i) {
$phpcsFile->fixer->replaceToken($i, '');
}

$phpcsFile->fixer->endChangeset();
}
} else {
$error = 'Missing strict type declaration as first statement in the script';
$fix = $phpcsFile->addFixableError($error, 0, 'Missing');

if ($fix === true) {
$phpcsFile->fixer->addContentBefore(
0,
sprintf('<?php %s ?>%s', $this->format, $phpcsFile->eolChar)
);
}
}//end if

$this->checkOtherDeclarations($phpcsFile);

return $phpcsFile->numTokens;
}//end if

$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);

if ($tokens[$next]['code'] === T_DECLARE) {
$eos = $phpcsFile->findEndOfStatement($next);
$string = $phpcsFile->getTokensAsString($next, ($eos - $next + 1));
$string = $phpcsFile->findNext(
T_STRING,
($tokens[$next]['parenthesis_opener'] + 1),
$tokens[$next]['parenthesis_closer']
);

if ($string !== false
&& stripos($tokens[$string]['content'], 'strict_types') !== false
) {
if (isset($tokens[$next]['scope_closer']) === true
&& $next === $tokens[$next]['scope_condition']
) {
$eos = $tokens[$next]['scope_closer'];
} else {
$eos = $phpcsFile->findEndOfStatement($next);
}

if (stripos($string, 'strict_types') !== false) {
$prev = $phpcsFile->findPrevious(T_WHITESPACE, ($next - 1), null, true);
$after = $phpcsFile->findNext(T_WHITESPACE, ($eos + 1), null, true);

Expand All @@ -93,7 +138,7 @@ public function process(File $phpcsFile, $stackPtr)
&& $tokens[$after]['code'] === T_CLOSE_TAG
) {
if ($tokens[$prev]['line'] !== $tokens[$next]['line']) {
$error = 'PHP open tag must be in the same line as declaration.';
$error = 'PHP open tag must be on the same line as strict type declaration.';
$fix = $phpcsFile->addFixableError($error, $prev, 'OpenTag');

if ($fix === true) {
Expand All @@ -110,7 +155,7 @@ public function process(File $phpcsFile, $stackPtr)
}//end if

if ($prev !== false && ($prev < ($next - 1) || $tokens[$prev]['content'] !== '<?php ')) {
$error = 'Expected single space after PHP open tag and before declaration.';
$error = 'Expected single space after PHP open tag and before strict type declaration.';
$fix = $phpcsFile->addFixableError($error, $prev, 'OpenTagSpace');

if ($fix === true) {
Expand All @@ -125,7 +170,7 @@ public function process(File $phpcsFile, $stackPtr)
}

if ($tokens[$after]['line'] !== $tokens[$eos]['line']) {
$error = 'PHP close tag must be in the same line as declaration.';
$error = 'PHP close tag must be on the same line as strict type declaration.';
$fix = $phpcsFile->addFixableError($error, $after, 'CloseTag');

if ($fix === true) {
Expand Down Expand Up @@ -160,16 +205,22 @@ public function process(File $phpcsFile, $stackPtr)
$after = false;
}//end if

// Check how many blank lines is before declare statement.
// Check how many blank lines there are before declare statement.
if ($prev !== false) {
$linesBefore = ($tokens[$next]['line'] - $tokens[$prev]['line'] - 1);
if ($linesBefore !== $this->linesBefore) {
$error = 'Invalid number of blank lines before declare statement; expected %d, but found %d';
$data = [
$this->linesBefore,
$linesBefore,
];
$fix = $phpcsFile->addFixableError($error, $next, 'LinesBefore', $data);
if ($linesBefore < 0) {
$error = 'Strict type declaration must be in new line';
$data = [];
} else {
$error = 'Invalid number of blank lines before declare statement; expected %d, but found %d';
$data = [
$this->linesBefore,
$linesBefore,
];
}

$fix = $phpcsFile->addFixableError($error, $next, 'LinesBefore', $data);

if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
Expand All @@ -182,15 +233,22 @@ public function process(File $phpcsFile, $stackPtr)
}
}
} else {
// Clear whitespaces between prev and next, but no new lines.
if ($linesBefore < 0) {
for ($i = ($prev + 1); $i < $next; ++$i) {
$phpcsFile->fixer->replaceToken($i, '');
}
}

// Add new blank line(s).
while ($linesBefore < $this->linesBefore) {
$phpcsFile->fixer->addNewlineBefore($next);
++$linesBefore;
}
}
}//end if

$phpcsFile->fixer->endChangeset();
}
}//end if
}//end if
}//end if

Expand All @@ -202,12 +260,18 @@ public function process(File $phpcsFile, $stackPtr)

$linesAfter = ($tokens[$after]['line'] - $tokens[$eos]['line'] - 1);
if ($linesAfter !== $this->linesAfter) {
$error = 'Invalid number of blank lines after declare statement; expected %d, but found %d';
$data = [
$this->linesAfter,
$linesAfter,
];
$fix = $phpcsFile->addFixableError($error, $eos, 'LinesAfter', $data);
if ($linesAfter < 0) {
$error = 'Strict type declaration must be the only statement in the line';
$data = [];
} else {
$error = 'Invalid number of blank lines after declare statement; expected %d, but found %d';
$data = [
$this->linesAfter,
$linesAfter,
];
}

$fix = $phpcsFile->addFixableError($error, $eos, 'LinesAfter', $data);

if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
Expand All @@ -219,42 +283,60 @@ public function process(File $phpcsFile, $stackPtr)
}
}
} else {
// Remove whitespaces between EOS and after token.
if ($linesAfter < 0) {
for ($i = ($eos + 1); $i < $after; ++$i) {
$phpcsFile->fixer->replaceToken($i, '');
}
}

// Add new lines after the statement.
while ($linesAfter < $this->linesAfter) {
$phpcsFile->fixer->addNewline($eos);
++$linesAfter;
}
}
}//end if

$phpcsFile->fixer->endChangeset();
}
}//end if
}//end if
}//end if

// Check if declare statement match provided format.
$string = $phpcsFile->getTokensAsString($next, ($eos - $next + 1));
if ($string !== $this->format) {
$error = 'Invalid format of declaration; expected "%s", but found "%s"';
$error = 'Invalid format of strict type declaration; expected "%s", but found "%s"';
$data = [
$this->format,
$string,
];
$fix = $phpcsFile->addFixableError($error, $next, 'InvalidFormat', $data);

if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
for ($i = $next; $i < $eos; ++$i) {
$phpcsFile->fixer->replaceToken($i, '');
}
if ($this->normalize($string) === $this->normalize($this->format)) {
$fix = $phpcsFile->addFixableError($error, $next, 'InvalidFormat', $data);

if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
for ($i = $next; $i < $eos; ++$i) {
$phpcsFile->fixer->replaceToken($i, '');
}

$phpcsFile->fixer->replaceToken($eos, $this->format);
$phpcsFile->fixer->endChangeset();
$phpcsFile->fixer->replaceToken($eos, $this->format);
$phpcsFile->fixer->endChangeset();
}
} else {
$phpcsFile->addError($error, $next, 'InvalidFormatNotFixable', $data);
}
}
}//end if

$this->checkOtherDeclarations($phpcsFile, $next);

return (count($tokens) + 1);
}//end if
}//end if

$error = 'Missing declaration of strict types at the beginning of the file';
$this->checkOtherDeclarations($phpcsFile, $next);

$error = 'Missing strict type declaration at the beginning of the file';
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'NotFound');

if ($fix === true) {
Expand Down Expand Up @@ -287,4 +369,67 @@ public function process(File $phpcsFile, $stackPtr)
}//end process()


/**
* Normalize given string by removing all white characters
* and changed to lower case.
*
* @param string $string String to be normalized.
*
* @return string
*/
private function normalize($string)
{
return strtolower(preg_replace('/\s/', '', $string));

}//end normalize()


/**
* Process other strict_type declaration in the file and remove them.
* The declaration has to be the very first statement in the script.
*
* @param File $phpcsFile The file being scanned.
* @param int $declare The position of the first declaration.
*
* @return void
*/
private function checkOtherDeclarations(File $phpcsFile, $declare=0)
{
$tokens = $phpcsFile->getTokens();

while (($declare = $phpcsFile->findNext(T_DECLARE, ($declare + 1))) !== false) {
$string = $phpcsFile->findNext(
T_STRING,
($tokens[$declare]['parenthesis_opener'] + 1),
$tokens[$declare]['parenthesis_closer']
);

if ($string !== false
&& stripos($tokens[$string]['content'], 'strict_types') !== false
) {
$error = 'Strict type declaration must be the very first statement in the script';
$fix = $phpcsFile->addFixableError($error, $declare, 'NotFirstStatement');

if ($fix === true) {
$end = $phpcsFile->findNext(
(Tokens::$emptyTokens + [T_SEMICOLON]),
($tokens[$declare]['parenthesis_closer'] + 1),
null,
true
);

if ($end === false) {
$end = $phpcsFile->numTokens;
}

for ($i = $declare; $i < $end; ++$i) {
$phpcsFile->fixer->replaceToken($i, '');
}
}
}//end if
}//end while

}//end checkOtherDeclarations()


}//end class
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
declare(ticks=1) {}

declare(strict_types=1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php

declare(strict_types=1);

declare(ticks=1) {}

12 changes: 12 additions & 0 deletions src/Standards/Generic/Tests/PHP/DeclareStrictTypesUnitTest.11.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<html>
<head>
<title>
<?php
declare(strict_types=1);
echo 'Title';
?>
</title>
</head>

<?php echo $content; ?>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php declare(strict_types=1); ?>
<html>
<head>
<title>
<?php
echo 'Title';
?>
</title>
</head>

<?php echo $content; ?>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php
// Comment
declare(strict_types=1); ?>
<html>
<?php echo $content; ?>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php
// Comment

declare(strict_types=1);
?>
<html>
<?php echo $content; ?>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?php
declare(strict_types=1) /* comment */;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php

declare(strict_types=1) /* comment */;
Original file line number Diff line number Diff line change
@@ -1,6 +1 @@
phpcs:set Generic.PHP.DeclareStrictTypes linesBefore 0
phpcs:set Generic.PHP.DeclareStrictTypes linesAfter 2
phpcs:set Generic.PHP.DeclareStrictTypes format declare(strict_types = 1);
<?php
declare(strict_types=1);
echo 'hey';
<?php /* Must be first. */ declare(strict_types=1); /* Comment after. */ ?>
Loading

0 comments on commit 8330b5e

Please sign in to comment.