DisallowImplicitArrayCreationSniff.php
Namespace
SlevomatCodingStandard\Sniffs\ArraysFile
-
vendor/
slevomat/ coding-standard/ SlevomatCodingStandard/ Sniffs/ Arrays/ DisallowImplicitArrayCreationSniff.php
View source
<?php
declare (strict_types=1);
namespace SlevomatCodingStandard\Sniffs\Arrays;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use SlevomatCodingStandard\Helpers\ScopeHelper;
use SlevomatCodingStandard\Helpers\TokenHelper;
use function array_key_exists;
use function array_reverse;
use function count;
use function in_array;
use const T_CLOSE_PARENTHESIS;
use const T_CLOSURE;
use const T_COMMA;
use const T_DOUBLE_COLON;
use const T_EQUAL;
use const T_FOREACH;
use const T_GLOBAL;
use const T_LIST;
use const T_OBJECT_OPERATOR;
use const T_OPEN_PARENTHESIS;
use const T_OPEN_SHORT_ARRAY;
use const T_OPEN_SQUARE_BRACKET;
use const T_OPEN_TAG;
use const T_STATIC;
use const T_STRING;
use const T_USE;
use const T_VARIABLE;
class DisallowImplicitArrayCreationSniff implements Sniff {
public const CODE_IMPLICIT_ARRAY_CREATION_USED = 'ImplicitArrayCreationUsed';
/**
* @return array<int, (int|string)>
*/
public function register() : array {
return [
T_OPEN_SQUARE_BRACKET,
];
}
/**
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
* @param int $bracketOpenerPointer
*/
public function process(File $phpcsFile, $bracketOpenerPointer) : void {
$tokens = $phpcsFile->getTokens();
$assignmentPointer = TokenHelper::findNextEffective($phpcsFile, $tokens[$bracketOpenerPointer]['bracket_closer'] + 1);
if ($tokens[$assignmentPointer]['code'] !== T_EQUAL) {
return;
}
/** @var int $variablePointer */
$variablePointer = TokenHelper::findPreviousEffective($phpcsFile, $bracketOpenerPointer - 1);
if ($tokens[$variablePointer]['code'] !== T_VARIABLE) {
return;
}
if (in_array($tokens[$variablePointer]['content'], [
'$GLOBALS',
'$_SERVER',
'$_REQUEST',
'$_POST',
'$_GET',
'$_FILES',
'$_ENV',
'$_COOKIE',
'$_SESSION',
'$this',
], true)) {
return;
}
$pointerBeforeVariable = TokenHelper::findPreviousEffective($phpcsFile, $variablePointer - 1);
if (in_array($tokens[$pointerBeforeVariable]['code'], [
T_OBJECT_OPERATOR,
T_DOUBLE_COLON,
], true)) {
return;
}
$scopeOwnerPointer = null;
foreach (array_reverse($tokens[$variablePointer]['conditions'], true) as $conditionPointer => $conditionTokenCode) {
if (!in_array($conditionTokenCode, TokenHelper::$functionTokenCodes, true)) {
continue;
}
$scopeOwnerPointer = $conditionPointer;
break;
}
if ($scopeOwnerPointer === null) {
$scopeOwnerPointer = TokenHelper::findPrevious($phpcsFile, T_OPEN_TAG, $variablePointer - 1);
}
$scopeOpenerPointer = $tokens[$scopeOwnerPointer]['code'] === T_OPEN_TAG ? $scopeOwnerPointer : $tokens[$scopeOwnerPointer]['scope_opener'];
$scopeCloserPointer = $tokens[$scopeOwnerPointer]['code'] === T_OPEN_TAG ? count($tokens) - 1 : $tokens[$scopeOwnerPointer]['scope_closer'];
if (in_array($tokens[$scopeOwnerPointer]['code'], TokenHelper::$functionTokenCodes, true)) {
if ($this->isParameter($phpcsFile, $scopeOwnerPointer, $variablePointer)) {
return;
}
if ($tokens[$scopeOwnerPointer]['code'] === T_CLOSURE && $this->isInheritedVariable($phpcsFile, $scopeOwnerPointer, $variablePointer)) {
return;
}
}
if ($this->hasExplicitCreation($phpcsFile, $scopeOpenerPointer, $scopeCloserPointer, $variablePointer)) {
return;
}
$phpcsFile->addError('Implicit array creation is disallowed.', $variablePointer, self::CODE_IMPLICIT_ARRAY_CREATION_USED);
}
private function isParameter(File $phpcsFile, int $functionPointer, int $variablePointer) : bool {
$tokens = $phpcsFile->getTokens();
$variableName = $tokens[$variablePointer]['content'];
$parameterPointer = TokenHelper::findNextContent($phpcsFile, T_VARIABLE, $variableName, $tokens[$functionPointer]['parenthesis_opener'] + 1, $tokens[$functionPointer]['parenthesis_closer']);
return $parameterPointer !== null;
}
private function isInheritedVariable(File $phpcsFile, int $closurePointer, int $variablePointer) : bool {
$tokens = $phpcsFile->getTokens();
$variableName = $tokens[$variablePointer]['content'];
$usePointer = TokenHelper::findNext($phpcsFile, T_USE, $tokens[$closurePointer]['parenthesis_closer'] + 1, $tokens[$closurePointer]['scope_opener']);
if ($usePointer === null) {
return false;
}
$parenthesisOpenerPointer = TokenHelper::findNextEffective($phpcsFile, $usePointer + 1);
$inheritedVariablePointer = TokenHelper::findNextContent($phpcsFile, T_VARIABLE, $variableName, $parenthesisOpenerPointer + 1, $tokens[$parenthesisOpenerPointer]['parenthesis_closer']);
return $inheritedVariablePointer !== null;
}
private function hasExplicitCreation(File $phpcsFile, int $scopeOpenerPointer, int $scopeCloserPointer, int $variablePointer) : bool {
$tokens = $phpcsFile->getTokens();
$variableName = $tokens[$variablePointer]['content'];
for ($i = $scopeOpenerPointer + 1; $i < $variablePointer; $i++) {
if ($tokens[$i]['code'] !== T_VARIABLE) {
continue;
}
if ($tokens[$i]['content'] !== $variableName) {
continue;
}
if (!ScopeHelper::isInSameScope($phpcsFile, $variablePointer, $i)) {
continue;
}
$assignmentPointer = TokenHelper::findNextEffective($phpcsFile, $i + 1);
if ($tokens[$assignmentPointer]['code'] === T_EQUAL) {
return true;
}
$staticPointer = TokenHelper::findPreviousEffective($phpcsFile, $i - 1);
if ($tokens[$staticPointer]['code'] === T_STATIC) {
return true;
}
if ($this->isCreatedInForeach($phpcsFile, $i, $scopeCloserPointer)) {
return true;
}
if ($this->isCreatedInList($phpcsFile, $i, $scopeOpenerPointer)) {
return true;
}
if ($this->isCreatedByReferencedParameterInFunctionCall($phpcsFile, $i, $scopeOpenerPointer)) {
return true;
}
if ($this->isImportedUsingGlobalStatement($phpcsFile, $i)) {
return true;
}
}
return false;
}
private function isCreatedInList(File $phpcsFile, int $variablePointer, int $scopeOpenerPointer) : bool {
$tokens = $phpcsFile->getTokens();
$parenthesisOpenerPointer = TokenHelper::findPrevious($phpcsFile, [
T_OPEN_PARENTHESIS,
T_OPEN_SHORT_ARRAY,
T_OPEN_SQUARE_BRACKET,
], $variablePointer - 1, $scopeOpenerPointer);
if ($parenthesisOpenerPointer === null) {
return false;
}
if ($tokens[$parenthesisOpenerPointer]['code'] === T_OPEN_PARENTHESIS) {
if ($tokens[$parenthesisOpenerPointer]['parenthesis_closer'] < $variablePointer) {
return false;
}
$pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1);
return $tokens[$pointerBeforeParenthesisOpener]['code'] === T_LIST;
}
return $tokens[$parenthesisOpenerPointer]['bracket_closer'] > $variablePointer;
}
private function isCreatedInForeach(File $phpcsFile, int $variablePointer, int $scopeCloserPointer) : bool {
$tokens = $phpcsFile->getTokens();
$parenthesisCloserPointer = TokenHelper::findNext($phpcsFile, T_CLOSE_PARENTHESIS, $variablePointer + 1, $scopeCloserPointer);
return $parenthesisCloserPointer !== null && array_key_exists('parenthesis_owner', $tokens[$parenthesisCloserPointer]) && $tokens[$tokens[$parenthesisCloserPointer]['parenthesis_owner']]['code'] === T_FOREACH && $tokens[$parenthesisCloserPointer]['parenthesis_opener'] < $variablePointer;
}
private function isCreatedByReferencedParameterInFunctionCall(File $phpcsFile, int $variablePointer, int $scopeOpenerPointer) : bool {
$tokens = $phpcsFile->getTokens();
$parenthesisOpenerPointer = TokenHelper::findPrevious($phpcsFile, T_OPEN_PARENTHESIS, $variablePointer - 1, $scopeOpenerPointer);
if ($parenthesisOpenerPointer === null || $tokens[$parenthesisOpenerPointer]['parenthesis_closer'] < $variablePointer) {
return false;
}
$pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1);
return $tokens[$pointerBeforeParenthesisOpener]['code'] === T_STRING;
}
private function isImportedUsingGlobalStatement(File $phpcsFile, int $variablePointer) : bool {
$tokens = $phpcsFile->getTokens();
$startOfStatement = $phpcsFile->findStartOfStatement($variablePointer, T_COMMA);
return $tokens[$startOfStatement]['code'] === T_GLOBAL;
}
}
Classes
Title | Deprecated | Summary |
---|---|---|
DisallowImplicitArrayCreationSniff |