mirror of
https://github.com/10h30/genesis-simple-sidebars.git
synced 2026-07-12 19:16:07 +09:00
Introducing coding standards validation.
This commit is contained in:
committed by
Nathan Rice
parent
6e40c921ab
commit
fd504d41ac
+124
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Variables\ForbiddenGlobalVariableVariableSniff.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim@cu.be>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Variables;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Variables\ForbiddenGlobalVariableVariableSniff.
|
||||
*
|
||||
* Variable variables are forbidden with global
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim@cu.be>
|
||||
*/
|
||||
class ForbiddenGlobalVariableVariableSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_GLOBAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
$endOfStatement = $phpcsFile->findNext(array(T_SEMICOLON, T_CLOSE_TAG), ($stackPtr + 1));
|
||||
if ($endOfStatement === false) {
|
||||
// No semi-colon - live coding.
|
||||
return;
|
||||
}
|
||||
|
||||
for ($ptr = ($stackPtr + 1); $ptr <= $endOfStatement; $ptr++) {
|
||||
$errorThrown = false;
|
||||
$nextComma = $phpcsFile->findNext(T_COMMA, $ptr, $endOfStatement, false, null, true);
|
||||
$varEnd = ($nextComma === false) ? $endOfStatement : $nextComma;
|
||||
$variable = $phpcsFile->findNext(T_VARIABLE, $ptr, $varEnd);
|
||||
$varString = trim($phpcsFile->getTokensAsString($ptr, ($varEnd - $ptr)));
|
||||
$data = array($varString);
|
||||
|
||||
if ($variable !== false) {
|
||||
|
||||
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($variable - 1), $ptr, true);
|
||||
|
||||
if ($prev !== false && $tokens[$prev]['type'] === 'T_DOLLAR') {
|
||||
|
||||
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($variable + 1), $varEnd, true);
|
||||
|
||||
if ($next !== false
|
||||
&& in_array($tokens[$next]['code'], array(T_OPEN_SQUARE_BRACKET, T_OBJECT_OPERATOR, T_DOUBLE_COLON), true) === true
|
||||
) {
|
||||
$phpcsFile->addError(
|
||||
'Global with variable variables is not allowed since PHP 7.0. Found %s',
|
||||
$variable,
|
||||
'Found',
|
||||
$data
|
||||
);
|
||||
$errorThrown = true;
|
||||
} else {
|
||||
$phpcsFile->addWarning(
|
||||
'Global with anything other than bare variables is discouraged since PHP 7.0. Found %s',
|
||||
$variable,
|
||||
'NonBareVariableFound',
|
||||
$data
|
||||
);
|
||||
$errorThrown = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($errorThrown === false) {
|
||||
$dollar = $phpcsFile->findNext(T_DOLLAR, $ptr, $varEnd);
|
||||
if ($dollar !== false) {
|
||||
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($dollar + 1), $varEnd, true);
|
||||
if ($tokens[$next]['code'] === T_OPEN_CURLY_BRACKET) {
|
||||
$phpcsFile->addWarning(
|
||||
'Global with anything other than bare variables is discouraged since PHP 7.0. Found %s',
|
||||
$dollar,
|
||||
'NonBareVariableFound',
|
||||
$data
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move the stack pointer forward to the next variable for multi-variable statements.
|
||||
if ($nextComma === false) {
|
||||
break;
|
||||
}
|
||||
$ptr = $nextComma;
|
||||
}
|
||||
}
|
||||
}
|
||||
+424
@@ -0,0 +1,424 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2018 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Variables;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect using $this in incompatible contexts.
|
||||
*
|
||||
* "Whilst $this is considered a special variable in PHP, it lacked proper checks
|
||||
* to ensure it wasn't used as a variable name or reassigned. This has now been
|
||||
* rectified to ensure that $this cannot be a user-defined variable, reassigned
|
||||
* to a different value, or be globalised."
|
||||
*
|
||||
* This sniff only addresses those situations which did *not* throw an error prior
|
||||
* to PHP 7.1, either at all or only in PHP 7.0.
|
||||
* In other words, the following situation, while mentioned in the RFC, will NOT
|
||||
* be sniffed for:
|
||||
* - Using $this as static variable. (error _message_ change only).
|
||||
*
|
||||
* Also, the changes with relation to assigning $this dynamically can not be
|
||||
* sniffed for reliably, so are not covered by this sniff.
|
||||
* - Disable ability to re-assign $this indirectly through $$.
|
||||
* - Disable ability to re-assign $this indirectly through reference.
|
||||
* - Disable ability to re-assign $this indirectly through extract() and parse_str().
|
||||
*
|
||||
* Other changes not (yet) covered:
|
||||
* - get_defined_vars() always doesn't show value of variable $this.
|
||||
* - Always show true $this value in magic method __call().
|
||||
* {@internal This could possibly be covered. Similar logic as "outside object context",
|
||||
* but with function name check and supportsBelow('7.0').}}
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/this_var
|
||||
*
|
||||
* @since 9.1.0
|
||||
*/
|
||||
class ForbiddenThisUseContextsSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* OO scope tokens.
|
||||
*
|
||||
* Duplicate of Tokens::$ooScopeTokens array in PHPCS which was added in 3.1.0.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $ooScopeTokens = array(
|
||||
'T_CLASS' => T_CLASS,
|
||||
'T_INTERFACE' => T_INTERFACE,
|
||||
'T_TRAIT' => T_TRAIT,
|
||||
);
|
||||
|
||||
/**
|
||||
* Scopes to skip over when examining the contents of functions.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $skipOverScopes = array(
|
||||
'T_FUNCTION' => true,
|
||||
'T_CLOSURE' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Valid uses of $this in plain functions or methods outside object context.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $validUseOutsideObject = array(
|
||||
T_ISSET => true,
|
||||
T_EMPTY => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
if (defined('T_ANON_CLASS')) {
|
||||
$this->ooScopeTokens['T_ANON_CLASS'] = T_ANON_CLASS;
|
||||
}
|
||||
|
||||
$this->skipOverScopes += $this->ooScopeTokens;
|
||||
|
||||
return array(
|
||||
T_FUNCTION,
|
||||
T_CLOSURE,
|
||||
T_GLOBAL,
|
||||
T_CATCH,
|
||||
T_FOREACH,
|
||||
T_UNSET,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @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.1') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
switch ($tokens[$stackPtr]['code']) {
|
||||
case T_FUNCTION:
|
||||
$this->isThisUsedAsParameter($phpcsFile, $stackPtr);
|
||||
$this->isThisUsedOutsideObjectContext($phpcsFile, $stackPtr);
|
||||
break;
|
||||
|
||||
case T_CLOSURE:
|
||||
$this->isThisUsedAsParameter($phpcsFile, $stackPtr);
|
||||
break;
|
||||
|
||||
case T_GLOBAL:
|
||||
/*
|
||||
* $this can no longer be imported using the `global` keyword.
|
||||
* This worked in PHP 7.0, though in PHP 5.x, it would throw a
|
||||
* fatal "Cannot re-assign $this" error.
|
||||
*/
|
||||
$endOfStatement = $phpcsFile->findNext(array(T_SEMICOLON, T_CLOSE_TAG), ($stackPtr + 1));
|
||||
if ($endOfStatement === false) {
|
||||
// No semi-colon - live coding.
|
||||
return;
|
||||
}
|
||||
|
||||
for ($i = ($stackPtr + 1); $i < $endOfStatement; $i++) {
|
||||
if ($tokens[$i]['code'] !== T_VARIABLE || $tokens[$i]['content'] !== '$this') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'"$this" can no longer be used with the "global" keyword since PHP 7.1.',
|
||||
$i,
|
||||
'Global'
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case T_CATCH:
|
||||
/*
|
||||
* $this can no longer be used as a catch variable.
|
||||
*/
|
||||
if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$varPtr = $phpcsFile->findNext(
|
||||
T_VARIABLE,
|
||||
($tokens[$stackPtr]['parenthesis_opener'] + 1),
|
||||
$tokens[$stackPtr]['parenthesis_closer']
|
||||
);
|
||||
|
||||
if ($varPtr === false || $tokens[$varPtr]['content'] !== '$this') {
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'"$this" can no longer be used as a catch variable since PHP 7.1.',
|
||||
$varPtr,
|
||||
'Catch'
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case T_FOREACH:
|
||||
/*
|
||||
* $this can no longer be used as a foreach *value* variable.
|
||||
* This worked in PHP 7.0, though in PHP 5.x, it would throw a
|
||||
* fatal "Cannot re-assign $this" error.
|
||||
*/
|
||||
if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$stopPtr = $phpcsFile->findPrevious(
|
||||
array(T_AS, T_DOUBLE_ARROW),
|
||||
($tokens[$stackPtr]['parenthesis_closer'] - 1),
|
||||
$tokens[$stackPtr]['parenthesis_opener']
|
||||
);
|
||||
if ($stopPtr === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$valueVarPtr = $phpcsFile->findNext(
|
||||
T_VARIABLE,
|
||||
($stopPtr + 1),
|
||||
$tokens[$stackPtr]['parenthesis_closer']
|
||||
);
|
||||
if ($valueVarPtr === false || $tokens[$valueVarPtr]['content'] !== '$this') {
|
||||
return;
|
||||
}
|
||||
|
||||
$afterThis = $phpcsFile->findNext(
|
||||
Tokens::$emptyTokens,
|
||||
($valueVarPtr + 1),
|
||||
$tokens[$stackPtr]['parenthesis_closer'],
|
||||
true
|
||||
);
|
||||
|
||||
if ($afterThis !== false
|
||||
&& ($tokens[$afterThis]['code'] === T_OBJECT_OPERATOR
|
||||
|| $tokens[$afterThis]['code'] === T_DOUBLE_COLON)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'"$this" can no longer be used as value variable in a foreach control structure since PHP 7.1.',
|
||||
$valueVarPtr,
|
||||
'ForeachValueVar'
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case T_UNSET:
|
||||
/*
|
||||
* $this can no longer be unset.
|
||||
*/
|
||||
$openParenthesis = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
||||
if ($openParenthesis === false
|
||||
|| $tokens[$openParenthesis]['code'] !== T_OPEN_PARENTHESIS
|
||||
|| isset($tokens[$openParenthesis]['parenthesis_closer']) === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
for ($i = ($openParenthesis + 1); $i < $tokens[$openParenthesis]['parenthesis_closer']; $i++) {
|
||||
if ($tokens[$i]['code'] !== T_VARIABLE || $tokens[$i]['content'] !== '$this') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$afterThis = $phpcsFile->findNext(
|
||||
Tokens::$emptyTokens,
|
||||
($i + 1),
|
||||
$tokens[$openParenthesis]['parenthesis_closer'],
|
||||
true
|
||||
);
|
||||
|
||||
if ($afterThis !== false
|
||||
&& ($tokens[$afterThis]['code'] === T_OBJECT_OPERATOR
|
||||
|| $tokens[$afterThis]['code'] === T_DOUBLE_COLON
|
||||
|| $tokens[$afterThis]['code'] === T_OPEN_SQUARE_BRACKET)
|
||||
) {
|
||||
$i = $afterThis;
|
||||
continue;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'"$this" can no longer be unset since PHP 7.1.',
|
||||
$i,
|
||||
'Unset'
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if $this is used as a parameter in a function declaration.
|
||||
*
|
||||
* $this can no longer be used as a parameter in a *global* function.
|
||||
* Use as a parameter in a method was already an error prior to PHP 7.1.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @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 isThisUsedAsParameter(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->validDirectScope($phpcsFile, $stackPtr, $this->ooScopeTokens) !== false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$params = PHPCSHelper::getMethodParameters($phpcsFile, $stackPtr);
|
||||
if (empty($params)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
foreach ($params as $param) {
|
||||
if ($param['name'] !== '$this') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokens[$stackPtr]['code'] === T_FUNCTION) {
|
||||
$phpcsFile->addError(
|
||||
'"$this" can no longer be used as a parameter since PHP 7.1.',
|
||||
$param['token'],
|
||||
'FunctionParam'
|
||||
);
|
||||
} else {
|
||||
$phpcsFile->addError(
|
||||
'"$this" can no longer be used as a closure parameter since PHP 7.0.7.',
|
||||
$param['token'],
|
||||
'ClosureParam'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if $this is used in a plain function or method.
|
||||
*
|
||||
* Prior to PHP 7.1, this would result in an "undefined variable" notice
|
||||
* and execution would continue with $this regarded as `null`.
|
||||
* As of PHP 7.1, this throws an exception.
|
||||
*
|
||||
* Note: use within isset() and empty() to check object context is still allowed.
|
||||
* Note: $this can still be used within a closure.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @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 isThisUsedOutsideObjectContext(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->validDirectScope($phpcsFile, $stackPtr, $this->ooScopeTokens) !== false) {
|
||||
$methodProps = $phpcsFile->getMethodProperties($stackPtr);
|
||||
if ($methodProps['is_static'] === false) {
|
||||
return;
|
||||
} else {
|
||||
$methodName = $phpcsFile->getDeclarationName($stackPtr);
|
||||
if ($methodName === '__call') {
|
||||
/*
|
||||
* This is an exception.
|
||||
* @link https://wiki.php.net/rfc/this_var#always_show_true_this_value_in_magic_method_call
|
||||
*/
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ($i = ($tokens[$stackPtr]['scope_opener'] + 1); $i < $tokens[$stackPtr]['scope_closer']; $i++) {
|
||||
if (isset($this->skipOverScopes[$tokens[$i]['type']])) {
|
||||
if (isset($tokens[$i]['scope_closer']) === false) {
|
||||
// Live coding or parse error, will only lead to inaccurate results.
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip over nested structures.
|
||||
$i = $tokens[$i]['scope_closer'];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokens[$i]['code'] !== T_VARIABLE || $tokens[$i]['content'] !== '$this') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($tokens[$i]['nested_parenthesis']) === true) {
|
||||
$nestedParenthesis = $tokens[$i]['nested_parenthesis'];
|
||||
$nestedOpenParenthesis = array_keys($nestedParenthesis);
|
||||
$lastOpenParenthesis = array_pop($nestedOpenParenthesis);
|
||||
|
||||
$previousNonEmpty = $phpcsFile->findPrevious(
|
||||
Tokens::$emptyTokens,
|
||||
($lastOpenParenthesis - 1),
|
||||
null,
|
||||
true,
|
||||
null,
|
||||
true
|
||||
);
|
||||
|
||||
if (isset($this->validUseOutsideObject[$tokens[$previousNonEmpty]['code']])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'"$this" can no longer be used in a plain function or method since PHP 7.1.',
|
||||
$i,
|
||||
'OutsideObjectContext'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Variables\NewUniformVariableSyntax.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Variables;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Variables\NewUniformVariableSyntax.
|
||||
*
|
||||
* The interpretation of variable variables has changed in PHP 7.0.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewUniformVariableSyntaxSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(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('7.0') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
// Verify that the next token is a square open bracket. If not, bow out.
|
||||
$nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true, null, true);
|
||||
|
||||
if ($nextToken === false || $tokens[$nextToken]['code'] !== T_OPEN_SQUARE_BRACKET || isset($tokens[$nextToken]['bracket_closer']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The previous non-empty token has to be a $, -> or ::.
|
||||
$prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true, null, true);
|
||||
if ($prevToken === false || in_array($tokens[$prevToken]['code'], array(T_DOLLAR, T_OBJECT_OPERATOR, T_DOUBLE_COLON), true) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For static object calls, it only applies when this is a function call.
|
||||
if ($tokens[$prevToken]['code'] === T_DOUBLE_COLON) {
|
||||
$hasBrackets = $tokens[$nextToken]['bracket_closer'];
|
||||
while (($hasBrackets = $phpcsFile->findNext(Tokens::$emptyTokens, ($hasBrackets + 1), null, true, null, true)) !== false) {
|
||||
if ($tokens[$hasBrackets]['code'] === T_OPEN_SQUARE_BRACKET) {
|
||||
if (isset($tokens[$hasBrackets]['bracket_closer'])) {
|
||||
$hasBrackets = $tokens[$hasBrackets]['bracket_closer'];
|
||||
continue;
|
||||
} else {
|
||||
// Live coding.
|
||||
return;
|
||||
}
|
||||
|
||||
} elseif ($tokens[$hasBrackets]['code'] === T_OPEN_PARENTHESIS) {
|
||||
// Caught!
|
||||
break;
|
||||
|
||||
} else {
|
||||
// Not a function call, so bow out.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Now let's also prevent false positives when used with self and static which still work fine.
|
||||
$classToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prevToken - 1), null, true, null, true);
|
||||
if ($classToken !== false) {
|
||||
if ($tokens[$classToken]['code'] === T_STATIC || $tokens[$classToken]['code'] === T_SELF) {
|
||||
return;
|
||||
} elseif ($tokens[$classToken]['code'] === T_STRING && $tokens[$classToken]['content'] === 'self') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'Indirect access to variables, properties and methods will be evaluated strictly in left-to-right order since PHP 7.0. Use curly braces to remove ambiguity.',
|
||||
$stackPtr,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
+293
@@ -0,0 +1,293 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Variables\RemovedPredefinedGlobalVariablesSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Variables;
|
||||
|
||||
use PHPCompatibility\AbstractRemovedFeatureSniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Variables\RemovedPredefinedGlobalVariablesSniff.
|
||||
*
|
||||
* Discourages the use of removed global variables. Suggests alternative extensions if available
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
class RemovedPredefinedGlobalVariablesSniff extends AbstractRemovedFeatureSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of removed global variables with their alternative, if any.
|
||||
*
|
||||
* The array lists : version number with false (deprecated) and true (removed).
|
||||
* If's sufficient to list the first version where the variable was deprecated/removed.
|
||||
*
|
||||
* @var array(string|null)
|
||||
*/
|
||||
protected $removedGlobalVariables = array(
|
||||
'HTTP_POST_VARS' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'alternative' => '$_POST',
|
||||
),
|
||||
'HTTP_GET_VARS' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'alternative' => '$_GET',
|
||||
),
|
||||
'HTTP_ENV_VARS' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'alternative' => '$_ENV',
|
||||
),
|
||||
'HTTP_SERVER_VARS' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'alternative' => '$_SERVER',
|
||||
),
|
||||
'HTTP_COOKIE_VARS' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'alternative' => '$_COOKIE',
|
||||
),
|
||||
'HTTP_SESSION_VARS' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'alternative' => '$_SESSION',
|
||||
),
|
||||
'HTTP_POST_FILES' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'alternative' => '$_FILES',
|
||||
),
|
||||
|
||||
'HTTP_RAW_POST_DATA' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => 'php://input',
|
||||
),
|
||||
|
||||
'php_errormsg' => array(
|
||||
'7.2' => false,
|
||||
'alternative' => 'error_get_last()',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(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();
|
||||
$varName = substr($tokens[$stackPtr]['content'], 1);
|
||||
|
||||
if (isset($this->removedGlobalVariables[$varName]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isClassProperty($phpcsFile, $stackPtr) === true) {
|
||||
// Ok, so this was a class property declaration, not our concern.
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for static usage of class properties shadowing the removed global variables.
|
||||
if ($this->inClassScope($phpcsFile, $stackPtr, false) === true) {
|
||||
$prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true, null, true);
|
||||
if ($prevToken !== false && $tokens[$prevToken]['code'] === T_DOUBLE_COLON) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Do some additional checks for the $php_errormsg variable.
|
||||
if ($varName === 'php_errormsg'
|
||||
&& $this->isTargetPHPErrormsgVar($phpcsFile, $stackPtr, $tokens) === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Still here, so throw an error/warning.
|
||||
$itemInfo = array(
|
||||
'name' => $varName,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->removedGlobalVariables[$itemInfo['name']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for this sniff.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return "Global variable '\$%s' is ";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the error message before it's passed to PHPCS.
|
||||
*
|
||||
* @param string $error The error message which was created.
|
||||
* @param array $itemInfo Base information about the item this error message applies to.
|
||||
* @param array $errorInfo Detail information about an item this error message applies to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function filterErrorMsg($error, array $itemInfo, array $errorInfo)
|
||||
{
|
||||
if ($itemInfo['name'] === 'php_errormsg') {
|
||||
$error = str_replace('Global', 'The', $error);
|
||||
}
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run some additional checks for the `$php_errormsg` variable.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
* @param array $tokens Token array of the current file.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isTargetPHPErrormsgVar(File $phpcsFile, $stackPtr, array $tokens)
|
||||
{
|
||||
$scopeStart = 0;
|
||||
|
||||
/*
|
||||
* If the variable is detected within the scope of a function/closure, limit the checking.
|
||||
*/
|
||||
$function = $phpcsFile->getCondition($stackPtr, T_CLOSURE);
|
||||
if ($function === false) {
|
||||
$function = $phpcsFile->getCondition($stackPtr, T_FUNCTION);
|
||||
}
|
||||
|
||||
// It could also be a function param, which is not in the function scope.
|
||||
if ($function === false && isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
|
||||
$nestedParentheses = $tokens[$stackPtr]['nested_parenthesis'];
|
||||
$parenthesisCloser = end($nestedParentheses);
|
||||
if (isset($tokens[$parenthesisCloser]['parenthesis_owner'])
|
||||
&& ($tokens[$tokens[$parenthesisCloser]['parenthesis_owner']]['code'] === T_FUNCTION
|
||||
|| $tokens[$tokens[$parenthesisCloser]['parenthesis_owner']]['code'] === T_CLOSURE)
|
||||
) {
|
||||
$function = $tokens[$parenthesisCloser]['parenthesis_owner'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($function !== false) {
|
||||
$scopeStart = $tokens[$function]['scope_opener'];
|
||||
}
|
||||
|
||||
/*
|
||||
* Now, let's do some additional checks.
|
||||
*/
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
||||
|
||||
// Is the variable being used as an array ?
|
||||
if ($nextNonEmpty !== false && $tokens[$nextNonEmpty]['code'] === T_OPEN_SQUARE_BRACKET) {
|
||||
// The PHP native variable is a string, so this is probably not it
|
||||
// (except for array access to string, but why would you in this case ?).
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is this a variable assignment ?
|
||||
if ($nextNonEmpty !== false
|
||||
&& isset(Tokens::$assignmentTokens[$tokens[$nextNonEmpty]['code']]) === true
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is this a function param shadowing the PHP native one ?
|
||||
if ($function !== false) {
|
||||
$parameters = PHPCSHelper::getMethodParameters($phpcsFile, $function);
|
||||
if (is_array($parameters) === true && empty($parameters) === false) {
|
||||
foreach ($parameters as $param) {
|
||||
if ($param['name'] === '$php_errormsg') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$skipPast = array(
|
||||
'T_CLASS' => true,
|
||||
'T_ANON_CLASS' => true,
|
||||
'T_INTERFACE' => true,
|
||||
'T_TRAIT' => true,
|
||||
'T_FUNCTION' => true,
|
||||
'T_CLOSURE' => true,
|
||||
);
|
||||
|
||||
// Walk back and see if there is an assignment to the variable within the same scope.
|
||||
for ($i = ($stackPtr - 1); $i >= $scopeStart; $i--) {
|
||||
if ($tokens[$i]['code'] === T_CLOSE_CURLY_BRACKET
|
||||
&& isset($tokens[$i]['scope_condition'])
|
||||
&& isset($skipPast[$tokens[$tokens[$i]['scope_condition']]['type']])
|
||||
) {
|
||||
// Skip past functions, classes etc.
|
||||
$i = $tokens[$i]['scope_condition'];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokens[$i]['code'] !== T_VARIABLE || $tokens[$i]['content'] !== '$php_errormsg') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($i + 1), null, true);
|
||||
|
||||
if ($nextNonEmpty !== false
|
||||
&& isset(Tokens::$assignmentTokens[$tokens[$nextNonEmpty]['code']]) === true
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user