diff --git a/Universal/Docs/PHP/DisallowExitDieParenthesesStandard.xml b/Universal/Docs/PHP/DisallowExitDieParenthesesStandard.xml new file mode 100644 index 0000000..5a32f51 --- /dev/null +++ b/Universal/Docs/PHP/DisallowExitDieParenthesesStandard.xml @@ -0,0 +1,25 @@ + + + + + + + + ; +exit($status); + ]]> + + + (); +exit(); + ]]> + + + diff --git a/Universal/Sniffs/PHP/DisallowExitDieParenthesesSniff.php b/Universal/Sniffs/PHP/DisallowExitDieParenthesesSniff.php new file mode 100644 index 0000000..2fa0a2c --- /dev/null +++ b/Universal/Sniffs/PHP/DisallowExitDieParenthesesSniff.php @@ -0,0 +1,110 @@ + + */ + public function register() + { + return [\T_EXIT]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.5.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 + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + if ($nextNonEmpty === false) { + // Live coding. Do not flag (yet). + return; + } + + if ($tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS) { + // No parentheses found. + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no'); + return; + } + + if (isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false) { + // Incomplete set of parentheses. Ignore. + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes'); + return; + } + + $opener = $nextNonEmpty; + $closer = $tokens[$opener]['parenthesis_closer']; + $hasParams = $phpcsFile->findNext(Tokens::$emptyTokens, ($opener + 1), $closer, true); + if ($hasParams !== false) { + // There is something between the parentheses. Ignore. + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes, with parameter(s)'); + return; + } + + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes'); + + $fix = $phpcsFile->addFixableError( + 'Parentheses not allowed when calling %s without passing parameters', + $stackPtr, + 'Found', + [\strtolower(\ltrim($tokens[$stackPtr]['content'], '\\'))] + ); + + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + + for ($i = ($stackPtr + 1); $i <= $closer; $i++) { + if (isset(Tokens::$commentTokens[$tokens[$i]['code']]) === true) { + continue; + } + + $phpcsFile->fixer->replaceToken($i, ''); + } + + $phpcsFile->fixer->endChangeset(); + } + } +} diff --git a/Universal/Tests/PHP/DisallowExitDieParenthesesUnitTest.1.inc b/Universal/Tests/PHP/DisallowExitDieParenthesesUnitTest.1.inc new file mode 100644 index 0000000..efc5b82 --- /dev/null +++ b/Universal/Tests/PHP/DisallowExitDieParenthesesUnitTest.1.inc @@ -0,0 +1,54 @@ +exit; + echo $this->die; + echo $this->exit(); + echo $this->die(); + } +} + +// Invalid PHP, but not our concern. Not our target. +namespace\Sub\exit; +Partially\Qualified\die; +\Fully\Qualified\exit; + +/* + * OK. + */ +exit; +die; +EXIT; +Die; +\exit; +\die; +exit ($status); +\die /*comment*/ (10); + +/* + * Bad. + */ +exit(); +die(); +Exit(/*comment*/); +DIE (); +\exit ( ); +\die(); diff --git a/Universal/Tests/PHP/DisallowExitDieParenthesesUnitTest.1.inc.fixed b/Universal/Tests/PHP/DisallowExitDieParenthesesUnitTest.1.inc.fixed new file mode 100644 index 0000000..9d302fe --- /dev/null +++ b/Universal/Tests/PHP/DisallowExitDieParenthesesUnitTest.1.inc.fixed @@ -0,0 +1,54 @@ +exit; + echo $this->die; + echo $this->exit(); + echo $this->die(); + } +} + +// Invalid PHP, but not our concern. Not our target. +namespace\Sub\exit; +Partially\Qualified\die; +\Fully\Qualified\exit; + +/* + * OK. + */ +exit; +die; +EXIT; +Die; +\exit; +\die; +exit ($status); +\die /*comment*/ (10); + +/* + * Bad. + */ +exit; +die; +Exit/*comment*/; +DIE; +\exit; +\die; diff --git a/Universal/Tests/PHP/DisallowExitDieParenthesesUnitTest.2.inc b/Universal/Tests/PHP/DisallowExitDieParenthesesUnitTest.2.inc new file mode 100644 index 0000000..2de3d49 --- /dev/null +++ b/Universal/Tests/PHP/DisallowExitDieParenthesesUnitTest.2.inc @@ -0,0 +1,6 @@ + Key is the line number, value is the number of expected errors. + */ + public function getErrorList($testFile = '') + { + switch ($testFile) { + case 'DisallowExitDieParenthesesUnitTest.1.inc': + return [ + 49 => 1, + 50 => 1, + 51 => 1, + 52 => 1, + 53 => 1, + 54 => 1, + ]; + + default: + return []; + } + } + + /** + * Returns the lines where warnings should occur. + * + * @return array Key is the line number, value is the number of expected warnings. + */ + public function getWarningList() + { + return []; + } +}