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
<?php | |
use PHP_CodeSniffer\Files\File; | |
use PHP_CodeSniffer\Sniffs\Sniff; | |
final class VdmgReturnIntValueCheckSniff implements Sniff { | |
/** | |
* String representation of error. | |
* | |
* @var string | |
*/ | |
private $errorMessage = 'Identical operator === / !== / >= / <= / > / < is not used for testing the return value of %s function'; | |
/** | |
* String representation of error. | |
* | |
* @var string | |
*/ | |
private $errorMessageBoolNotUsage = '"!" is used for testing the return value of %s function'; | |
/** | |
* Error violation code. | |
* | |
* @var string | |
*/ | |
private $errorCode = 'ImproperValueTesting'; | |
/** | |
* Searched functions. | |
* | |
* @var string[] | |
*/ | |
private $functions = [ | |
'count', | |
]; | |
/** | |
* All tokens from current file. | |
* | |
* @var array | |
*/ | |
private $tokens = []; | |
/** | |
* PHP_CodeSniffer file. | |
* | |
* @var File | |
*/ | |
private $file; | |
/** | |
* Left limit for search of identical operators. | |
* | |
* @var int | |
*/ | |
private $leftLimit; | |
/** | |
* Right limit for search of identical operators. | |
* | |
* @var int | |
*/ | |
private $rightLimit; | |
/** | |
* List of tokens which declares left bound of current scope. | |
* | |
* @var array | |
*/ | |
private $leftRangeTokens = [ | |
\T_IS_IDENTICAL, | |
\T_IS_NOT_IDENTICAL, | |
T_LESS_THAN, | |
T_GREATER_THAN, | |
\T_IS_SMALLER_OR_EQUAL, | |
\T_IS_GREATER_OR_EQUAL, | |
T_OPEN_PARENTHESIS, | |
\T_BOOLEAN_AND, | |
\T_BOOLEAN_OR, | |
]; | |
/** | |
* List of tokens which declares right bound of current scope. | |
* | |
* @var array | |
*/ | |
private $rightRangeTokens = [ | |
\T_IS_IDENTICAL, | |
\T_IS_NOT_IDENTICAL, | |
T_LESS_THAN, | |
T_GREATER_THAN, | |
\T_IS_SMALLER_OR_EQUAL, | |
\T_IS_GREATER_OR_EQUAL, | |
T_CLOSE_PARENTHESIS, | |
\T_BOOLEAN_AND, | |
\T_BOOLEAN_OR, | |
]; | |
/** | |
* List of tokens which declares identical operators. | |
* | |
* @var array | |
*/ | |
private $identical = [ | |
\T_IS_IDENTICAL, | |
\T_IS_NOT_IDENTICAL, | |
T_LESS_THAN, | |
T_GREATER_THAN, | |
\T_IS_SMALLER_OR_EQUAL, | |
\T_IS_GREATER_OR_EQUAL, | |
]; | |
/** | |
* Finds the position of close parenthesis of detected function. | |
* | |
* @param int $currentPosition | |
*/ | |
private function findFunctionParenthesisCloser($currentPosition) { | |
$nextOpenParenthesis = $this->file->findNext(T_OPEN_PARENTHESIS, $currentPosition, $this->rightLimit); | |
return $nextOpenParenthesis ? $this->tokens[(int)$nextOpenParenthesis]['parenthesis_closer'] : false; | |
} | |
/** | |
* Recursively finds identical operators in current scope. | |
* | |
* @param int $leftCurrentPosition | |
* @param int $rightCurrentPosition | |
* | |
* @return bool | |
*/ | |
private function findIdentical($leftCurrentPosition, $rightCurrentPosition) { | |
$leftBound = $this->file->findPrevious($this->leftRangeTokens, $leftCurrentPosition, $this->leftLimit - 1); | |
$rightBound = $this->file->findNext($this->rightRangeTokens, $rightCurrentPosition, $this->rightLimit + 1); | |
if ($leftBound === false || $rightBound === false) { | |
return false; | |
} | |
$leftToken = $this->tokens[(int)$leftBound]; | |
$rightToken = $this->tokens[(int)$rightBound]; | |
if ( | |
$leftToken['code'] === T_OPEN_PARENTHESIS | |
&& | |
$rightToken['code'] === T_CLOSE_PARENTHESIS | |
) { | |
return $this->findIdentical($leftBound - 1, $rightBound + 1); | |
} | |
return ( | |
in_array($leftToken['code'], $this->identical) | |
|| | |
in_array($rightToken['code'], $this->identical) | |
) ?: false; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function process(File $phpcsFile, $stackPtr): void { | |
$this->tokens = $phpcsFile->getTokens(); | |
$this->file = $phpcsFile; | |
$open = $this->tokens[$stackPtr]['parenthesis_opener']; | |
$this->leftLimit = $open; | |
$close = $this->tokens[$stackPtr]['parenthesis_closer']; | |
$this->rightLimit = $close; | |
for ($i = ($open + 1); $i < $close; $i++) { | |
if ( | |
( | |
$this->tokens[$i]['code'] === \T_STRING | |
&& | |
$this->tokens[$i + 1]['code'] === T_OPEN_PARENTHESIS | |
&& | |
in_array($this->tokens[$i]['content'], $this->functions, true) | |
) | |
&& | |
( | |
$this->tokens[$i - 1]['code'] === T_BOOLEAN_NOT | |
|| | |
!$this->findIdentical($i - 1, $this->findFunctionParenthesisCloser($i) + 1) | |
) | |
) { | |
$foundFunctionName = $this->tokens[$i]['content']; | |
if ($this->tokens[$i - 1]['code'] === T_BOOLEAN_NOT) { | |
$phpcsFile->addError($this->errorMessageBoolNotUsage, $i, $this->errorCode, [$foundFunctionName]); | |
} else { | |
$phpcsFile->addError($this->errorMessage, $i, $this->errorCode, [$foundFunctionName]); | |
} | |
} | |
} | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function register() { | |
return [\T_IF, \T_ELSEIF]; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment