xarxaprod-wp-theme/vendor/phpcompatibility/php-compatibility/PHPCompatibility/Sniffs/Syntax/RemovedCurlyBraceArrayAcces...

363 lines
12 KiB
PHP

<?php
/**
* PHPCompatibility, an external standard for PHP_CodeSniffer.
*
* @package PHPCompatibility
* @copyright 2012-2019 PHPCompatibility Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCompatibility/PHPCompatibility
*/
namespace PHPCompatibility\Sniffs\Syntax;
use PHPCompatibility\Sniff;
use PHPCompatibility\Sniffs\Syntax\NewArrayStringDereferencingSniff;
use PHPCompatibility\Sniffs\Syntax\NewClassMemberAccessSniff;
use PHPCompatibility\Sniffs\Syntax\NewFunctionArrayDereferencingSniff;
use PHP_CodeSniffer_File as File;
use PHP_CodeSniffer_Tokens as Tokens;
/**
* Using the curly brace syntax to access array or string offsets has been deprecated in PHP 7.4.
*
* PHP version 7.4
*
* @link https://www.php.net/manual/en/migration74.deprecated.php#migration74.deprecated.core.array-string-access-curly-brace
* @link https://wiki.php.net/rfc/deprecate_curly_braces_array_access
*
* @since 9.3.0
*/
class RemovedCurlyBraceArrayAccessSniff extends Sniff
{
/**
* Instance of the NewArrayStringDereferencing sniff.
*
* @since 9.3.0
*
* @var \PHPCompatibility\Sniffs\Syntax\NewArrayStringDereferencingSniff
*/
private $newArrayStringDereferencing;
/**
* Target tokens as register by the NewArrayStringDereferencing sniff.
*
* @since 9.3.0
*
* @var array
*/
private $newArrayStringDereferencingTargets;
/**
* Instance of the NewClassMemberAccess sniff.
*
* @since 9.3.0
*
* @var \PHPCompatibility\Sniffs\Syntax\NewClassMemberAccessSniff
*/
private $newClassMemberAccess;
/**
* Target tokens as register by the NewClassMemberAccess sniff.
*
* @since 9.3.0
*
* @var array
*/
private $newClassMemberAccessTargets;
/**
* Instance of the NewFunctionArrayDereferencing sniff.
*
* @since 9.3.0
*
* @var \PHPCompatibility\Sniffs\Syntax\NewFunctionArrayDereferencingSniff
*/
private $newFunctionArrayDereferencing;
/**
* Target tokens as register by the NewFunctionArrayDereferencing sniff.
*
* @since 9.3.0
*
* @var array
*/
private $newFunctionArrayDereferencingTargets;
/**
* Constructor.
*
* @since 9.3.0
*/
public function __construct()
{
$this->newArrayStringDereferencing = new NewArrayStringDereferencingSniff();
$this->newClassMemberAccess = new NewClassMemberAccessSniff();
$this->newFunctionArrayDereferencing = new NewFunctionArrayDereferencingSniff();
}
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 9.3.0
*
* @return array
*/
public function register()
{
$targets = array(
array(
\T_VARIABLE,
\T_STRING, // Constants.
),
);
// Registers T_ARRAY, T_OPEN_SHORT_ARRAY and T_CONSTANT_ENCAPSED_STRING.
$additionalTargets = $this->newArrayStringDereferencing->register();
$this->newArrayStringDereferencingTargets = array_flip($additionalTargets);
$targets[] = $additionalTargets;
// Registers T_NEW and T_CLONE.
$additionalTargets = $this->newClassMemberAccess->register();
$this->newClassMemberAccessTargets = array_flip($additionalTargets);
$targets[] = $additionalTargets;
// Registers T_STRING.
$additionalTargets = $this->newFunctionArrayDereferencing->register();
$this->newFunctionArrayDereferencingTargets = array_flip($additionalTargets);
$targets[] = $additionalTargets;
return call_user_func_array('array_merge', $targets);
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 9.3.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.4') === false) {
return;
}
$tokens = $phpcsFile->getTokens();
$braces = array();
// Note: Overwriting braces in each `if` is fine as only one will match anyway.
if ($tokens[$stackPtr]['code'] === \T_VARIABLE) {
$braces = $this->isVariableArrayAccess($phpcsFile, $stackPtr);
}
if (isset($this->newArrayStringDereferencingTargets[$tokens[$stackPtr]['code']])) {
$dereferencing = $this->newArrayStringDereferencing->isArrayStringDereferencing($phpcsFile, $stackPtr);
if (isset($dereferencing['braces'])) {
$braces = $dereferencing['braces'];
}
}
if (isset($this->newClassMemberAccessTargets[$tokens[$stackPtr]['code']])) {
$braces = $this->newClassMemberAccess->isClassMemberAccess($phpcsFile, $stackPtr);
}
if (isset($this->newFunctionArrayDereferencingTargets[$tokens[$stackPtr]['code']])) {
$braces = $this->newFunctionArrayDereferencing->isFunctionArrayDereferencing($phpcsFile, $stackPtr);
}
if (empty($braces) && $tokens[$stackPtr]['code'] === \T_STRING) {
$braces = $this->isConstantArrayAccess($phpcsFile, $stackPtr);
}
if (empty($braces)) {
return;
}
foreach ($braces as $open => $close) {
// Some of the functions will sniff for both curlies as well as square braces.
if ($tokens[$open]['code'] !== \T_OPEN_CURLY_BRACKET) {
continue;
}
// Make sure there is something between the braces, otherwise it's still not curly brace array access.
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($open + 1), $close, true);
if ($nextNonEmpty === false) {
// Nothing between the brackets. Parse error. Ignore.
continue;
}
// OK, so we've found curly brace array access.
$snippet = $phpcsFile->getTokensAsString($stackPtr, (($close - $stackPtr) + 1));
$fix = $phpcsFile->addFixableWarning(
'Curly brace syntax for accessing array elements and string offsets has been deprecated in PHP 7.4. Found: %s',
$open,
'Found',
array($snippet)
);
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->replaceToken($open, '[');
$phpcsFile->fixer->replaceToken($close, ']');
$phpcsFile->fixer->endChangeset();
}
}
}
/**
* Determine whether a variable is being dereferenced using curly brace syntax.
*
* @since 9.3.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 array An array with the stack pointers to the open/close braces of
* the curly brace array access, or an empty array if no curly
* brace array access was detected.
*/
protected function isVariableArrayAccess(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$current = $stackPtr;
$braces = array();
do {
$current = $phpcsFile->findNext(Tokens::$emptyTokens, ($current + 1), null, true);
if ($current === false) {
break;
}
// Skip over square bracket array access. Bracket styles can be mixed.
if ($tokens[$current]['code'] === \T_OPEN_SQUARE_BRACKET
&& isset($tokens[$current]['bracket_closer']) === true
&& $current === $tokens[$current]['bracket_opener']
) {
$current = $tokens[$current]['bracket_closer'];
continue;
}
// Handle property access.
if ($tokens[$current]['code'] === \T_OBJECT_OPERATOR) {
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($current + 1), null, true);
if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_STRING) {
// Live coding or parse error.
break;
}
$current = $nextNonEmpty;
continue;
}
if ($tokens[$current]['code'] === \T_OPEN_CURLY_BRACKET) {
if (isset($tokens[$current]['bracket_closer']) === false) {
// Live coding or parse error.
break;
}
$braces[$current] = $tokens[$current]['bracket_closer'];
// Continue, just in case there is nested access using curly braces, i.e. `$a{$i}{$j};`.
$current = $tokens[$current]['bracket_closer'];
continue;
}
// If we're still here, we've reached the end of the variable.
break;
} while (true);
return $braces;
}
/**
* Determine whether a T_STRING is a constant being dereferenced using curly brace syntax.
*
* {@internal Note: the first braces for array access to a constant, for some unknown reason,
* can never be curlies, but have to be square brackets.
* Subsequent braces can be curlies.}
*
* @since 9.3.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 array An array with the stack pointers to the open/close braces of
* the curly brace array access, or an empty array if no curly
* brace array access was detected.
*/
protected function isConstantArrayAccess(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if ($this->isUseOfGlobalConstant($phpcsFile, $stackPtr) === false
&& $tokens[$prevNonEmpty]['code'] !== \T_DOUBLE_COLON // Class constant access.
) {
return array();
}
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($nextNonEmpty === false) {
return array();
}
if ($tokens[$nextNonEmpty]['code'] !== \T_OPEN_SQUARE_BRACKET
|| isset($tokens[$nextNonEmpty]['bracket_closer']) === false
) {
// Array access for constants must start with square brackets.
return array();
}
$current = $tokens[$nextNonEmpty]['bracket_closer'];
$braces = array();
do {
$current = $phpcsFile->findNext(Tokens::$emptyTokens, ($current + 1), null, true);
if ($current === false) {
break;
}
// Skip over square bracket array access. Bracket styles can be mixed.
if ($tokens[$current]['code'] === \T_OPEN_SQUARE_BRACKET
&& isset($tokens[$current]['bracket_closer']) === true
&& $current === $tokens[$current]['bracket_opener']
) {
$current = $tokens[$current]['bracket_closer'];
continue;
}
if ($tokens[$current]['code'] === \T_OPEN_CURLY_BRACKET) {
if (isset($tokens[$current]['bracket_closer']) === false) {
// Live coding or parse error.
break;
}
$braces[$current] = $tokens[$current]['bracket_closer'];
// Continue, just in case there is nested access using curly braces, i.e. `$a{$i}{$j};`.
$current = $tokens[$current]['bracket_closer'];
continue;
}
// If we're still here, we've reached the end of the variable.
break;
} while (true);
return $braces;
}
}