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,253 @@
<?php
/**
* \PHPCompatibility\Sniffs\Syntax\ForbiddenCallTimePassByReference.
*
* PHP version 5.4
*
* @category PHP
* @package PHPCompatibility
* @author Gary Rogers <gmrwebde@gmail.com>
* @author Florian Grandel <jerico.dev@gmail.com>
* @copyright 2009 Florian Grandel
*/
namespace PHPCompatibility\Sniffs\Syntax;
use PHPCompatibility\Sniff;
use PHP_CodeSniffer_File as File;
use PHP_CodeSniffer_Tokens as Tokens;
/**
* \PHPCompatibility\Sniffs\Syntax\ForbiddenCallTimePassByReference.
*
* Discourages the use of call time pass by references
*
* PHP version 5.4
*
* @category PHP
* @package PHPCompatibility
* @author Gary Rogers <gmrwebde@gmail.com>
* @author Florian Grandel <jerico.dev@gmail.com>
* @copyright 2009 Florian Grandel
*/
class ForbiddenCallTimePassByReferenceSniff extends Sniff
{
/**
* Tokens that represent assignments or equality comparisons.
*
* Near duplicate of Tokens::$assignmentTokens + Tokens::$equalityTokens.
* Copied in for PHPCS cross-version compatibility.
*
* @var array
*/
private $assignOrCompare = array(
// Equality tokens.
'T_IS_EQUAL' => true,
'T_IS_NOT_EQUAL' => true,
'T_IS_IDENTICAL' => true,
'T_IS_NOT_IDENTICAL' => true,
'T_IS_SMALLER_OR_EQUAL' => true,
'T_IS_GREATER_OR_EQUAL' => true,
// Assignment tokens.
'T_EQUAL' => true,
'T_AND_EQUAL' => true,
'T_OR_EQUAL' => true,
'T_CONCAT_EQUAL' => true,
'T_DIV_EQUAL' => true,
'T_MINUS_EQUAL' => true,
'T_POW_EQUAL' => true,
'T_MOD_EQUAL' => true,
'T_MUL_EQUAL' => true,
'T_PLUS_EQUAL' => true,
'T_XOR_EQUAL' => true,
'T_DOUBLE_ARROW' => true,
'T_SL_EQUAL' => true,
'T_SR_EQUAL' => true,
'T_COALESCE_EQUAL' => true,
'T_ZSR_EQUAL' => true,
);
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_STRING,
T_VARIABLE,
);
}
/**
* 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('5.3') === false) {
return;
}
$tokens = $phpcsFile->getTokens();
// Skip tokens that are the names of functions or classes
// within their definitions. For example: function myFunction...
// "myFunction" is T_STRING but we should skip because it is not a
// function or method *call*.
$findTokens = Tokens::$emptyTokens;
$findTokens[] = T_BITWISE_AND;
$prevNonEmpty = $phpcsFile->findPrevious(
$findTokens,
($stackPtr - 1),
null,
true
);
if ($prevNonEmpty !== false && in_array($tokens[$prevNonEmpty]['type'], array('T_FUNCTION', 'T_CLASS', 'T_INTERFACE', 'T_TRAIT'), true)) {
return;
}
// If the next non-whitespace token after the function or method call
// is not an opening parenthesis then it can't really be a *call*.
$openBracket = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($openBracket === false || $tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS
|| isset($tokens[$openBracket]['parenthesis_closer']) === false
) {
return;
}
// Get the function call parameters.
$parameters = $this->getFunctionCallParameters($phpcsFile, $stackPtr);
if (count($parameters) === 0) {
return;
}
// Which nesting level is the one we are interested in ?
$nestedParenthesisCount = 1;
if (isset($tokens[$openBracket]['nested_parenthesis'])) {
$nestedParenthesisCount = count($tokens[$openBracket]['nested_parenthesis']) + 1;
}
foreach ($parameters as $parameter) {
if ($this->isCallTimePassByReferenceParam($phpcsFile, $parameter, $nestedParenthesisCount) === true) {
// T_BITWISE_AND represents a pass-by-reference.
$error = 'Using a call-time pass-by-reference is deprecated since PHP 5.3';
$isError = false;
$errorCode = 'Deprecated';
if ($this->supportsAbove('5.4')) {
$error .= ' and prohibited since PHP 5.4';
$isError = true;
$errorCode = 'NotAllowed';
}
$this->addMessage($phpcsFile, $error, $parameter['start'], $isError, $errorCode);
}
}
}
/**
* Determine whether a parameter is passed by reference.
*
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param array $parameter Information on the current parameter
* to be examined.
* @param int $nestingLevel Target nesting level.
*
* @return bool
*/
protected function isCallTimePassByReferenceParam(File $phpcsFile, $parameter, $nestingLevel)
{
$tokens = $phpcsFile->getTokens();
$searchStartToken = $parameter['start'] - 1;
$searchEndToken = $parameter['end'] + 1;
$nextVariable = $searchStartToken;
do {
$nextVariable = $phpcsFile->findNext(array(T_VARIABLE, T_OPEN_SHORT_ARRAY, T_CLOSURE), ($nextVariable + 1), $searchEndToken);
if ($nextVariable === false) {
return false;
}
// Ignore anything within short array definition brackets.
if ($tokens[$nextVariable]['type'] === 'T_OPEN_SHORT_ARRAY'
&& (isset($tokens[$nextVariable]['bracket_opener'])
&& $tokens[$nextVariable]['bracket_opener'] === $nextVariable)
&& isset($tokens[$nextVariable]['bracket_closer'])
) {
// Skip forward to the end of the short array definition.
$nextVariable = $tokens[$nextVariable]['bracket_closer'];
continue;
}
// Skip past closures passed as function parameters.
if ($tokens[$nextVariable]['type'] === 'T_CLOSURE'
&& (isset($tokens[$nextVariable]['scope_condition'])
&& $tokens[$nextVariable]['scope_condition'] === $nextVariable)
&& isset($tokens[$nextVariable]['scope_closer'])
) {
// Skip forward to the end of the closure declaration.
$nextVariable = $tokens[$nextVariable]['scope_closer'];
continue;
}
// Make sure the variable belongs directly to this function call
// and is not inside a nested function call or array.
if (isset($tokens[$nextVariable]['nested_parenthesis']) === false
|| (count($tokens[$nextVariable]['nested_parenthesis']) !== $nestingLevel)
) {
continue;
}
// Checking this: $value = my_function(...[*]$arg...).
$tokenBefore = $phpcsFile->findPrevious(
Tokens::$emptyTokens,
($nextVariable - 1),
$searchStartToken,
true
);
if ($tokenBefore === false || $tokens[$tokenBefore]['code'] !== T_BITWISE_AND) {
// Nothing before the token or no &.
continue;
}
if ($phpcsFile->isReference($tokenBefore) === false) {
continue;
}
// Checking this: $value = my_function(...[*]&$arg...).
$tokenBefore = $phpcsFile->findPrevious(
Tokens::$emptyTokens,
($tokenBefore - 1),
$searchStartToken,
true
);
// Prevent false positive on assign by reference and compare with reference
// within function call parameters.
if (isset($this->assignOrCompare[$tokens[$tokenBefore]['type']])) {
continue;
}
// The found T_BITWISE_AND represents a pass-by-reference.
return true;
} while ($nextVariable < $searchEndToken);
// This code should never be reached, but here in case of weird bugs.
return false;
}
}
@@ -0,0 +1,108 @@
<?php
/**
* \PHPCompatibility\Sniffs\Syntax\NewArrayStringDereferencingSniff.
*
* PHP version 5.5
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
namespace PHPCompatibility\Sniffs\Syntax;
use PHPCompatibility\Sniff;
use PHP_CodeSniffer_File as File;
use PHP_CodeSniffer_Tokens as Tokens;
/**
* \PHPCompatibility\Sniffs\Syntax\NewArrayStringDereferencingSniff.
*
* Array and string literals can now be dereferenced directly to access individual elements and characters.
*
* PHP version 5.5
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
class NewArrayStringDereferencingSniff extends Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_ARRAY,
T_OPEN_SHORT_ARRAY,
T_CONSTANT_ENCAPSED_STRING,
);
}
/**
* 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('5.4') === false) {
return;
}
$tokens = $phpcsFile->getTokens();
switch ($tokens[$stackPtr]['code']) {
case T_CONSTANT_ENCAPSED_STRING:
$type = 'string literals';
$end = $stackPtr;
break;
case T_ARRAY:
if (isset($tokens[$stackPtr]['parenthesis_closer']) === false) {
// Live coding.
return;
} else {
$type = 'arrays';
$end = $tokens[$stackPtr]['parenthesis_closer'];
}
break;
case T_OPEN_SHORT_ARRAY:
if (isset($tokens[$stackPtr]['bracket_closer']) === false) {
// Live coding.
return;
} else {
$type = 'arrays';
$end = $tokens[$stackPtr]['bracket_closer'];
}
break;
}
if (isset($type, $end) === false) {
// Shouldn't happen, but for some reason did.
return;
}
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true, null, true);
if ($nextNonEmpty !== false
&& ($tokens[$nextNonEmpty]['type'] === 'T_OPEN_SQUARE_BRACKET'
|| $tokens[$nextNonEmpty]['type'] === 'T_OPEN_SHORT_ARRAY') // Work around bug #1381 in PHPCS 2.8.1 and lower.
) {
$phpcsFile->addError(
'Direct array dereferencing of %s is not present in PHP version 5.4 or earlier',
$nextNonEmpty,
'Found',
array($type)
);
}
}
}
@@ -0,0 +1,113 @@
<?php
/**
* \PHPCompatibility\Sniffs\Syntax\NewClassMemberAccessSniff.
*
* PHP version 5.4
* PHP version 7.0
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
namespace PHPCompatibility\Sniffs\Syntax;
use PHPCompatibility\Sniff;
use PHP_CodeSniffer_File as File;
use PHP_CodeSniffer_Tokens as Tokens;
/**
* \PHPCompatibility\Sniffs\Syntax\NewClassMemberAccessSniff.
*
* PHP 5.4: Class member access on instantiation has been added, e.g. (new Foo)->bar().
* PHP 7.0: Class member access on cloning has been added, e.g. (clone $foo)->bar().
*
* PHP version 5.4
* PHP version 7.0
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
class NewClassMemberAccessSniff extends Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_NEW,
T_CLONE,
);
}
/**
* 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)
{
$tokens = $phpcsFile->getTokens();
if ($tokens[$stackPtr]['code'] === T_NEW && $this->supportsBelow('5.3') !== true) {
return;
} elseif ($tokens[$stackPtr]['code'] === T_CLONE && $this->supportsBelow('5.6') !== true) {
return;
}
if (isset($tokens[$stackPtr]['nested_parenthesis']) === false) {
// The `new className/clone $a` has to be in parentheses, without is not supported.
return;
}
$parenthesisCloser = end($tokens[$stackPtr]['nested_parenthesis']);
$parenthesisOpener = key($tokens[$stackPtr]['nested_parenthesis']);
if (isset($tokens[$parenthesisOpener]['parenthesis_owner']) === true) {
// If there is an owner, these parentheses are for a different purpose.
return;
}
$prevBeforeParenthesis = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($parenthesisOpener - 1), null, true);
if ($prevBeforeParenthesis !== false && $tokens[$prevBeforeParenthesis]['code'] === T_STRING) {
// This is most likely a function call with the new/cloned object as a parameter.
return;
}
$nextAfterParenthesis = $phpcsFile->findNext(Tokens::$emptyTokens, ($parenthesisCloser + 1), null, true);
if ($nextAfterParenthesis === false) {
// Live coding.
return;
}
if ($tokens[$nextAfterParenthesis]['code'] !== T_OBJECT_OPERATOR
&& $tokens[$nextAfterParenthesis]['code'] !== T_OPEN_SQUARE_BRACKET
) {
return;
}
$data = array('instantiation', '5.3');
$errorCode = 'OnNewFound';
if ($tokens[$stackPtr]['code'] === T_CLONE) {
$data = array('cloning', '5.6');
$errorCode = 'OnCloneFound';
}
$phpcsFile->addError(
'Class member access on object %s was not supported in PHP %s or earlier',
$parenthesisCloser,
$errorCode,
$data
);
}
}
@@ -0,0 +1,85 @@
<?php
/**
* \PHPCompatibility\Sniffs\Syntax\NewDynamicAccessToStaticSniff.
*
* PHP version 5.3
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
namespace PHPCompatibility\Sniffs\Syntax;
use PHPCompatibility\Sniff;
use PHP_CodeSniffer_File as File;
use PHP_CodeSniffer_Tokens as Tokens;
/**
* \PHPCompatibility\Sniffs\Syntax\NewDynamicAccessToStaticSniff.
*
* As of PHP 5.3, static properties and methods as well as class constants
* can be accessed using a dynamic (variable) class name.
*
* PHP version 5.3
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
class NewDynamicAccessToStaticSniff extends Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_DOUBLE_COLON,
);
}
/**
* 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('5.2') === false) {
return;
}
$tokens = $phpcsFile->getTokens();
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
// Disregard `static::` as well. Late static binding is reported by another sniff.
if ($tokens[$prevNonEmpty]['code'] === T_SELF
|| $tokens[$prevNonEmpty]['code'] === T_PARENT
|| $tokens[$prevNonEmpty]['code'] === T_STATIC
) {
return;
}
if ($tokens[$prevNonEmpty]['code'] === T_STRING) {
$prevPrevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prevNonEmpty - 1), null, true);
if ($tokens[$prevPrevNonEmpty]['code'] !== T_OBJECT_OPERATOR) {
return;
}
}
$phpcsFile->addError(
'Static class properties and methods, as well as class constants, could not be accessed using a dynamic (variable) classname in PHP 5.2 or earlier.',
$stackPtr,
'Found'
);
}
}
@@ -0,0 +1,247 @@
<?php
/**
* \PHPCompatibility\Sniffs\Syntax\NewFlexibleHeredocNowdocSniff.
*
* PHP version 7.3
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
namespace PHPCompatibility\Sniffs\Syntax;
use PHPCompatibility\Sniff;
use PHPCompatibility\PHPCSHelper;
use PHP_CodeSniffer_File as File;
/**
* New Flexible Heredoc Nowdoc.
*
* As of PHP 7.3:
* - The body and the closing marker of a Heredoc/nowdoc can be indented;
* - The closing marker no longer needs to be on a line by itself;
* - The heredoc/nowdoc body may no longer contain the closing marker at the
* start of any of its lines.
*
* PHP version 7.3
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
class NewFlexibleHeredocNowdocSniff extends Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
$targets = array(
T_END_HEREDOC,
T_END_NOWDOC,
);
if (version_compare(PHP_VERSION_ID, '70299', '>') === false) {
// Start identifier of a PHP 7.3 flexible heredoc/nowdoc.
$targets[] = T_STRING;
}
return $targets;
}
/**
* 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)
{
/*
* Due to a tokenizer bug which gets hit when the PHP 7.3 heredoc/nowdoc syntax
* is used, this part of the sniff cannot possibly work on PHPCS < 2.6.0.
* See upstream issue #928.
*/
if ($this->supportsBelow('7.2') === true && version_compare(PHPCSHelper::getVersion(), '2.6.0', '>=')) {
$this->detectIndentedNonStandAloneClosingMarker($phpcsFile, $stackPtr);
}
$tokens = $phpcsFile->getTokens();
if ($this->supportsAbove('7.3') === true && $tokens[$stackPtr]['code'] !== T_STRING) {
$this->detectClosingMarkerInBody($phpcsFile, $stackPtr);
}
}
/**
* Detect indented and/or non-stand alone closing markers.
*
* @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
*/
protected function detectIndentedNonStandAloneClosingMarker(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$indentError = 'Heredoc/nowdoc with an indented closing marker is not supported in PHP 7.2 or earlier.';
$indentErrorCode = 'IndentedClosingMarker';
$trailingError = 'Having code - other than a semi-colon or new line - after the closing marker of a heredoc/nowdoc is not supported in PHP 7.2 or earlier.';
$trailingErrorCode = 'ClosingMarkerNoNewLine';
if (version_compare(PHP_VERSION_ID, '70299', '>') === true) {
/*
* Check for indented closing marker.
*/
if (ltrim($tokens[$stackPtr]['content']) !== $tokens[$stackPtr]['content']) {
$phpcsFile->addError($indentError, $stackPtr, $indentErrorCode);
}
/*
* Check for tokens after the closing marker.
*/
$nextNonWhitespace = $phpcsFile->findNext(array(T_WHITESPACE, T_SEMICOLON), ($stackPtr + 1), null, true);
if ($tokens[$stackPtr]['line'] === $tokens[$nextNonWhitespace]['line']) {
$phpcsFile->addError($trailingError, $stackPtr, $trailingErrorCode);
}
} else {
// For PHP < 7.3, we're only interested in T_STRING tokens.
if ($tokens[$stackPtr]['code'] !== T_STRING) {
return;
}
if (preg_match('`^<<<([\'"]?)([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\1[\r\n]+`', $tokens[$stackPtr]['content'], $matches) !== 1) {
// Not the start of a PHP 7.3 flexible heredoc/nowdoc.
return;
}
$identifier = $matches[2];
for ($i = ($stackPtr + 1); $i <= $phpcsFile->numTokens; $i++) {
if ($tokens[$i]['code'] !== T_ENCAPSED_AND_WHITESPACE) {
continue;
}
$trimmed = ltrim($tokens[$i]['content']);
if (strpos($trimmed, $identifier) !== 0) {
continue;
}
// OK, we've found the PHP 7.3 flexible heredoc/nowdoc closing marker.
/*
* Check for indented closing marker.
*/
if ($trimmed !== $tokens[$i]['content']) {
// Indent found before closing marker.
$phpcsFile->addError($indentError, $i, $indentErrorCode);
}
/*
* Check for tokens after the closing marker.
*/
// Remove the identifier.
$afterMarker = substr($trimmed, strlen($identifier));
// Remove a potential semi-colon at the beginning of what's left of the string.
$afterMarker = ltrim($afterMarker, ';');
// Remove new line characters at the end of the string.
$afterMarker = rtrim($afterMarker, "\r\n");
if ($afterMarker !== '') {
$phpcsFile->addError($trailingError, $i, $trailingErrorCode);
}
break;
}
}
}
/**
* Detect heredoc/nowdoc identifiers at the start of lines in the heredoc/nowdoc body.
*
* @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
*/
protected function detectClosingMarkerInBody(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$error = 'The body of a heredoc/nowdoc can not contain the heredoc/nowdoc closing marker as text at the start of a line since PHP 7.3.';
$errorCode = 'ClosingMarkerNoNewLine';
if (version_compare(PHP_VERSION_ID, '70299', '>') === true) {
$nextNonWhitespace = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true, null, true);
if ($nextNonWhitespace === false
|| $tokens[$nextNonWhitespace]['code'] === T_SEMICOLON
|| (($tokens[$nextNonWhitespace]['code'] === T_COMMA
|| $tokens[$nextNonWhitespace]['code'] === T_STRING_CONCAT)
&& $tokens[$nextNonWhitespace]['line'] !== $tokens[$stackPtr]['line'])
) {
// This is most likely a correctly identified closing marker.
return;
}
// The real closing tag has to be before the next heredoc/nowdoc.
$nextHereNowDoc = $phpcsFile->findNext(array(T_START_HEREDOC, T_START_NOWDOC), ($stackPtr + 1));
if ($nextHereNowDoc === false) {
$nextHereNowDoc = null;
}
$identifier = trim($tokens[$stackPtr]['content']);
$realClosingMarker = $stackPtr;
while (($realClosingMarker = $phpcsFile->findNext(T_STRING, ($realClosingMarker + 1), $nextHereNowDoc, false, $identifier)) !== false) {
$prevNonWhitespace = $phpcsFile->findPrevious(T_WHITESPACE, ($realClosingMarker - 1), null, true);
if ($prevNonWhitespace === false
|| $tokens[$prevNonWhitespace]['line'] === $tokens[$realClosingMarker]['line']
) {
// Marker text found, but not at the start of the line.
continue;
}
// The original T_END_HEREDOC/T_END_NOWDOC was most likely incorrect as we've found
// a possible alternative closing marker.
$phpcsFile->addError($error, $stackPtr, $errorCode);
break;
}
} else {
if (isset($tokens[$stackPtr]['scope_closer'], $tokens[$stackPtr]['scope_opener']) === true
&& $tokens[$stackPtr]['scope_closer'] === $stackPtr
) {
$opener = $tokens[$stackPtr]['scope_opener'];
} else {
// PHPCS < 3.0.2 did not add scope_* values for Nowdocs.
$opener = $phpcsFile->findPrevious(T_START_NOWDOC, ($stackPtr - 1));
if ($opener === false) {
return;
}
}
$quotedIdentifier = preg_quote($tokens[$stackPtr]['content'], '`');
// Throw an error for each line in the body which starts with the identifier.
for ($i = ($opener + 1); $i < $stackPtr; $i++) {
if (preg_match('`^[ \t]*' . $quotedIdentifier . '\b`', $tokens[$i]['content']) === 1) {
$phpcsFile->addError($error, $i, $errorCode);
}
}
}
}
}
@@ -0,0 +1,95 @@
<?php
/**
* \PHPCompatibility\Sniffs\Syntax\NewFunctionArrayDereferencingSniff.
*
* PHP version 5.4
*
* @category PHP
* @package PHPCompatibility
* @author Wim Godden <wim.godden@cu.be>
*/
namespace PHPCompatibility\Sniffs\Syntax;
use PHPCompatibility\Sniff;
use PHP_CodeSniffer_File as File;
use PHP_CodeSniffer_Tokens as Tokens;
/**
* \PHPCompatibility\Sniffs\Syntax\NewFunctionArrayDereferencingSniff.
*
* PHP version 5.4
*
* @category PHP
* @package PHPCompatibility
* @author Wim Godden <wim.godden@cu.be>
*/
class NewFunctionArrayDereferencingSniff extends Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_STRING);
}
/**
* 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('5.3') === false) {
return;
}
$tokens = $phpcsFile->getTokens();
// Next non-empty token should be the open parenthesis.
$openParenthesis = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true, null, true);
if ($openParenthesis === false || $tokens[$openParenthesis]['code'] !== T_OPEN_PARENTHESIS) {
return;
}
// Don't throw errors during live coding.
if (isset($tokens[$openParenthesis]['parenthesis_closer']) === false) {
return;
}
// Is this T_STRING really a function or method call ?
$prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if ($prevToken !== false && in_array($tokens[$prevToken]['code'], array(T_DOUBLE_COLON, T_OBJECT_OPERATOR), true) === false) {
$ignore = array(
T_FUNCTION => true,
T_CONST => true,
T_USE => true,
T_NEW => true,
T_CLASS => true,
T_INTERFACE => true,
);
if (isset($ignore[$tokens[$prevToken]['code']]) === true) {
// Not a call to a PHP function or method.
return;
}
}
$closeParenthesis = $tokens[$openParenthesis]['parenthesis_closer'];
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($closeParenthesis + 1), null, true, null, true);
if ($nextNonEmpty !== false && $tokens[$nextNonEmpty]['type'] === 'T_OPEN_SQUARE_BRACKET') {
$phpcsFile->addError(
'Function array dereferencing is not present in PHP version 5.3 or earlier',
$nextNonEmpty,
'Found'
);
}
}
}
@@ -0,0 +1,115 @@
<?php
/**
* \PHPCompatibility\Sniffs\Syntax\NewFunctionCallTrailingCommaSniff.
*
* PHP version 7.3
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
namespace PHPCompatibility\Sniffs\Syntax;
use PHPCompatibility\Sniff;
use PHP_CodeSniffer_File as File;
use PHP_CodeSniffer_Tokens as Tokens;
/**
* \PHPCompatibility\Sniffs\Syntax\NewFunctionCallTrailingCommaSniff.
*
* PHP version 7.3
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
class NewFunctionCallTrailingCommaSniff extends Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_STRING,
T_VARIABLE,
T_ISSET,
T_UNSET,
);
}
/**
* 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.2') === false) {
return;
}
$tokens = $phpcsFile->getTokens();
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($tokens[$nextNonEmpty]['code'] !== T_OPEN_PARENTHESIS
|| isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false
) {
return;
}
if ($tokens[$stackPtr]['code'] === T_STRING) {
$ignore = array(
T_FUNCTION => true,
T_CONST => true,
T_USE => true,
);
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if (isset($ignore[$tokens[$prevNonEmpty]['code']]) === true) {
// Not a function call.
return;
}
}
$closer = $tokens[$nextNonEmpty]['parenthesis_closer'];
$lastInParenthesis = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($closer - 1), $nextNonEmpty, true);
if ($tokens[$lastInParenthesis]['code'] !== T_COMMA) {
return;
}
$data = array();
switch ($tokens[$stackPtr]['code']) {
case T_ISSET:
$data[] = 'calls to isset()';
$errorCode = 'FoundInIsset';
break;
case T_UNSET:
$data[] = 'calls to unset()';
$errorCode = 'FoundInUnset';
break;
default:
$data[] = 'function calls';
$errorCode = 'FoundInFunctionCall';
break;
}
$phpcsFile->addError(
'Trailing comma\'s are not allowed in %s in PHP 7.2 or earlier',
$lastInParenthesis,
$errorCode,
$data
);
}
}
@@ -0,0 +1,74 @@
<?php
/**
* \PHPCompatibility\Sniffs\Syntax\NewShortArray.
*
* PHP version 5.4
*
* @category PHP
* @package PHPCompatibility
* @author Alex Miroshnikov <unknown@example.com>
*/
namespace PHPCompatibility\Sniffs\Syntax;
use PHPCompatibility\Sniff;
use PHP_CodeSniffer_File as File;
/**
* \PHPCompatibility\Sniffs\Syntax\NewShortArray.
*
* Short array syntax is available since PHP 5.4
*
* PHP version 5.4
*
* @category PHP
* @package PHPCompatibility
* @author Alex Miroshnikov <unknown@example.com>
*/
class NewShortArraySniff extends Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_OPEN_SHORT_ARRAY,
T_CLOSE_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('5.3') === false) {
return;
}
$tokens = $phpcsFile->getTokens();
$token = $tokens[$stackPtr];
$error = '%s is available since 5.4';
$data = array();
if ($token['type'] === 'T_OPEN_SHORT_ARRAY') {
$data[] = 'Short array syntax (open)';
} elseif ($token['type'] === 'T_CLOSE_SHORT_ARRAY') {
$data[] = 'Short array syntax (close)';
}
$phpcsFile->addError($error, $stackPtr, 'Found', $data);
}
}
@@ -0,0 +1,77 @@
<?php
/**
* \PHPCompatibility\Sniffs\Syntax\RemovedNewReferenceSniff.
*
* PHP version 5.4
*
* @category PHP
* @package PHPCompatibility
* @author Wim Godden <wim.godden@cu.be>
* @copyright 2012 Cu.be Solutions bvba
*/
namespace PHPCompatibility\Sniffs\Syntax;
use PHPCompatibility\Sniff;
use PHP_CodeSniffer_File as File;
use PHP_CodeSniffer_Tokens as Tokens;
/**
* \PHPCompatibility\Sniffs\Syntax\RemovedNewReferenceSniff.
*
* Discourages the use of assigning the return value of new by reference
*
* PHP version 5.4
*
* @category PHP
* @package PHPCompatibility
* @author Wim Godden <wim.godden@cu.be>
* @copyright 2012 Cu.be Solutions bvba
*/
class RemovedNewReferenceSniff extends Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_NEW);
}
/**
* 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('5.3') === false) {
return;
}
$tokens = $phpcsFile->getTokens();
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if ($prevNonEmpty === false || $tokens[$prevNonEmpty]['type'] !== 'T_BITWISE_AND') {
return;
}
$error = 'Assigning the return value of new by reference is deprecated in PHP 5.3';
$isError = false;
$errorCode = 'Deprecated';
if ($this->supportsAbove('7.0') === true) {
$error .= ' and has been removed in PHP 7.0';
$isError = true;
$errorCode = 'Removed';
}
$this->addMessage($phpcsFile, $error, $stackPtr, $isError, $errorCode);
}
}