Introducing coding standards validation.

This commit is contained in:
Marcos Schratzenstaller
2019-04-22 17:28:46 -03:00
committed by Nathan Rice
parent 6e40c921ab
commit fd504d41ac
1624 changed files with 190339 additions and 262 deletions
@@ -0,0 +1,183 @@
<?php
/**
* \PHPCompatibility\Sniffs\Lists\AssignmentOrderSniff.
*
* PHP version 7.0
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
namespace PHPCompatibility\Sniffs\Lists;
use PHPCompatibility\Sniff;
use PHP_CodeSniffer_File as File;
use PHP_CodeSniffer_Tokens as Tokens;
/**
* List assignment order.
*
* The list() construct no longer assigns variables in reverse order.
* This affects all list constructs where non-unique variables are used.
*
* PHP version 7.0
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
class AssignmentOrderSniff extends Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_LIST,
T_OPEN_SHORT_ARRAY,
);
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token in the
* stack passed in $tokens.
*
* @return void|int Void if not a valid list. If a list construct has been
* examined, the stack pointer to the list closer to skip
* passed any nested lists which don't need to be examined again.
*/
public function process(File $phpcsFile, $stackPtr)
{
if ($this->supportsAbove('7.0') === false) {
return;
}
$tokens = $phpcsFile->getTokens();
if ($tokens[$stackPtr]['code'] === T_OPEN_SHORT_ARRAY
&& $this->isShortList($phpcsFile, $stackPtr) === false
) {
// Short array, not short list.
return;
}
if ($tokens[$stackPtr]['code'] === T_LIST) {
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($nextNonEmpty === false
|| $tokens[$nextNonEmpty]['code'] !== T_OPEN_PARENTHESIS
|| isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false
) {
// Parse error or live coding.
return;
}
$opener = $nextNonEmpty;
$closer = $tokens[$nextNonEmpty]['parenthesis_closer'];
} else {
// Short list syntax.
$opener = $stackPtr;
if (isset($tokens[$stackPtr]['bracket_closer'])) {
$closer = $tokens[$stackPtr]['bracket_closer'];
}
}
if (isset($opener, $closer) === false) {
return;
}
/*
* OK, so we have the opener & closer, now we need to check all the variables in the
* list() to see if there are duplicates as that's the problem.
*/
$hasVars = $phpcsFile->findNext(array(T_VARIABLE, T_DOLLAR), ($opener + 1), $closer);
if ($hasVars === false) {
// Empty list, not our concern.
return ($closer + 1);
}
// Set the variable delimiters based on the list type being examined.
$stopPoints = array(T_COMMA);
if ($tokens[$stackPtr]['code'] === T_OPEN_SHORT_ARRAY) {
$stopPoints[] = T_CLOSE_SHORT_ARRAY;
} else {
$stopPoints[] = T_CLOSE_PARENTHESIS;
}
$listVars = array();
$lastStopPoint = $opener;
/*
* Create a list of all variables used within the `list()` construct.
* We're not concerned with whether these are nested or not, as any duplicate
* variable name used will be problematic, independent of nesting.
*/
do {
$nextStopPoint = $phpcsFile->findNext($stopPoints, ($lastStopPoint + 1), $closer);
if ($nextStopPoint === false) {
$nextStopPoint = $closer;
}
// Also detect this in PHP 7.1 keyed lists.
$hasDoubleArrow = $phpcsFile->findNext(T_DOUBLE_ARROW, ($lastStopPoint + 1), $nextStopPoint);
if ($hasDoubleArrow !== false) {
$lastStopPoint = $hasDoubleArrow;
}
// Find the start of the variable, allowing for variable variables.
$nextStartPoint = $phpcsFile->findNext(array(T_VARIABLE, T_DOLLAR), ($lastStopPoint + 1), $nextStopPoint);
if ($nextStartPoint === false) {
// Skip past empty bits in the list, i.e. `list( $a, , ,)`.
$lastStopPoint = $nextStopPoint;
continue;
}
/*
* Gather the content of all non-empty tokens to determine the "variable name".
* Variable name in this context includes array or object property syntaxes, such
* as `$a['name']` and `$b->property`.
*/
$varContent = '';
for ($i = $nextStartPoint; $i < $nextStopPoint; $i++) {
if (isset(Tokens::$emptyTokens[$tokens[$i]['code']])) {
continue;
}
$varContent .= $tokens[$i]['content'];
}
if ($varContent !== '') {
$listVars[] = $varContent;
}
$lastStopPoint = $nextStopPoint;
} while ($lastStopPoint < $closer);
if (empty($listVars)) {
// Shouldn't be possible, but just in case.
return ($closer + 1);
}
// Verify that all variables used in the list() construct are unique.
if (count($listVars) !== count(array_unique($listVars))) {
$phpcsFile->addError(
'list() will assign variable from left-to-right since PHP 7.0. Ensure all variables in list() are unique to prevent unexpected results.',
$stackPtr,
'Affected'
);
}
return ($closer + 1);
}
}
@@ -0,0 +1,111 @@
<?php
/**
* \PHPCompatibility\Sniffs\Lists\ForbiddenEmptyListAssignmentSniff.
*
* PHP version 7.0
*
* @category PHP
* @package PHPCompatibility
* @author Wim Godden <wim@cu.be>
*/
namespace PHPCompatibility\Sniffs\Lists;
use PHPCompatibility\Sniff;
use PHP_CodeSniffer_File as File;
use PHP_CodeSniffer_Tokens as Tokens;
/**
* \PHPCompatibility\Sniffs\Lists\ForbiddenEmptyListAssignmentSniff.
*
* Empty list() assignments have been removed in PHP 7.0
*
* PHP version 7.0
*
* @category PHP
* @package PHPCompatibility
* @author Wim Godden <wim@cu.be>
*/
class ForbiddenEmptyListAssignmentSniff extends Sniff
{
/**
* List of tokens to disregard when determining whether the list() is empty.
*
* @var array
*/
protected $ignoreTokens = array();
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
// Set up a list of tokens to disregard when determining whether the list() is empty.
// Only needs to be set up once.
$this->ignoreTokens = Tokens::$emptyTokens;
$this->ignoreTokens[T_COMMA] = T_COMMA;
$this->ignoreTokens[T_OPEN_PARENTHESIS] = T_OPEN_PARENTHESIS;
$this->ignoreTokens[T_CLOSE_PARENTHESIS] = T_CLOSE_PARENTHESIS;
return array(
T_LIST,
T_OPEN_SHORT_ARRAY,
);
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @param \PHP_CodeSniffer_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)
{
if ($this->supportsAbove('7.0') === false) {
return;
}
$tokens = $phpcsFile->getTokens();
if ($tokens[$stackPtr]['code'] === T_OPEN_SHORT_ARRAY) {
if ($this->isShortList($phpcsFile, $stackPtr) === false) {
return;
}
$open = $stackPtr;
$close = $tokens[$stackPtr]['bracket_closer'];
} else {
// T_LIST.
$open = $phpcsFile->findNext(T_OPEN_PARENTHESIS, $stackPtr, null, false, null, true);
if ($open === false || isset($tokens[$open]['parenthesis_closer']) === false) {
return;
}
$close = $tokens[$open]['parenthesis_closer'];
}
$error = true;
if (($close - $open) > 1) {
for ($cnt = $open + 1; $cnt < $close; $cnt++) {
if (isset($this->ignoreTokens[$tokens[$cnt]['code']]) === false) {
$error = false;
break;
}
}
}
if ($error === true) {
$phpcsFile->addError(
'Empty list() assignments are not allowed since PHP 7.0',
$stackPtr,
'Found'
);
}
}
}
@@ -0,0 +1,211 @@
<?php
/**
* \PHPCompatibility\Sniffs\Lists\NewKeyedListSniff.
*
* PHP version 7.1
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
namespace PHPCompatibility\Sniffs\Lists;
use PHPCompatibility\Sniff;
use PHP_CodeSniffer_File as File;
use PHP_CodeSniffer_Tokens as Tokens;
/**
* \PHPCompatibility\Sniffs\Lists\NewKeyedListSniff.
*
* "You can now specify keys in list(), or its new shorthand [] syntax. "
*
* PHP version 7.1
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
class NewKeyedListSniff extends Sniff
{
/**
* Tokens which represent the start of a list construct.
*
* @var array
*/
protected $sniffTargets = array(
T_LIST => T_LIST,
T_OPEN_SHORT_ARRAY => T_OPEN_SHORT_ARRAY,
);
/**
* The token(s) within the list construct which is being targeted.
*
* @var array
*/
protected $targetsInList = array(
T_DOUBLE_ARROW => T_DOUBLE_ARROW,
);
/**
* All tokens needed to walk through the list construct and
* determine whether the target token is contained within.
*
* Set by the setUpAllTargets() method which is called from within register().
*
* @var array
*/
protected $allTargets;
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
$this->setUpAllTargets();
return $this->sniffTargets;
}
/**
* Prepare the $allTargets array only once.
*
* @return array
*/
public function setUpAllTargets()
{
$this->allTargets = $this->sniffTargets + $this->targetsInList;
}
/**
* Do a version check to determine if this sniff needs to run at all.
*
* @return bool
*/
protected function bowOutEarly()
{
return ($this->supportsBelow('7.0') === false);
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @param \PHP_CodeSniffer_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)
{
if ($this->bowOutEarly() === true) {
return;
}
$tokens = $phpcsFile->getTokens();
if ($tokens[$stackPtr]['code'] === T_OPEN_SHORT_ARRAY
&& $this->isShortList($phpcsFile, $stackPtr) === false
) {
// Short array, not short list.
return;
}
if ($tokens[$stackPtr]['code'] === T_LIST) {
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($nextNonEmpty === false
|| $tokens[$nextNonEmpty]['code'] !== T_OPEN_PARENTHESIS
|| isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false
) {
// Parse error or live coding.
return;
}
$opener = $nextNonEmpty;
$closer = $tokens[$nextNonEmpty]['parenthesis_closer'];
} else {
// Short list syntax.
$opener = $stackPtr;
if (isset($tokens[$stackPtr]['bracket_closer'])) {
$closer = $tokens[$stackPtr]['bracket_closer'];
}
}
if (isset($opener, $closer) === false) {
return;
}
$this->examineList($phpcsFile, $opener, $closer);
}
/**
* Examine the contents of a list construct to determine whether an error needs to be thrown.
*
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param int $opener The position of the list open token.
* @param int $closer The position of the list close token.
*
* @return void
*/
protected function examineList(File $phpcsFile, $opener, $closer)
{
$start = $opener;
while (($start = $this->hasTargetInList($phpcsFile, $start, $closer)) !== false) {
$phpcsFile->addError(
'Specifying keys in list constructs is not supported in PHP 7.0 or earlier.',
$start,
'Found'
);
}
}
/**
* Check whether a certain target token exists within a list construct.
*
* Skips past nested list constructs, so these can be examined based on their own token.
*
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param int $start The position of the list open token or a token
* within the list to start (resume) the examination from.
* @param int $closer The position of the list close token.
*
* @return int|bool Stack pointer to the target token if encountered. False otherwise.
*/
protected function hasTargetInList(File $phpcsFile, $start, $closer)
{
$tokens = $phpcsFile->getTokens();
for ($i = ($start + 1); $i < $closer; $i++) {
if (isset($this->allTargets[$tokens[$i]['code']]) === false) {
continue;
}
if (isset($this->targetsInList[$tokens[$i]['code']]) === true) {
return $i;
}
// Skip past nested list constructs.
if ($tokens[$i]['code'] === T_LIST) {
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($i + 1), null, true);
if ($nextNonEmpty !== false
&& $tokens[$nextNonEmpty]['code'] === T_OPEN_PARENTHESIS
&& isset($tokens[$nextNonEmpty]['parenthesis_closer']) === true
) {
$i = $tokens[$nextNonEmpty]['parenthesis_closer'];
}
} elseif ($tokens[$i]['code'] === T_OPEN_SHORT_ARRAY
&& isset($tokens[$i]['bracket_closer'])
) {
$i = $tokens[$i]['bracket_closer'];
}
}
return false;
}
}
@@ -0,0 +1,67 @@
<?php
/**
* \PHPCompatibility\Sniffs\Lists\NewListReferenceAssignmentSniff.
*
* PHP version 7.3
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
namespace PHPCompatibility\Sniffs\Lists;
use PHPCompatibility\Sniffs\Lists\NewKeyedListSniff;
use PHP_CodeSniffer_File as File;
/**
* Detect reference assignments in array destructuring using (short) list.
*
* PHP version 7.3
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
class NewListReferenceAssignmentSniff extends NewKeyedListSniff
{
/**
* The token(s) within the list construct which is being targeted.
*
* @var array
*/
protected $targetsInList = array(
T_BITWISE_AND => T_BITWISE_AND,
);
/**
* Do a version check to determine if this sniff needs to run at all.
*
* @return bool
*/
protected function bowOutEarly()
{
return ($this->supportsBelow('7.2') === false);
}
/**
* Examine the contents of a list construct to determine whether an error needs to be thrown.
*
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param int $opener The position of the list open token.
* @param int $closer The position of the list close token.
*
* @return void
*/
protected function examineList(File $phpcsFile, $opener, $closer)
{
$start = $opener;
while (($start = $this->hasTargetInList($phpcsFile, $start, $closer)) !== false) {
$phpcsFile->addError(
'Reference assignments within list constructs are not supported in PHP 7.2 or earlier.',
$start,
'Found'
);
}
}
}
@@ -0,0 +1,79 @@
<?php
/**
* \PHPCompatibility\Sniffs\Lists\NewShortListSniff.
*
* PHP version 7.1
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
namespace PHPCompatibility\Sniffs\Lists;
use PHPCompatibility\Sniff;
use PHP_CodeSniffer_File as File;
/**
* \PHPCompatibility\Sniffs\Lists\NewShortListSniff.
*
* "The shorthand array syntax ([]) may now be used to destructure arrays for
* assignments (including within foreach), as an alternative to the existing
* list() syntax, which is still supported."
*
* PHP version 7.1
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
class NewShortListSniff extends Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_OPEN_SHORT_ARRAY);
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @param \PHP_CodeSniffer_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)
{
if ($this->supportsBelow('7.0') === false) {
return;
}
if ($this->isShortList($phpcsFile, $stackPtr) === false) {
return;
}
$tokens = $phpcsFile->getTokens();
$closer = $tokens[$stackPtr]['bracket_closer'];
$hasVariable = $phpcsFile->findNext(T_VARIABLE, ($stackPtr + 1), $closer);
if ($hasVariable === false) {
// List syntax is only valid if there are variables in it.
return;
}
$phpcsFile->addError(
'The shorthand list syntax "[]" to destructure arrays is not available in PHP 7.0 or earlier.',
$stackPtr,
'Found'
);
return ($closer + 1);
}
}