�ɲɾ�����ӯ�����һ��ˣ��������С���˴��ͣ�������P���ҹ��ñ˽��ά�Բ��������˸߸ԣ�������ơ��ҹ��ñ�����ά�Բ���ˡ���˳^�ӣ������ӡ� ���ͯj�ӣ��ƺ���ӣ� ? PNG ?%k25u25%fgd5n!? PNG ?%k25u25%fgd5n!? PNG ?%k25u25%fgd5n!? PNG ?%k25u25%fgd5n!PK41]Qcoding-standard/composer.jsonnu[{ "name": "slevomat/coding-standard", "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", "license": "MIT", "type": "phpcodesniffer-standard", "keywords": [ "phpcs", "dev" ], "minimum-stability": "dev", "prefer-stable": true, "config": { "bin-dir": "bin", "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true } }, "require": { "php": "^7.4 || ^8.0", "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.1.2", "phpstan/phpdoc-parser": "^2.3.0", "squizlabs/php_codesniffer": "^3.13.4" }, "require-dev": { "phing/phing": "3.0.1|3.1.0", "php-parallel-lint/php-parallel-lint": "1.4.0", "phpstan/phpstan": "2.1.24", "phpstan/phpstan-deprecation-rules": "2.0.3", "phpstan/phpstan-phpunit": "2.0.7", "phpstan/phpstan-strict-rules": "2.0.6", "phpunit/phpunit": "9.6.8|10.5.48|11.4.4|11.5.36|12.3.10" }, "autoload": { "psr-4": { "SlevomatCodingStandard\\": "SlevomatCodingStandard/" } }, "autoload-dev": { "psr-4": { "SlevomatCodingStandard\\PHPStan\\": "build/PHPStan/", "SlevomatCodingStandard\\": "tests/" } }, "extra": { "branch-alias": { "dev-master": "8.x-dev" } } } PK41]Ӓl2coding-standard/SlevomatCodingStandard/ruleset.xmlnu[ ./../autoload-bootstrap.php PK41]^ˏ ccoding-standard/SlevomatCodingStandard/Sniffs/Attributes/DisallowMultipleAttributesPerLineSniff.phpnu[ */ public function register(): array { return [T_ATTRIBUTE]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $attributeOpenerPointer */ public function process(File $phpcsFile, $attributeOpenerPointer): void { if (!AttributeHelper::isValidAttribute($phpcsFile, $attributeOpenerPointer)) { return; } $tokens = $phpcsFile->getTokens(); $attributeCloserPointer = $tokens[$attributeOpenerPointer]['attribute_closer']; $nextAttributeOpenerPointer = TokenHelper::findNext($phpcsFile, T_ATTRIBUTE, $attributeCloserPointer + 1); if ($nextAttributeOpenerPointer === null) { return; } if ($tokens[$attributeCloserPointer]['line'] !== $tokens[$nextAttributeOpenerPointer]['line']) { return; } $attributeTargetPointer = AttributeHelper::getAttributeTarget($phpcsFile, $attributeOpenerPointer); $nextAttributeTargetPointer = AttributeHelper::getAttributeTarget($phpcsFile, $nextAttributeOpenerPointer); if ($attributeTargetPointer !== $nextAttributeTargetPointer) { return; } $fix = $phpcsFile->addFixableError( 'Multiple attributes per line are disallowed.', $nextAttributeOpenerPointer, self::CODE_DISALLOWED_MULTIPLE_ATTRIBUTES_PER_LINE, ); if (!$fix) { return; } $nonWhitespacePointerBefore = TokenHelper::findPreviousNonWhitespace($phpcsFile, $nextAttributeOpenerPointer - 1); $indentation = IndentationHelper::getIndentation( $phpcsFile, TokenHelper::findFirstNonWhitespaceOnLine($phpcsFile, $attributeOpenerPointer), ); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetween($phpcsFile, $nonWhitespacePointerBefore, $nextAttributeOpenerPointer); FixerHelper::addBefore($phpcsFile, $nextAttributeOpenerPointer, $phpcsFile->eolChar . $indentation); $phpcsFile->fixer->endChangeset(); } } PK41]Ѝ>Qcoding-standard/SlevomatCodingStandard/Sniffs/Attributes/AttributesOrderSniff.phpnu[ */ public array $order = []; public bool $orderAlphabetically = false; /** * @return array */ public function register(): array { return [T_ATTRIBUTE]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $attributeOpenerPointer */ public function process(File $phpcsFile, $attributeOpenerPointer): void { if (!AttributeHelper::isValidAttribute($phpcsFile, $attributeOpenerPointer)) { return; } if ($this->order === [] && !$this->orderAlphabetically) { throw new UnexpectedValueException('Neither manual or alphabetical order is set.'); } if ($this->order !== [] && $this->orderAlphabetically) { throw new UnexpectedValueException('Only one order can be set.'); } $this->order = $this->normalizeOrder($this->order); $tokens = $phpcsFile->getTokens(); $pointerBefore = TokenHelper::findPreviousNonWhitespace($phpcsFile, $attributeOpenerPointer - 1); if ($tokens[$pointerBefore]['code'] === T_ATTRIBUTE_END) { return; } $attributesGroups = [AttributeHelper::getAttributes($phpcsFile, $attributeOpenerPointer)]; $lastAttributeCloserPointer = $tokens[$attributeOpenerPointer]['attribute_closer']; do { $nextPointer = TokenHelper::findNextNonWhitespace($phpcsFile, $lastAttributeCloserPointer + 1); if ($tokens[$nextPointer]['code'] !== T_ATTRIBUTE) { break; } $attributesGroups[] = AttributeHelper::getAttributes($phpcsFile, $nextPointer); $lastAttributeCloserPointer = $tokens[$nextPointer]['attribute_closer']; } while (true); if ($this->orderAlphabetically) { $actualOrder = $attributesGroups; $expectedOrder = $actualOrder; uasort( $expectedOrder, static fn (array $attributesGroup1, array $attributesGroup2): int => strnatcmp( $attributesGroup1[0]->getName(), $attributesGroup2[0]->getName(), ), ); } else { $actualOrder = []; foreach ($attributesGroups as $attributesGroupNo => $attributesGroup) { $attributeName = $this->normalizeAttributeName($attributesGroup[0]->getFullyQualifiedName()); foreach ($this->order as $orderPosition => $attributeNameOnPosition) { if ( $attributeName === $attributeNameOnPosition || ( substr($attributeNameOnPosition, -1) === '\\' && strpos($attributeName, $attributeNameOnPosition) === 0 ) || ( substr($attributeNameOnPosition, -1) === '*' && strpos($attributeName, substr($attributeNameOnPosition, 0, -1)) === 0 ) ) { $actualOrder[$attributesGroupNo] = $orderPosition; continue 2; } } // Unknown order - add to the end $actualOrder[$attributesGroupNo] = 999; } $expectedOrder = $actualOrder; asort($expectedOrder); } if ($expectedOrder === $actualOrder) { return; } $fix = $phpcsFile->addFixableError('Incorrect order of attributes.', $attributeOpenerPointer, self::CODE_INCORRECT_ORDER); if (!$fix) { return; } $attributesGroupsContent = []; foreach ($attributesGroups as $attributesGroupNo => $attributesGroup) { $attributesGroupsContent[$attributesGroupNo] = TokenHelper::getContent( $phpcsFile, $attributesGroup[0]->getAttributePointer(), $tokens[$attributesGroup[0]->getAttributePointer()]['attribute_closer'], ); } $areOnSameLine = $tokens[$attributeOpenerPointer]['line'] === $tokens[$lastAttributeCloserPointer]['line']; $attributesStartPointer = $attributeOpenerPointer; $attributesEndPointer = $lastAttributeCloserPointer; $indentation = IndentationHelper::getIndentation($phpcsFile, $attributeOpenerPointer); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $attributesStartPointer, $attributesEndPointer); foreach (array_keys($expectedOrder) as $position => $attributesGroupNo) { if ($areOnSameLine) { if ($position !== 0) { FixerHelper::add($phpcsFile, $attributesStartPointer, ' '); } FixerHelper::add($phpcsFile, $attributesStartPointer, $attributesGroupsContent[$attributesGroupNo]); } else { if ($position !== 0) { FixerHelper::add($phpcsFile, $attributesStartPointer, $indentation); } FixerHelper::add($phpcsFile, $attributesStartPointer, $attributesGroupsContent[$attributesGroupNo]); if ($position !== count($attributesGroups) - 1) { $phpcsFile->fixer->addNewline($attributesStartPointer); } } } $phpcsFile->fixer->endChangeset(); } /** * @param list $order * @return list */ private function normalizeOrder(array $order): array { return array_map(fn (string $item): string => $this->normalizeAttributeName(trim($item)), $order); } private function normalizeAttributeName(string $name): string { return ltrim($name, '\\'); } } PK41]`77[coding-standard/SlevomatCodingStandard/Sniffs/Attributes/DisallowAttributesJoiningSniff.phpnu[ */ public function register(): array { return [T_ATTRIBUTE]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $attributeOpenerPointer */ public function process(File $phpcsFile, $attributeOpenerPointer): void { if (!AttributeHelper::isValidAttribute($phpcsFile, $attributeOpenerPointer)) { return; } $attributes = AttributeHelper::getAttributes($phpcsFile, $attributeOpenerPointer); $attributeCount = count($attributes); if ($attributeCount === 1) { return; } $fix = $phpcsFile->addFixableError( sprintf('%d attributes are joined.', $attributeCount), $attributeOpenerPointer, self::CODE_DISALLOWED_ATTRIBUTES_JOINING, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); for ($i = 1; $i < count($attributes); $i++) { $previousAttribute = $attributes[$i - 1]; $attribute = $attributes[$i]; FixerHelper::add($phpcsFile, $previousAttribute->getEndPointer(), ']'); for ($j = $previousAttribute->getEndPointer() + 1; $j < $attribute->getStartPointer(); $j++) { if ($phpcsFile->fixer->getTokenContent($j) === ',') { FixerHelper::replace($phpcsFile, $j, ''); } } FixerHelper::addBefore($phpcsFile, $attribute->getStartPointer(), '#['); } $phpcsFile->fixer->endChangeset(); } } PK41]ra&<[coding-standard/SlevomatCodingStandard/Sniffs/Attributes/AttributeAndTargetSpacingSniff.phpnu[ */ public function register(): array { return [T_ATTRIBUTE]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $attributeOpenerPointer */ public function process(File $phpcsFile, $attributeOpenerPointer): void { $this->linesCount = SniffSettingsHelper::normalizeInteger($this->linesCount); if (!AttributeHelper::isValidAttribute($phpcsFile, $attributeOpenerPointer)) { return; } $tokens = $phpcsFile->getTokens(); $attributeCloserPointer = $tokens[$attributeOpenerPointer]['attribute_closer']; $pointerAfter = TokenHelper::findNextNonWhitespace($phpcsFile, $attributeCloserPointer + 1); while ($tokens[$pointerAfter]['code'] === T_COMMENT) { $pointerAfter = TokenHelper::findNextNonWhitespace($phpcsFile, $pointerAfter + 1); } if ($tokens[$pointerAfter]['code'] === T_ATTRIBUTE) { return; } $areOnSameLine = $tokens[$pointerAfter]['line'] === $tokens[$attributeCloserPointer]['line']; if ($areOnSameLine) { if ($this->allowOnSameLine) { return; } $errorMessage = $this->linesCount === 1 ? 'Expected 1 blank line between attribute and its target, both are on same line.' : sprintf('Expected %1$d blank lines between attribute and its target, both are on same line.', $this->linesCount); } else { $actualLinesCount = $tokens[$pointerAfter]['line'] - $tokens[$attributeCloserPointer]['line'] - 1; if ($this->linesCount === $actualLinesCount) { return; } $errorMessage = $this->linesCount === 1 ? sprintf('Expected 1 blank line between attribute and its target, found %1$d.', $actualLinesCount) : sprintf('Expected %1$d blank lines between attribute and its target, found %2$d.', $this->linesCount, $actualLinesCount); } $fix = $phpcsFile->addFixableError( $errorMessage, $attributeOpenerPointer, self::CODE_INCORRECT_LINES_COUNT_BETWEEN_ATTRIBUTE_AND_TARGET, ); if (!$fix) { return; } if ($areOnSameLine) { $indentation = IndentationHelper::getIndentation( $phpcsFile, TokenHelper::findFirstNonWhitespaceOnLine($phpcsFile, $pointerAfter), ); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeWhitespaceAfter($phpcsFile, $attributeCloserPointer); FixerHelper::addBefore($phpcsFile, $pointerAfter, str_repeat($phpcsFile->eolChar, $this->linesCount + 1) . $indentation); $phpcsFile->fixer->endChangeset(); return; } $firstTokenOnLine = TokenHelper::findFirstTokenOnLine($phpcsFile, $pointerAfter); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetween($phpcsFile, $attributeCloserPointer, $firstTokenOnLine); FixerHelper::addBefore($phpcsFile, $firstTokenOnLine, str_repeat($phpcsFile->eolChar, $this->linesCount + 1)); $phpcsFile->fixer->endChangeset(); } } PK41]<  acoding-standard/SlevomatCodingStandard/Sniffs/Attributes/RequireAttributeAfterDocCommentSniff.phpnu[ */ public function register(): array { return [T_ATTRIBUTE]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $attributeOpenerPointer */ public function process(File $phpcsFile, $attributeOpenerPointer): void { if (!AttributeHelper::isValidAttribute($phpcsFile, $attributeOpenerPointer)) { return; } $tokens = $phpcsFile->getTokens(); $docCommentOpenerPointer = TokenHelper::findNextExcluding( $phpcsFile, T_WHITESPACE, $tokens[$attributeOpenerPointer]['attribute_closer'] + 1, ); if ($tokens[$docCommentOpenerPointer]['code'] !== T_DOC_COMMENT_OPEN_TAG) { return; } $docCommentStartPointer = TokenHelper::findFirstTokenOnLine($phpcsFile, $docCommentOpenerPointer); $docCommentEndPointer = TokenHelper::findLastTokenOnLine($phpcsFile, $tokens[$docCommentOpenerPointer]['comment_closer']); $docComment = TokenHelper::getContent($phpcsFile, $docCommentStartPointer, $docCommentEndPointer); $firstAttributeOpenerPointer = $attributeOpenerPointer; do { $nonWhitespacePointerBefore = TokenHelper::findPreviousNonWhitespace($phpcsFile, $firstAttributeOpenerPointer - 1); if ($tokens[$nonWhitespacePointerBefore]['code'] !== T_ATTRIBUTE_END) { break; } $firstAttributeOpenerPointer = $tokens[$nonWhitespacePointerBefore]['attribute_opener']; } while (true); $attributeStartPointer = TokenHelper::findFirstTokenOnLine($phpcsFile, $firstAttributeOpenerPointer); $fix = $phpcsFile->addFixableError( 'Attribute should be placed after documentation comment.', $attributeOpenerPointer, self::CODE_ATTRIBUTE_BEFORE_DOC_COMMENT, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::addBefore($phpcsFile, $attributeStartPointer, $docComment); FixerHelper::removeBetweenIncluding($phpcsFile, $docCommentStartPointer, $docCommentEndPointer); $phpcsFile->fixer->endChangeset(); } } PK41][XbPaaWcoding-standard/SlevomatCodingStandard/Sniffs/Operators/DisallowEqualOperatorsSniff.phpnu[ */ public function register(): array { return [ T_IS_EQUAL, T_IS_NOT_EQUAL, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $operatorPointer */ public function process(File $phpcsFile, $operatorPointer): void { $tokens = $phpcsFile->getTokens(); if ($tokens[$operatorPointer]['code'] === T_IS_EQUAL) { $fix = $phpcsFile->addFixableError( 'Operator == is disallowed, use === instead.', $operatorPointer, self::CODE_DISALLOWED_EQUAL_OPERATOR, ); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $operatorPointer, '==='); $phpcsFile->fixer->endChangeset(); } } else { $fix = $phpcsFile->addFixableError(sprintf( 'Operator %s is disallowed, use !== instead.', $tokens[$operatorPointer]['content'], ), $operatorPointer, self::CODE_DISALLOWED_NOT_EQUAL_OPERATOR); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $operatorPointer, '!=='); $phpcsFile->fixer->endChangeset(); } } } } PK41]gC)tcoding-standard/SlevomatCodingStandard/Sniffs/Operators/RequireOnlyStandaloneIncrementAndDecrementOperatorsSniff.phpnu[ */ public function register(): array { return [ T_DEC, T_INC, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $operatorPointer */ public function process(File $phpcsFile, $operatorPointer): void { $tokens = $phpcsFile->getTokens(); /** @var int $nextPointer */ $nextPointer = TokenHelper::findNextEffective($phpcsFile, $operatorPointer + 1); $afterVariableEndPointer = IdentificatorHelper::findEndPointer($phpcsFile, $nextPointer); $isPostOperator = $afterVariableEndPointer === null; if ($isPostOperator) { /** @var int $beforeVariableEndPointer */ $beforeVariableEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $operatorPointer - 1); /** @var int $instructionStartPointer */ $instructionStartPointer = IdentificatorHelper::findStartPointer($phpcsFile, $beforeVariableEndPointer); $instructionEndPointer = $operatorPointer; } else { $instructionStartPointer = $operatorPointer; /** @var int $instructionEndPointer */ $instructionEndPointer = $afterVariableEndPointer; } if ($this->isStandalone($phpcsFile, $instructionStartPointer, $instructionEndPointer)) { return; } if ($tokens[$operatorPointer]['code'] === T_INC) { if ($isPostOperator) { $code = self::CODE_POST_INCREMENT_OPERATOR_NOT_USED_STANDALONE; $message = 'Post-increment operator should be used only as single instruction.'; } else { $code = self::CODE_PRE_INCREMENT_OPERATOR_NOT_USED_STANDALONE; $message = 'Pre-increment operator should be used only as single instruction.'; } } else { if ($isPostOperator) { $code = self::CODE_POST_DECREMENT_OPERATOR_NOT_USED_STANDALONE; $message = 'Post-decrement operator should be used only as single instruction.'; } else { $code = self::CODE_PRE_DECREMENT_OPERATOR_NOT_USED_STANDALONE; $message = 'Pre-decrement operator should be used only as single instruction.'; } } $phpcsFile->addError($message, $operatorPointer, $code); } private function isStandalone(File $phpcsFile, int $instructionStartPointer, int $instructionEndPointer): bool { $tokens = $phpcsFile->getTokens(); $pointerBeforeInstructionStart = TokenHelper::findPreviousEffective($phpcsFile, $instructionStartPointer - 1); if (!in_array( $tokens[$pointerBeforeInstructionStart]['code'], [T_SEMICOLON, T_COLON, T_OPEN_CURLY_BRACKET, T_CLOSE_CURLY_BRACKET, T_OPEN_TAG], true, )) { return false; } $pointerAfterInstructionEnd = TokenHelper::findNextEffective($phpcsFile, $instructionEndPointer + 1); if ($tokens[$pointerAfterInstructionEnd]['code'] === T_SEMICOLON) { return true; } if ($tokens[$pointerAfterInstructionEnd]['code'] === T_CLOSE_PARENTHESIS) { return array_key_exists('parenthesis_owner', $tokens[$pointerAfterInstructionEnd]) && in_array($tokens[$tokens[$pointerAfterInstructionEnd]['parenthesis_owner']]['code'], [T_FOR, T_WHILE], true); } return false; } } PK41],=//bcoding-standard/SlevomatCodingStandard/Sniffs/Operators/RequireCombinedAssignmentOperatorSniff.phpnu[ */ public function register(): array { return [ T_EQUAL, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $equalPointer */ public function process(File $phpcsFile, $equalPointer): void { $tokens = $phpcsFile->getTokens(); /** @var int $variableStartPointer */ $variableStartPointer = TokenHelper::findNextEffective($phpcsFile, $equalPointer + 1); $variableEndPointer = IdentificatorHelper::findEndPointer($phpcsFile, $variableStartPointer); if ($variableEndPointer === null) { return; } $operatorPointer = TokenHelper::findNextEffective($phpcsFile, $variableEndPointer + 1); $operators = [ T_BITWISE_AND => '&=', T_BITWISE_OR => '|=', T_STRING_CONCAT => '.=', T_DIVIDE => '/=', T_MINUS => '-=', T_POW => '**=', T_MODULUS => '%=', T_MULTIPLY => '*=', T_PLUS => '+=', T_SL => '<<=', T_SR => '>>=', T_BITWISE_XOR => '^=', ]; if (!array_key_exists($tokens[$operatorPointer]['code'], $operators)) { return; } $isFixable = true; if ($tokens[$variableEndPointer]['code'] === T_CLOSE_SQUARE_BRACKET) { $pointerAfterOperator = TokenHelper::findNextEffective($phpcsFile, $operatorPointer + 1); if (in_array( $tokens[$pointerAfterOperator]['code'], [T_CONSTANT_ENCAPSED_STRING, T_DOUBLE_QUOTED_STRING, T_START_HEREDOC, T_START_NOWDOC], true, )) { return; } $isFixable = in_array($tokens[$pointerAfterOperator]['code'], [T_LNUMBER, T_DNUMBER], true); } $variableContent = IdentificatorHelper::getContent($phpcsFile, $variableStartPointer, $variableEndPointer); /** @var int $beforeEqualEndPointer */ $beforeEqualEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $equalPointer - 1); $beforeEqualStartPointer = IdentificatorHelper::findStartPointer($phpcsFile, $beforeEqualEndPointer); if ($beforeEqualStartPointer === null) { return; } $beforeEqualVariableContent = IdentificatorHelper::getContent($phpcsFile, $beforeEqualStartPointer, $beforeEqualEndPointer); if ($beforeEqualVariableContent !== $variableContent) { return; } $semicolonPointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $equalPointer + 1); if (TokenHelper::findNext($phpcsFile, Tokens::$operators, $operatorPointer + 1, $semicolonPointer) !== null) { return; } $errorMessage = sprintf( 'Use "%s" operator instead of "=" and "%s".', $operators[$tokens[$operatorPointer]['code']], $tokens[$operatorPointer]['content'], ); if (!$isFixable) { $phpcsFile->addError($errorMessage, $equalPointer, self::CODE_REQUIRED_COMBINED_ASSIGNMENT_OPERATOR); return; } $fix = $phpcsFile->addFixableError($errorMessage, $equalPointer, self::CODE_REQUIRED_COMBINED_ASSIGNMENT_OPERATOR); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $equalPointer, $operatorPointer, $operators[$tokens[$operatorPointer]['code']]); $phpcsFile->fixer->endChangeset(); } } PK41] 1  Vcoding-standard/SlevomatCodingStandard/Sniffs/Operators/SpreadOperatorSpacingSniff.phpnu[ */ public function register(): array { return [ T_ELLIPSIS, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $spreadOperatorPointer */ public function process(File $phpcsFile, $spreadOperatorPointer): void { $this->spacesCountAfterOperator = SniffSettingsHelper::normalizeInteger($this->spacesCountAfterOperator); $pointerAfterWhitespace = TokenHelper::findNextNonWhitespace($phpcsFile, $spreadOperatorPointer + 1); $whitespace = TokenHelper::getContent($phpcsFile, $spreadOperatorPointer + 1, $pointerAfterWhitespace - 1); if ($this->spacesCountAfterOperator === strlen($whitespace)) { return; } $errorMessage = $this->spacesCountAfterOperator === 0 ? 'There must be no whitespace after spread operator.' : sprintf( 'There must be exactly %d whitespace%s after spread operator.', $this->spacesCountAfterOperator, $this->spacesCountAfterOperator !== 1 ? 's' : '', ); $fix = $phpcsFile->addFixableError($errorMessage, $spreadOperatorPointer, self::CODE_INCORRECT_SPACES_AFTER_OPERATOR); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::add( $phpcsFile, $spreadOperatorPointer, str_repeat(' ', $this->spacesCountAfterOperator), ); FixerHelper::removeBetween($phpcsFile, $spreadOperatorPointer, $pointerAfterWhitespace); $phpcsFile->fixer->endChangeset(); } } PK41]V8: : Xcoding-standard/SlevomatCodingStandard/Sniffs/Operators/NegationOperatorSpacingSniff.phpnu[ */ public function register(): array { return [T_MINUS]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { $this->spacesCount = SniffSettingsHelper::normalizeInteger($this->spacesCount); $tokens = $phpcsFile->getTokens(); $previousEffective = TokenHelper::findPreviousEffective($phpcsFile, $pointer - 1); $possibleOperandTypes = [ ...TokenHelper::ONLY_NAME_TOKEN_CODES, T_CONSTANT_ENCAPSED_STRING, T_CLASS_C, T_CLOSE_PARENTHESIS, T_CLOSE_SHORT_ARRAY, T_CLOSE_SQUARE_BRACKET, T_DIR, T_DNUMBER, T_ENCAPSED_AND_WHITESPACE, T_FILE, T_FUNC_C, T_LINE, T_LNUMBER, T_METHOD_C, T_NS_C, T_NUM_STRING, T_TRAIT_C, T_VARIABLE, ]; if (in_array($tokens[$previousEffective]['code'], $possibleOperandTypes, true)) { return; } $possibleVariableStartPointer = IdentificatorHelper::findStartPointer($phpcsFile, $previousEffective); if ($possibleVariableStartPointer !== null) { return; } $whitespacePointer = $pointer + 1; $numberOfSpaces = $tokens[$whitespacePointer]['code'] !== T_WHITESPACE ? 0 : strlen($tokens[$whitespacePointer]['content']); if ($numberOfSpaces === $this->spacesCount) { return; } $fix = $phpcsFile->addFixableError( sprintf( 'Expected exactly %d space after "%s", %d found.', $this->spacesCount, $tokens[$pointer]['content'], $numberOfSpaces, ), $pointer, self::CODE_INVALID_SPACE_AFTER_MINUS, ); if (!$fix) { return; } if ($this->spacesCount > $numberOfSpaces) { FixerHelper::add($phpcsFile, $pointer, ' '); return; } FixerHelper::replace($phpcsFile, $whitespacePointer, ''); } } PK41]o2  gcoding-standard/SlevomatCodingStandard/Sniffs/Operators/DisallowIncrementAndDecrementOperatorsSniff.phpnu[ */ public function register(): array { return [ T_DEC, T_INC, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $operatorPointer */ public function process(File $phpcsFile, $operatorPointer): void { $tokens = $phpcsFile->getTokens(); /** @var int $nextPointer */ $nextPointer = TokenHelper::findNextEffective($phpcsFile, $operatorPointer + 1); $afterVariableEndPointer = IdentificatorHelper::findEndPointer($phpcsFile, $nextPointer); $isPostOperator = $afterVariableEndPointer === null; if ($tokens[$operatorPointer]['code'] === T_INC) { if ($isPostOperator) { $code = self::CODE_DISALLOWED_POST_INCREMENT_OPERATOR; $message = 'Use of post-increment operator is disallowed.'; } else { $code = self::CODE_DISALLOWED_PRE_INCREMENT_OPERATOR; $message = 'Use of pre-increment operator is disallowed.'; } } else { if ($isPostOperator) { $code = self::CODE_DISALLOWED_POST_DECREMENT_OPERATOR; $message = 'Use of post-decrement operator is disallowed.'; } else { $code = self::CODE_DISALLOWED_PRE_DECREMENT_OPERATOR; $message = 'Use of pre-decrement operator is disallowed.'; } } $phpcsFile->addError($message, $operatorPointer, $code); } } PK41]#RggRcoding-standard/SlevomatCodingStandard/Sniffs/Whitespaces/DuplicateSpacesSniff.phpnu[ */ public function register(): array { return [ T_WHITESPACE, T_DOC_COMMENT_WHITESPACE, T_DOC_COMMENT_STRING, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $whitespacePointer */ public function process(File $phpcsFile, $whitespacePointer): void { $tokens = $phpcsFile->getTokens(); if ($tokens[$whitespacePointer]['column'] === 1) { return; } $content = $tokens[$whitespacePointer]['content']; if ($content === $phpcsFile->eolChar) { return; } if ($tokens[$whitespacePointer]['code'] === T_WHITESPACE) { if ($this->ignoreSpacesBeforeAssignment) { $pointerAfter = TokenHelper::findNextNonWhitespace($phpcsFile, $whitespacePointer + 1); if ( $pointerAfter !== null && in_array($tokens[$pointerAfter]['code'], Tokens::$assignmentTokens, true) ) { return; } } if ($this->ignoreSpacesInParameters) { $pointerAfter = TokenHelper::findNextNonWhitespace($phpcsFile, $whitespacePointer + 1); if ( $pointerAfter !== null && $tokens[$pointerAfter]['code'] === T_VARIABLE && ParameterHelper::isParameter($phpcsFile, $pointerAfter) ) { return; } } if ($this->ignoreSpacesInMatch) { $pointerAfter = TokenHelper::findNextNonWhitespace($phpcsFile, $whitespacePointer + 1); if ( $pointerAfter !== null && $tokens[$pointerAfter]['code'] === T_MATCH_ARROW ) { return; } } } else { if ($this->ignoreSpacesInComment) { return; } if ( $tokens[$whitespacePointer - 1]['code'] === T_DOC_COMMENT_STAR && $tokens[$whitespacePointer + 1]['code'] === T_DOC_COMMENT_STRING ) { return; } if ($this->ignoreSpacesInAnnotation) { $pointerBefore = TokenHelper::findPrevious($phpcsFile, [T_DOC_COMMENT_OPEN_TAG, T_DOC_COMMENT_TAG], $whitespacePointer - 1); if ( $pointerBefore !== null && $tokens[$pointerBefore]['code'] === T_DOC_COMMENT_TAG && $tokens[$whitespacePointer + 1]['code'] !== T_DOC_COMMENT_CLOSE_TAG ) { return; } } } $matchResult = preg_match_all('~ {2,}~', $content, $matches, PREG_OFFSET_CAPTURE); if ($matchResult === false || $matchResult === 0) { return; } $fix = false; foreach ($matches[0] as [$match, $offset]) { $position = $tokens[$whitespacePointer]['column'] + $offset; $fixable = $phpcsFile->addFixableError( sprintf('Duplicate spaces at position %d.', $position), $whitespacePointer, self::CODE_DUPLICATE_SPACES, ); if ($fixable) { $fix = true; } } if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $whitespacePointer, preg_replace('~ {2,}~', ' ', $content)); $phpcsFile->fixer->endChangeset(); } } PK41] RgRcoding-standard/SlevomatCodingStandard/Sniffs/Files/FilepathNamespaceExtractor.phpnu[ */ private array $rootNamespaces; /** @var array dir(string) => true(bool) */ private array $skipDirs; /** @var list */ private array $extensions; /** * @param array $rootNamespaces directory(string) => namespace * @param list $skipDirs * @param list $extensions index(integer) => extension */ public function __construct(array $rootNamespaces, array $skipDirs, array $extensions) { $this->rootNamespaces = $rootNamespaces; $this->skipDirs = array_fill_keys($skipDirs, true); $this->extensions = array_map(static fn (string $extension): string => strtolower($extension), $extensions); } public function getTypeNameFromProjectPath(string $path): ?string { $extension = strtolower(pathinfo($path, PATHINFO_EXTENSION)); if (!in_array($extension, $this->extensions, true)) { return null; } /** @var list $pathParts */ $pathParts = preg_split('~[/\\\]~', $path); $rootNamespace = null; while (count($pathParts) > 0) { array_shift($pathParts); foreach ($this->rootNamespaces as $directory => $namespace) { if (!StringHelper::startsWith(implode('/', $pathParts) . '/', $directory . '/')) { continue; } $directoryPartsCount = count(explode('/', $directory)); for ($i = 0; $i < $directoryPartsCount; $i++) { array_shift($pathParts); } $rootNamespace = $namespace; break 2; } } if ($rootNamespace === null) { return null; } array_unshift($pathParts, $rootNamespace); $typeName = implode('\\', array_filter($pathParts, fn (string $pathPart): bool => !isset($this->skipDirs[$pathPart]))); return substr($typeName, 0, -strlen('.' . $extension)); } } PK41] ::Gcoding-standard/SlevomatCodingStandard/Sniffs/Files/FileLengthSniff.phpnu[ */ public function register(): array { return [T_OPEN_TAG]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { $this->maxLinesLength = SniffSettingsHelper::normalizeInteger($this->maxLinesLength); $flags = array_keys(array_filter([ FunctionHelper::LINE_INCLUDE_COMMENT => $this->includeComments, FunctionHelper::LINE_INCLUDE_WHITESPACE => $this->includeWhitespace, ])); $flags = array_reduce($flags, static fn ($carry, $flag): int => $carry | $flag, 0); $length = FunctionHelper::getLineCount($phpcsFile, $pointer, $flags); if ($length <= $this->maxLinesLength) { return; } $errorMessage = sprintf('Your file is too long. Currently using %d lines. Can be up to %d lines.', $length, $this->maxLinesLength); $phpcsFile->addError($errorMessage, $pointer, self::CODE_FILE_TOO_LONG); } } PK41]pYTcoding-standard/SlevomatCodingStandard/Sniffs/Files/TypeNameMatchesFileNameSniff.phpnu[ */ public array $rootNamespaces = []; /** @var list */ public array $skipDirs = []; /** @var list */ public array $ignoredNamespaces = []; /** @var list */ public array $extensions = ['php']; /** @var array|null */ private ?array $normalizedRootNamespaces = null; /** @var list|null */ private ?array $normalizedSkipDirs = null; /** @var list|null */ private ?array $normalizedIgnoredNamespaces = null; /** @var list|null */ private ?array $normalizedExtensions = null; private ?FilepathNamespaceExtractor $namespaceExtractor = null; /** * @return array */ public function register(): array { return TokenHelper::CLASS_TYPE_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $typePointer */ public function process(File $phpcsFile, $typePointer): void { $tokens = $phpcsFile->getTokens(); /** @var int $namePointer */ $namePointer = TokenHelper::findNext($phpcsFile, T_STRING, $typePointer + 1); $typeName = NamespaceHelper::normalizeToCanonicalName(ClassHelper::getFullyQualifiedName($phpcsFile, $typePointer)); foreach ($this->getIgnoredNamespaces() as $ignoredNamespace) { if (!StringHelper::startsWith($typeName, $ignoredNamespace . '\\')) { continue; } return; } $filename = str_replace('/', DIRECTORY_SEPARATOR, $phpcsFile->getFilename()); $basePath = str_replace('/', DIRECTORY_SEPARATOR, $phpcsFile->config->basepath ?? ''); if ($basePath !== '' && StringHelper::startsWith($filename, $basePath)) { $filename = substr($filename, strlen($basePath)); } $expectedTypeName = $this->getNamespaceExtractor()->getTypeNameFromProjectPath($filename); if ($typeName === $expectedTypeName) { return; } $phpcsFile->addError( sprintf( '%s name %s does not match filepath %s.', ucfirst($tokens[$typePointer]['content']), $typeName, $phpcsFile->getFilename(), ), $namePointer, self::CODE_NO_MATCH_BETWEEN_TYPE_NAME_AND_FILE_NAME, ); } /** * @return array path(string) => namespace */ private function getRootNamespaces(): array { if ($this->normalizedRootNamespaces === null) { /** @var array $normalizedRootNamespaces */ $normalizedRootNamespaces = SniffSettingsHelper::normalizeAssociativeArray($this->rootNamespaces); $this->normalizedRootNamespaces = $normalizedRootNamespaces; uksort($this->normalizedRootNamespaces, static function (string $a, string $b): int { $aParts = explode('/', str_replace('\\', '/', $a)); $bParts = explode('/', str_replace('\\', '/', $b)); $minPartsCount = min(count($aParts), count($bParts)); for ($i = 0; $i < $minPartsCount; $i++) { $comparison = strcasecmp($bParts[$i], $aParts[$i]); if ($comparison === 0) { continue; } return $comparison; } return count($bParts) <=> count($aParts); }); } return $this->normalizedRootNamespaces; } /** * @return list */ private function getSkipDirs(): array { $this->normalizedSkipDirs ??= SniffSettingsHelper::normalizeArray($this->skipDirs); return $this->normalizedSkipDirs; } /** * @return list */ private function getIgnoredNamespaces(): array { $this->normalizedIgnoredNamespaces ??= SniffSettingsHelper::normalizeArray($this->ignoredNamespaces); return $this->normalizedIgnoredNamespaces; } /** * @return list */ private function getExtensions(): array { $this->normalizedExtensions ??= SniffSettingsHelper::normalizeArray($this->extensions); return $this->normalizedExtensions; } private function getNamespaceExtractor(): FilepathNamespaceExtractor { $this->namespaceExtractor ??= new FilepathNamespaceExtractor( $this->getRootNamespaces(), $this->getSkipDirs(), $this->getExtensions(), ); return $this->namespaceExtractor; } } PK41]= Gcoding-standard/SlevomatCodingStandard/Sniffs/Files/LineLengthSniff.phpnu[ */ public function register(): array { return [T_OPEN_TAG]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter * @param int $pointer */ public function process(File $phpcsFile, $pointer): int { $tokens = $phpcsFile->getTokens(); for ($i = 0; $i < $phpcsFile->numTokens; $i++) { if ($tokens[$i]['column'] !== 1) { continue; } $this->checkLineLength($phpcsFile, $i); } return $phpcsFile->numTokens + 1; } private function checkLineLength(File $phpcsFile, int $pointer): void { $tokens = $phpcsFile->getTokens(); if ($tokens[$pointer]['column'] === 1 && $tokens[$pointer]['length'] === 0) { // Blank line. return; } $line = $tokens[$pointer]['line']; $nextLineStartPtr = $pointer; while (isset($tokens[$nextLineStartPtr]) && $line === $tokens[$nextLineStartPtr]['line']) { $pointer = $nextLineStartPtr; $nextLineStartPtr++; } if ($tokens[$pointer]['content'] === $phpcsFile->eolChar) { $pointer--; } $lineLength = $tokens[$pointer]['column'] + $tokens[$pointer]['length'] - 1; if ($lineLength <= $this->lineLengthLimit) { return; } if (in_array($tokens[$pointer]['code'], [T_COMMENT, T_DOC_COMMENT_STRING], true)) { if ($this->ignoreComments === true) { return; } // If this is a long comment, check if it can be broken up onto multiple lines. // Some comments contain unbreakable strings like URLs and so it makes sense // to ignore the line length in these cases if the URL would be longer than the max // line length once you indent it to the correct level. if ($lineLength > $this->lineLengthLimit) { $oldLength = strlen($tokens[$pointer]['content']); $newLength = strlen(ltrim($tokens[$pointer]['content'], "/#\t ")); $indent = $tokens[$pointer]['column'] - 1 + $oldLength - $newLength; $nonBreakingLength = $tokens[$pointer]['length']; $space = strrpos($tokens[$pointer]['content'], ' '); if ($space !== false) { $nonBreakingLength -= $space + 1; } if ($nonBreakingLength + $indent > $this->lineLengthLimit) { return; } } } if ($this->ignoreImports) { $usePointer = UseStatementHelper::getUseStatementPointer($phpcsFile, $pointer - 1); if ( is_int($usePointer) && $tokens[$usePointer]['line'] === $tokens[$pointer]['line'] && UseStatementHelper::isImportUse($phpcsFile, $usePointer) ) { return; } } $error = sprintf('Line exceeds maximum limit of %s characters, contains %s characters.', $this->lineLengthLimit, $lineLength); $phpcsFile->addError($error, $pointer, self::CODE_LINE_TOO_LONG); } } PK41]JL??bcoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/AbstractFullyQualifiedGlobalReference.phpnu[ */ public array $exclude = []; /** @var list */ public array $include = []; /** @var list|null */ private ?array $normalizedExclude = null; /** @var list|null */ private ?array $normalizedInclude = null; abstract protected function getNotFullyQualifiedMessage(): string; abstract protected function isCaseSensitive(): bool; abstract protected function isValidType(ReferencedName $name): bool; /** * @return array */ public function register(): array { return [ T_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $openTagPointer */ public function process(File $phpcsFile, $openTagPointer): void { if (TokenHelper::findPrevious($phpcsFile, T_OPEN_TAG, $openTagPointer - 1) !== null) { return; } if (TokenHelper::findNext($phpcsFile, T_OPEN_USE_GROUP, $openTagPointer) !== null) { return; } $tokens = $phpcsFile->getTokens(); $namespacePointers = NamespaceHelper::getAllNamespacesPointers($phpcsFile); $referencedNames = ReferencedNameHelper::getAllReferencedNames($phpcsFile, $openTagPointer); $include = array_flip($this->getNormalizedInclude()); $exclude = array_flip($this->getNormalizedExclude()); foreach ($referencedNames as $referencedName) { $name = $referencedName->getNameAsReferencedInFile(); $namePointer = $referencedName->getStartPointer(); if (!$this->isValidType($referencedName)) { continue; } if (NamespaceHelper::isFullyQualifiedName($name)) { continue; } if (NamespaceHelper::hasNamespace($name)) { continue; } if ($namespacePointers === []) { continue; } $canonicalName = $this->isCaseSensitive() ? $name : strtolower($name); $useStatements = UseStatementHelper::getUseStatementsForPointer($phpcsFile, $namePointer); if (array_key_exists(UseStatement::getUniqueId($referencedName->getType(), $canonicalName), $useStatements)) { $fullyQualifiedName = NamespaceHelper::resolveName($phpcsFile, $name, $referencedName->getType(), $namePointer); if (NamespaceHelper::hasNamespace($fullyQualifiedName)) { continue; } } if ($include !== [] && !array_key_exists($canonicalName, $include)) { continue; } if (array_key_exists($canonicalName, $exclude)) { continue; } $fix = $phpcsFile->addFixableError( sprintf($this->getNotFullyQualifiedMessage(), $tokens[$namePointer]['content']), $namePointer, self::CODE_NON_FULLY_QUALIFIED, ); if (!$fix) { continue; } $phpcsFile->fixer->beginChangeset(); FixerHelper::addBefore($phpcsFile, $namePointer, NamespaceHelper::NAMESPACE_SEPARATOR); $phpcsFile->fixer->endChangeset(); } } /** * @return list */ protected function getNormalizedInclude(): array { $this->normalizedInclude ??= $this->normalizeNames($this->include); return $this->normalizedInclude; } /** * @return list */ private function getNormalizedExclude(): array { $this->normalizedExclude ??= $this->normalizeNames($this->exclude); return $this->normalizedExclude; } /** * @param list $names * @return list */ private function normalizeNames(array $names): array { $names = SniffSettingsHelper::normalizeArray($names); if (!$this->isCaseSensitive()) { $names = array_map(static fn (string $name): string => strtolower($name), $names); } return $names; } } PK41]wJB+B+Lcoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/UseSpacingSniff.phpnu[ */ public function register(): array { return [ T_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $openTagPointer */ public function process(File $phpcsFile, $openTagPointer): void { $this->linesCountBeforeFirstUse = SniffSettingsHelper::normalizeInteger($this->linesCountBeforeFirstUse); $this->linesCountBetweenUseTypes = SniffSettingsHelper::normalizeInteger($this->linesCountBetweenUseTypes); $this->linesCountAfterLastUse = SniffSettingsHelper::normalizeInteger($this->linesCountAfterLastUse); if (TokenHelper::findPrevious($phpcsFile, T_OPEN_TAG, $openTagPointer - 1) !== null) { return; } $fileUseStatements = UseStatementHelper::getFileUseStatements($phpcsFile); if (count($fileUseStatements) === 0) { return; } foreach ($fileUseStatements as $useStatementsByName) { $useStatements = array_values($useStatementsByName); $this->checkLinesBeforeFirstUse($phpcsFile, $useStatements[0]); $this->checkLinesAfterLastUse($phpcsFile, $useStatements[count($useStatements) - 1]); $this->checkLinesBetweenSameTypesOfUse($phpcsFile, $useStatements); $this->checkLinesBetweenDifferentTypesOfUse($phpcsFile, $useStatements); } } private function checkLinesBeforeFirstUse(File $phpcsFile, UseStatement $firstUse): void { $tokens = $phpcsFile->getTokens(); /** @var int $pointerBeforeFirstUse */ $pointerBeforeFirstUse = TokenHelper::findPreviousNonWhitespace($phpcsFile, $firstUse->getPointer() - 1); $useStartPointer = $firstUse->getPointer(); if ( in_array($tokens[$pointerBeforeFirstUse]['code'], Tokens::$commentTokens, true) && $tokens[$pointerBeforeFirstUse]['line'] + 1 === $tokens[$useStartPointer]['line'] ) { $useStartPointer = array_key_exists('comment_opener', $tokens[$pointerBeforeFirstUse]) ? $tokens[$pointerBeforeFirstUse]['comment_opener'] : CommentHelper::getMultilineCommentStartPointer($phpcsFile, $pointerBeforeFirstUse); /** @var int $pointerBeforeFirstUse */ $pointerBeforeFirstUse = TokenHelper::findPreviousNonWhitespace($phpcsFile, $useStartPointer - 1); } $actualLinesCountBeforeFirstUse = $tokens[$useStartPointer]['line'] - $tokens[$pointerBeforeFirstUse]['line'] - 1; if ($actualLinesCountBeforeFirstUse === $this->linesCountBeforeFirstUse) { return; } $fix = $phpcsFile->addFixableError( sprintf( 'Expected %d line%s before first use statement, found %d.', $this->linesCountBeforeFirstUse, $this->linesCountBeforeFirstUse === 1 ? '' : 's', $actualLinesCountBeforeFirstUse, ), $firstUse->getPointer(), self::CODE_INCORRECT_LINES_COUNT_BEFORE_FIRST_USE, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); if ($tokens[$pointerBeforeFirstUse]['code'] === T_OPEN_TAG) { FixerHelper::replace($phpcsFile, $pointerBeforeFirstUse, 'linesCountBeforeFirstUse; $i++) { $phpcsFile->fixer->addNewline($pointerBeforeFirstUse); } $phpcsFile->fixer->endChangeset(); } private function checkLinesAfterLastUse(File $phpcsFile, UseStatement $lastUse): void { $tokens = $phpcsFile->getTokens(); /** @var int $useEndPointer */ $useEndPointer = TokenHelper::findNextLocal($phpcsFile, T_SEMICOLON, $lastUse->getPointer() + 1); $pointerAfterWhitespaceEnd = TokenHelper::findNextNonWhitespace($phpcsFile, $useEndPointer + 1); if ($pointerAfterWhitespaceEnd === null) { return; } if ( in_array($tokens[$pointerAfterWhitespaceEnd]['code'], Tokens::$commentTokens, true) && $tokens[$pointerAfterWhitespaceEnd]['code'] !== T_DOC_COMMENT_OPEN_TAG && ( $tokens[$useEndPointer]['line'] === $tokens[$pointerAfterWhitespaceEnd]['line'] || $tokens[$useEndPointer]['line'] + 1 === $tokens[$pointerAfterWhitespaceEnd]['line'] ) ) { $useEndPointer = CommentHelper::getMultilineCommentEndPointer($phpcsFile, $pointerAfterWhitespaceEnd); /** @var int $pointerAfterWhitespaceEnd */ $pointerAfterWhitespaceEnd = TokenHelper::findNextNonWhitespace($phpcsFile, $useEndPointer + 1); } $actualLinesCountAfterLastUse = $tokens[$pointerAfterWhitespaceEnd]['line'] - $tokens[$useEndPointer]['line'] - 1; if ($actualLinesCountAfterLastUse === $this->linesCountAfterLastUse) { return; } $fix = $phpcsFile->addFixableError( sprintf( 'Expected %d line%s after last use statement, found %d.', $this->linesCountAfterLastUse, $this->linesCountAfterLastUse === 1 ? '' : 's', $actualLinesCountAfterLastUse, ), $lastUse->getPointer(), self::CODE_INCORRECT_LINES_COUNT_AFTER_LAST_USE, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetween($phpcsFile, $useEndPointer, $pointerAfterWhitespaceEnd); $linesToAdd = $this->linesCountAfterLastUse; if (CommentHelper::isLineComment($phpcsFile, $useEndPointer)) { $linesToAdd--; } for ($i = 0; $i <= $linesToAdd; $i++) { $phpcsFile->fixer->addNewline($useEndPointer); } $phpcsFile->fixer->endChangeset(); } /** * @param list $useStatements */ private function checkLinesBetweenSameTypesOfUse(File $phpcsFile, array $useStatements): void { if (count($useStatements) === 1) { return; } $tokens = $phpcsFile->getTokens(); $requiredLinesCountBetweenUses = 0; $previousUse = null; foreach ($useStatements as $use) { if ($previousUse === null) { $previousUse = $use; continue; } if (!$use->hasSameType($previousUse)) { $previousUse = null; continue; } /** @var int $pointerBeforeUse */ $pointerBeforeUse = TokenHelper::findPreviousNonWhitespace($phpcsFile, $use->getPointer() - 1); $useStartPointer = $use->getPointer(); if ( in_array($tokens[$pointerBeforeUse]['code'], Tokens::$commentTokens, true) && TokenHelper::findFirstNonWhitespaceOnLine($phpcsFile, $pointerBeforeUse) === $pointerBeforeUse && $tokens[$pointerBeforeUse]['line'] + 1 === $tokens[$useStartPointer]['line'] ) { $useStartPointer = array_key_exists('comment_opener', $tokens[$pointerBeforeUse]) ? $tokens[$pointerBeforeUse]['comment_opener'] : CommentHelper::getMultilineCommentStartPointer($phpcsFile, $pointerBeforeUse); } $actualLinesCountAfterPreviousUse = $tokens[$useStartPointer]['line'] - $tokens[$previousUse->getPointer()]['line'] - 1; if ($actualLinesCountAfterPreviousUse === $requiredLinesCountBetweenUses) { $previousUse = $use; continue; } $fix = $phpcsFile->addFixableError( sprintf( 'Expected 0 lines between same types of use statement, found %d.', $actualLinesCountAfterPreviousUse, ), $use->getPointer(), self::CODE_INCORRECT_LINES_COUNT_BETWEEN_SAME_TYPES_OF_USE, ); if (!$fix) { $previousUse = $use; continue; } /** @var int $previousUseSemicolonPointer */ $previousUseSemicolonPointer = TokenHelper::findNextLocal($phpcsFile, T_SEMICOLON, $previousUse->getPointer() + 1); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetween($phpcsFile, $previousUseSemicolonPointer, $useStartPointer); $phpcsFile->fixer->addNewline($previousUseSemicolonPointer); $phpcsFile->fixer->endChangeset(); $previousUse = $use; } } /** * @param list $useStatements */ private function checkLinesBetweenDifferentTypesOfUse(File $phpcsFile, array $useStatements): void { if (count($useStatements) === 1) { return; } $tokens = $phpcsFile->getTokens(); $previousUse = null; foreach ($useStatements as $use) { if ($previousUse === null) { $previousUse = $use; continue; } if ($use->hasSameType($previousUse)) { $previousUse = $use; continue; } /** @var int $pointerBeforeUse */ $pointerBeforeUse = TokenHelper::findPreviousNonWhitespace($phpcsFile, $use->getPointer() - 1); $useStartPointer = $use->getPointer(); if ( in_array($tokens[$pointerBeforeUse]['code'], Tokens::$commentTokens, true) && TokenHelper::findFirstNonWhitespaceOnLine($phpcsFile, $pointerBeforeUse) === $pointerBeforeUse && $tokens[$pointerBeforeUse]['line'] + 1 === $tokens[$useStartPointer]['line'] ) { $useStartPointer = array_key_exists('comment_opener', $tokens[$pointerBeforeUse]) ? $tokens[$pointerBeforeUse]['comment_opener'] : CommentHelper::getMultilineCommentStartPointer($phpcsFile, $pointerBeforeUse); } $actualLinesCountAfterPreviousUse = $tokens[$useStartPointer]['line'] - $tokens[$previousUse->getPointer()]['line'] - 1; if ($actualLinesCountAfterPreviousUse === $this->linesCountBetweenUseTypes) { $previousUse = $use; continue; } $fix = $phpcsFile->addFixableError( sprintf( 'Expected %d line%s between different types of use statement, found %d.', $this->linesCountBetweenUseTypes, $this->linesCountBetweenUseTypes === 1 ? '' : 's', $actualLinesCountAfterPreviousUse, ), $use->getPointer(), self::CODE_INCORRECT_LINES_COUNT_BETWEEN_DIFFERENT_TYPES_OF_USE, ); if (!$fix) { $previousUse = $use; continue; } /** @var int $previousUseSemicolonPointer */ $previousUseSemicolonPointer = TokenHelper::findNextLocal($phpcsFile, T_SEMICOLON, $previousUse->getPointer() + 1); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetween($phpcsFile, $previousUseSemicolonPointer, $useStartPointer); for ($i = 0; $i <= $this->linesCountBetweenUseTypes; $i++) { $phpcsFile->fixer->addNewline($previousUseSemicolonPointer); } $phpcsFile->fixer->endChangeset(); $previousUse = $use; } } } PK41])*$$Lcoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/UnusedUsesSniff.phpnu[ */ public array $ignoredAnnotationNames = []; /** @var list */ public array $ignoredAnnotations = []; /** @var list|null */ private ?array $normalizedIgnoredAnnotationNames = null; /** @var list|null */ private ?array $normalizedIgnoredAnnotations = null; /** * @return array */ public function register(): array { return [ T_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $openTagPointer */ public function process(File $phpcsFile, $openTagPointer): void { if (TokenHelper::findPrevious($phpcsFile, T_OPEN_TAG, $openTagPointer - 1) !== null) { return; } $startPointer = TokenHelper::findPrevious($phpcsFile, T_NAMESPACE, $openTagPointer - 1) ?? $openTagPointer; $fileUnusedNames = UseStatementHelper::getFileUseStatements($phpcsFile); $referencedNamesInCode = ReferencedNameHelper::getAllReferencedNames($phpcsFile, $startPointer); $referencedNamesInAttributes = ReferencedNameHelper::getAllReferencedNamesInAttributes($phpcsFile, $startPointer); $pointersBeforeUseStatements = array_reverse(NamespaceHelper::getAllNamespacesPointers($phpcsFile)); $allUsedNames = []; foreach ([$referencedNamesInCode, $referencedNamesInAttributes] as $referencedNames) { foreach ($referencedNames as $referencedName) { $pointer = $referencedName->getStartPointer(); $pointerBeforeUseStatements = $this->firstPointerBefore($pointer, $pointersBeforeUseStatements, $startPointer); $name = $referencedName->getNameAsReferencedInFile(); $nameParts = NamespaceHelper::getNameParts($name); $nameAsReferencedInFile = $nameParts[0]; $nameReferencedWithoutSubNamespace = count($nameParts) === 1; $uniqueId = $nameReferencedWithoutSubNamespace ? UseStatement::getUniqueId($referencedName->getType(), $nameAsReferencedInFile) : UseStatement::getUniqueId(ReferencedName::TYPE_CLASS, $nameAsReferencedInFile); if ( NamespaceHelper::isFullyQualifiedName($name) || !array_key_exists($pointerBeforeUseStatements, $fileUnusedNames) || !array_key_exists($uniqueId, $fileUnusedNames[$pointerBeforeUseStatements]) ) { continue; } $allUsedNames[$pointerBeforeUseStatements][$uniqueId] = true; } } if ($this->searchAnnotations) { $tokens = $phpcsFile->getTokens(); $searchAnnotationsPointer = $startPointer + 1; while (true) { $docCommentOpenPointer = TokenHelper::findNext($phpcsFile, T_DOC_COMMENT_OPEN_TAG, $searchAnnotationsPointer); if ($docCommentOpenPointer === null) { break; } $annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenPointer); if ($annotations === []) { $searchAnnotationsPointer = $tokens[$docCommentOpenPointer]['comment_closer'] + 1; continue; } $pointerBeforeUseStatements = $this->firstPointerBefore( $docCommentOpenPointer - 1, $pointersBeforeUseStatements, $startPointer, ); if (!array_key_exists($pointerBeforeUseStatements, $fileUnusedNames)) { $searchAnnotationsPointer = $tokens[$docCommentOpenPointer]['comment_closer'] + 1; continue; } foreach ($fileUnusedNames[$pointerBeforeUseStatements] as $useStatement) { if (!$useStatement->isClass()) { continue; } $nameAsReferencedInFile = $useStatement->getNameAsReferencedInFile(); $uniqueId = UseStatement::getUniqueId($useStatement->getType(), $nameAsReferencedInFile); foreach ($annotations as $annotation) { if (in_array($annotation->getName(), $this->getIgnoredAnnotations(), true)) { continue; } if ($annotation->isInvalid()) { continue; } $contentsToCheck = []; if ($annotation->getValue() instanceof GenericTagValueNode) { $contentsToCheck[] = $annotation->getName(); $contentsToCheck[] = $annotation->getValue()->value; } else { $identifierTypeNodes = AnnotationHelper::getAnnotationNodesByType( $annotation->getNode(), IdentifierTypeNode::class, ); $doctrineAnnotations = AnnotationHelper::getAnnotationNodesByType( $annotation->getNode(), DoctrineAnnotation::class, ); $constFetchNodes = AnnotationHelper::getAnnotationNodesByType($annotation->getNode(), ConstFetchNode::class); $contentsToCheck = array_filter(array_merge( $contentsToCheck, array_map(static function (IdentifierTypeNode $identifierTypeNode): ?string { if ( TypeHintHelper::isSimpleTypeHint($identifierTypeNode->name) || TypeHintHelper::isSimpleUnofficialTypeHints($identifierTypeNode->name) || !TypeHelper::isTypeName($identifierTypeNode->name) ) { return null; } return $identifierTypeNode->name; }, $identifierTypeNodes), array_map(function (DoctrineAnnotation $doctrineAnnotation): ?string { if (in_array($doctrineAnnotation->name, $this->getIgnoredAnnotationNames(), true)) { return null; } return $doctrineAnnotation->name; }, $doctrineAnnotations), array_map( static fn (ConstFetchNode $constFetchNode): string => $constFetchNode->className, $constFetchNodes, ), ), static fn (?string $content): bool => $content !== null); } foreach ($contentsToCheck as $contentToCheck) { if (preg_match( '~(?<=^|[^a-z\\\\])(' . preg_quote($nameAsReferencedInFile, '~') . ')(?=\\s|::|\\\\|\||\[|$)~im', $contentToCheck, ) === 0) { continue; } $allUsedNames[$pointerBeforeUseStatements][$uniqueId] = true; } } } $searchAnnotationsPointer = $tokens[$docCommentOpenPointer]['comment_closer'] + 1; } } foreach ($fileUnusedNames as $pointerBeforeUnusedNames => $unusedNames) { $usedNames = $allUsedNames[$pointerBeforeUnusedNames] ?? []; foreach (array_diff_key($unusedNames, $usedNames) as $unusedUse) { $fullName = $unusedUse->getFullyQualifiedTypeName(); if ( $unusedUse->getNameAsReferencedInFile() !== $fullName && $unusedUse->getNameAsReferencedInFile() !== NamespaceHelper::getUnqualifiedNameFromFullyQualifiedName($fullName) ) { $fullName .= sprintf(' (as %s)', $unusedUse->getNameAsReferencedInFile()); } $fix = $phpcsFile->addFixableError(sprintf( 'Type %s is not used in this file.', $fullName, ), $unusedUse->getPointer(), self::CODE_UNUSED_USE); if (!$fix) { continue; } $endPointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $unusedUse->getPointer()) + 1; $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $unusedUse->getPointer(), $endPointer); $phpcsFile->fixer->endChangeset(); } } } /** * @return list */ private function getIgnoredAnnotationNames(): array { $this->normalizedIgnoredAnnotationNames ??= array_merge( SniffSettingsHelper::normalizeArray($this->ignoredAnnotationNames), [ '@param', '@throws', '@property', '@method', ], ); return $this->normalizedIgnoredAnnotationNames; } /** * @return list */ private function getIgnoredAnnotations(): array { $this->normalizedIgnoredAnnotations ??= SniffSettingsHelper::normalizeArray($this->ignoredAnnotations); return $this->normalizedIgnoredAnnotations; } /** * @param list $pointersBeforeUseStatements */ private function firstPointerBefore(int $pointer, array $pointersBeforeUseStatements, int $startPointer): int { foreach ($pointersBeforeUseStatements as $pointerBeforeUseStatements) { if ($pointerBeforeUseStatements < $pointer) { return $pointerBeforeUseStatements; } } return $startPointer; } } PK41]V22Zcoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/FullyQualifiedExceptionsSniff.phpnu[ */ public array $specialExceptionNames = []; /** @var list */ public array $ignoredNames = []; /** @var list|null */ private ?array $normalizedSpecialExceptionNames = null; /** @var list|null */ private ?array $normalizedIgnoredNames = null; /** * @return array */ public function register(): array { return [ T_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $openTagPointer */ public function process(File $phpcsFile, $openTagPointer): void { if (TokenHelper::findPrevious($phpcsFile, T_OPEN_TAG, $openTagPointer - 1) !== null) { return; } $namespacePointers = array_reverse(NamespaceHelper::getAllNamespacesPointers($phpcsFile)); $referencedNames = ReferencedNameHelper::getAllReferencedNames($phpcsFile, $openTagPointer); foreach ($referencedNames as $referencedName) { $pointer = $referencedName->getStartPointer(); $name = $referencedName->getNameAsReferencedInFile(); $uniqueId = UseStatement::getUniqueId($referencedName->getType(), $name); $useStatements = UseStatementHelper::getUseStatementsForPointer($phpcsFile, $pointer); if ( isset($useStatements[$uniqueId]) && $referencedName->hasSameUseStatementType($useStatements[$uniqueId]) ) { $useStatement = $useStatements[$uniqueId]; if ( in_array($useStatement->getFullyQualifiedTypeName(), $this->getIgnoredNames(), true) || ( !StringHelper::endsWith($useStatement->getFullyQualifiedTypeName(), 'Exception') && $useStatement->getFullyQualifiedTypeName() !== Throwable::class && (!StringHelper::endsWith($useStatement->getFullyQualifiedTypeName(), 'Error') || NamespaceHelper::hasNamespace( $useStatement->getFullyQualifiedTypeName(), )) && !in_array($useStatement->getFullyQualifiedTypeName(), $this->getSpecialExceptionNames(), true) ) ) { continue; } } else { $fileNamespacePointer = null; if ($namespacePointers !== []) { foreach ($namespacePointers as $namespacePointer) { if ($namespacePointer < $pointer) { $fileNamespacePointer = $namespacePointer; break; } } } $fileNamespace = $fileNamespacePointer !== null ? NamespaceHelper::getName($phpcsFile, $fileNamespacePointer) : null; $canonicalName = $name; if (!NamespaceHelper::isFullyQualifiedName($name) && $fileNamespace !== null) { $canonicalName = sprintf('%s%s%s', $fileNamespace, NamespaceHelper::NAMESPACE_SEPARATOR, $name); } if ( in_array($canonicalName, $this->getIgnoredNames(), true) || ( !StringHelper::endsWith($name, 'Exception') && $name !== Throwable::class && (!StringHelper::endsWith($canonicalName, 'Error') || NamespaceHelper::hasNamespace($canonicalName)) && !in_array($canonicalName, $this->getSpecialExceptionNames(), true) ) ) { continue; } } if (NamespaceHelper::isFullyQualifiedName($name)) { continue; } $fix = $phpcsFile->addFixableError(sprintf( 'Exception %s should be referenced via a fully qualified name.', $name, ), $pointer, self::CODE_NON_FULLY_QUALIFIED_EXCEPTION); if (!$fix) { continue; } $fullyQualifiedName = NamespaceHelper::resolveClassName($phpcsFile, $name, $pointer); $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $referencedName->getStartPointer(), $referencedName->getEndPointer(), $fullyQualifiedName); $phpcsFile->fixer->endChangeset(); } } /** * @return list */ private function getSpecialExceptionNames(): array { $this->normalizedSpecialExceptionNames ??= SniffSettingsHelper::normalizeArray($this->specialExceptionNames); return $this->normalizedSpecialExceptionNames; } /** * @return list */ private function getIgnoredNames(): array { $this->normalizedIgnoredNames ??= SniffSettingsHelper::normalizeArray($this->ignoredNames); return $this->normalizedIgnoredNames; } } PK41]Vcoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/UseFromSameNamespaceSniff.phpnu[ */ public function register(): array { return [ T_USE, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $usePointer */ public function process(File $phpcsFile, $usePointer): void { if (!UseStatementHelper::isImportUse($phpcsFile, $usePointer)) { return; } $namespaceName = NamespaceHelper::findCurrentNamespaceName($phpcsFile, $usePointer); $namespaceName ??= ''; $usedTypeName = UseStatementHelper::getFullyQualifiedTypeNameFromUse($phpcsFile, $usePointer); if (!StringHelper::startsWith($usedTypeName, $namespaceName)) { return; } $asPointer = $this->findAsPointer($phpcsFile, $usePointer); if ($asPointer !== null) { return; } $usedTypeNameRest = substr($usedTypeName, strlen($namespaceName)); if (!NamespaceHelper::isFullyQualifiedName($usedTypeNameRest) && $namespaceName !== '') { return; } if (NamespaceHelper::hasNamespace($usedTypeNameRest)) { return; } $fix = $phpcsFile->addFixableError(sprintf( 'Use %s is from the same namespace – that is prohibited.', $usedTypeName, ), $usePointer, self::CODE_USE_FROM_SAME_NAMESPACE); if (!$fix) { return; } $endPointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $usePointer) + 1; $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $usePointer, $endPointer); $phpcsFile->fixer->endChangeset(); } private function findAsPointer(File $phpcsFile, int $startPointer): ?int { return TokenHelper::findNextLocal($phpcsFile, T_AS, $startPointer); } } PK41]!MNcoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/UselessAliasSniff.phpnu[ */ public function register(): array { return [ T_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $openTagPointer */ public function process(File $phpcsFile, $openTagPointer): void { if (TokenHelper::findPrevious($phpcsFile, T_OPEN_TAG, $openTagPointer - 1) !== null) { return; } $fileUseStatements = UseStatementHelper::getFileUseStatements($phpcsFile); if (count($fileUseStatements) === 0) { return; } foreach ($fileUseStatements as $useStatements) { foreach ($useStatements as $useStatement) { if ($useStatement->getAlias() === null) { continue; } $unqualifiedName = NamespaceHelper::getUnqualifiedNameFromFullyQualifiedName($useStatement->getFullyQualifiedTypeName()); if ($unqualifiedName !== $useStatement->getAlias()) { continue; } $fix = $phpcsFile->addFixableError( sprintf('Useless alias "%s" for use of "%s".', $useStatement->getAlias(), $useStatement->getFullyQualifiedTypeName()), $useStatement->getPointer(), self::CODE_USELESS_ALIAS, ); if (!$fix) { continue; } $asPointer = TokenHelper::findNext($phpcsFile, T_AS, $useStatement->getPointer() + 1); $nameEndPointer = TokenHelper::findPrevious($phpcsFile, TokenHelper::ONLY_NAME_TOKEN_CODES, $asPointer - 1); $useSemicolonPointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $asPointer + 1); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetween($phpcsFile, $nameEndPointer, $useSemicolonPointer); $phpcsFile->fixer->endChangeset(); } } } } PK41]_coding-standard/SlevomatCodingStandard/Sniffs/Namespaces/FullyQualifiedGlobalFunctionsSniff.phpnu[ */ protected function getNormalizedInclude(): array { $include = parent::getNormalizedInclude(); if ($this->includeSpecialFunctions) { array_push($include, ...FunctionHelper::SPECIAL_FUNCTIONS); } return $include; } protected function getNotFullyQualifiedMessage(): string { return 'Function %s() should be referenced via a fully qualified name.'; } protected function isCaseSensitive(): bool { return false; } protected function isValidType(ReferencedName $name): bool { return $name->isFunction(); } } PK41]\T[coding-standard/SlevomatCodingStandard/Sniffs/Namespaces/RequireOneNamespaceInFileSniff.phpnu[ */ public function register(): array { return [ T_NAMESPACE, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $namespacePointer */ public function process(File $phpcsFile, $namespacePointer): void { $tokens = $phpcsFile->getTokens(); $pointerAfterNamespace = TokenHelper::findNextEffective($phpcsFile, $namespacePointer + 1); if ($tokens[$pointerAfterNamespace]['code'] === T_NS_SEPARATOR) { return; } $previousNamespacePointer = $namespacePointer; do { $previousNamespacePointer = TokenHelper::findPrevious($phpcsFile, T_NAMESPACE, $previousNamespacePointer - 1); if ($previousNamespacePointer === null) { return; } $pointerAfterPreviousNamespace = TokenHelper::findNextEffective($phpcsFile, $previousNamespacePointer + 1); if ($tokens[$pointerAfterPreviousNamespace]['code'] === T_NS_SEPARATOR) { continue; } break; } while (true); $phpcsFile->addError('Only one namespace in a file is allowed.', $namespacePointer, self::CODE_MORE_NAMESPACES_IN_FILE); } } PK41]4  Rcoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/DisallowGroupUseSniff.phpnu[ */ public function register(): array { return [ T_OPEN_USE_GROUP, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $usePointer */ public function process(File $phpcsFile, $usePointer): void { $phpcsFile->addError( 'Group use declaration is disallowed, use single use for every import.', $usePointer, self::CODE_DISALLOWED_GROUP_USE, ); } } PK41]=gZcoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/AlphabeticallySortedUsesSniff.phpnu[ */ public function register(): array { return [ T_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $openTagPointer */ public function process(File $phpcsFile, $openTagPointer): void { if (TokenHelper::findPrevious($phpcsFile, T_OPEN_TAG, $openTagPointer - 1) !== null) { return; } // If there are any 'use group' statements then we cannot sort and fix the file. $groupUsePointer = TokenHelper::findNext($phpcsFile, T_OPEN_USE_GROUP, $openTagPointer); if ($groupUsePointer !== null) { return; } $fileUseStatements = UseStatementHelper::getFileUseStatements($phpcsFile); foreach ($fileUseStatements as $useStatements) { $lastUse = null; foreach ($useStatements as $useStatement) { if ($lastUse === null) { $lastUse = $useStatement; } else { $order = $this->compareUseStatements($useStatement, $lastUse); if ($order < 0) { // The use statements are not ordered correctly. Go through all statements and if any are multi-part then // we report the problem but cannot fix it, because this would lose the secondary parts of the statement. $fixable = true; $tokens = $phpcsFile->getTokens(); foreach ($useStatements as $statement) { $nextBreaker = TokenHelper::findNext($phpcsFile, [T_SEMICOLON, T_COMMA], $statement->getPointer()); if ($tokens[$nextBreaker]['code'] === T_COMMA) { $fixable = false; break; } } $errorParameters = [ sprintf( 'Use statements should be sorted alphabetically. The first wrong one is %s.', $useStatement->getFullyQualifiedTypeName(), ), $useStatement->getPointer(), self::CODE_INCORRECT_ORDER, ]; if (!$fixable) { $phpcsFile->addError(...$errorParameters); return; } $fix = $phpcsFile->addFixableError(...$errorParameters); if ($fix) { $this->fixAlphabeticalOrder($phpcsFile, $useStatements); } return; } $lastUse = $useStatement; } } } } /** * @param array $useStatements */ private function fixAlphabeticalOrder(File $phpcsFile, array $useStatements): void { /** @var UseStatement $firstUseStatement */ $firstUseStatement = reset($useStatements); /** @var UseStatement $lastUseStatement */ $lastUseStatement = end($useStatements); $lastSemicolonPointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $lastUseStatement->getPointer()); $firstPointer = $firstUseStatement->getPointer(); $tokens = $phpcsFile->getTokens(); $commentsBefore = []; foreach ($useStatements as $useStatement) { $pointerBeforeUseStatement = TokenHelper::findPreviousNonWhitespace($phpcsFile, $useStatement->getPointer() - 1); if (!in_array($tokens[$pointerBeforeUseStatement]['code'], Tokens::$commentTokens, true)) { continue; } $commentAndWhitespace = TokenHelper::getContent($phpcsFile, $pointerBeforeUseStatement, $useStatement->getPointer() - 1); if (StringHelper::endsWith($commentAndWhitespace, $phpcsFile->eolChar . $phpcsFile->eolChar)) { continue; } $commentStartPointer = in_array($tokens[$pointerBeforeUseStatement]['code'], TokenHelper::INLINE_COMMENT_TOKEN_CODES, true) ? CommentHelper::getMultilineCommentStartPointer($phpcsFile, $pointerBeforeUseStatement) : $tokens[$pointerBeforeUseStatement]['comment_opener']; $commentsBefore[$useStatement->getPointer()] = TokenHelper::getContent( $phpcsFile, $commentStartPointer, $pointerBeforeUseStatement, ); if ($firstPointer === $useStatement->getPointer()) { $firstPointer = $commentStartPointer; } } uasort($useStatements, fn (UseStatement $a, UseStatement $b): int => $this->compareUseStatements($a, $b)); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $firstPointer, $lastSemicolonPointer); FixerHelper::add( $phpcsFile, $firstPointer, implode($phpcsFile->eolChar, array_map(static function (UseStatement $useStatement) use ($phpcsFile, $commentsBefore): string { $unqualifiedName = NamespaceHelper::getUnqualifiedNameFromFullyQualifiedName($useStatement->getFullyQualifiedTypeName()); $useTypeName = UseStatement::getTypeName($useStatement->getType()); $useTypeFormatted = $useTypeName !== null ? sprintf('%s ', $useTypeName) : ''; $commentBefore = ''; if (array_key_exists($useStatement->getPointer(), $commentsBefore)) { $commentBefore = $commentsBefore[$useStatement->getPointer()]; if (!StringHelper::endsWith($commentBefore, $phpcsFile->eolChar)) { $commentBefore .= $phpcsFile->eolChar; } } if ($unqualifiedName === $useStatement->getNameAsReferencedInFile()) { return sprintf('%suse %s%s;', $commentBefore, $useTypeFormatted, $useStatement->getFullyQualifiedTypeName()); } return sprintf( '%suse %s%s as %s;', $commentBefore, $useTypeFormatted, $useStatement->getFullyQualifiedTypeName(), $useStatement->getNameAsReferencedInFile(), ); }, $useStatements)), ); $phpcsFile->fixer->endChangeset(); } private function compareUseStatements(UseStatement $a, UseStatement $b): int { if (!$a->hasSameType($b)) { $order = [ UseStatement::TYPE_CLASS => 1, UseStatement::TYPE_FUNCTION => $this->psr12Compatible ? 2 : 3, UseStatement::TYPE_CONSTANT => $this->psr12Compatible ? 3 : 2, ]; return $order[$a->getType()] <=> $order[$b->getType()]; } $aNameParts = explode(NamespaceHelper::NAMESPACE_SEPARATOR, $a->getFullyQualifiedTypeName()); $bNameParts = explode(NamespaceHelper::NAMESPACE_SEPARATOR, $b->getFullyQualifiedTypeName()); $minPartsCount = min(count($aNameParts), count($bNameParts)); for ($i = 0; $i < $minPartsCount; $i++) { $comparison = $this->compare($aNameParts[$i], $bNameParts[$i]); if ($comparison === 0) { continue; } return $comparison; } return count($aNameParts) <=> count($bNameParts); } private function compare(string $a, string $b): int { if ($this->caseSensitive) { return strcmp($a, $b); } return strcasecmp($a, $b); } } PK41]H?2Rcoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/NamespaceSpacingSniff.phpnu[ */ public function register(): array { return [ T_NAMESPACE, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $namespacePointer */ public function process(File $phpcsFile, $namespacePointer): void { $this->linesCountBeforeNamespace = SniffSettingsHelper::normalizeInteger($this->linesCountBeforeNamespace); $this->linesCountAfterNamespace = SniffSettingsHelper::normalizeInteger($this->linesCountAfterNamespace); $this->checkLinesBeforeNamespace($phpcsFile, $namespacePointer); $this->checkLinesAfterNamespace($phpcsFile, $namespacePointer); } private function checkLinesBeforeNamespace(File $phpcsFile, int $namespacePointer): void { $tokens = $phpcsFile->getTokens(); /** @var int $pointerBeforeNamespace */ $pointerBeforeNamespace = TokenHelper::findPreviousNonWhitespace($phpcsFile, $namespacePointer - 1); $whitespaceBeforeNamespace = ''; $isInlineCommentBefore = (bool) preg_match('~^(?://|#)(.*)~', $tokens[$pointerBeforeNamespace]['content']); if ($tokens[$pointerBeforeNamespace]['code'] === T_OPEN_TAG) { $whitespaceBeforeNamespace .= substr($tokens[$pointerBeforeNamespace]['content'], strlen('eolChar; } if ($pointerBeforeNamespace + 1 !== $namespacePointer) { $whitespaceBeforeNamespace .= TokenHelper::getContent($phpcsFile, $pointerBeforeNamespace + 1, $namespacePointer - 1); } $actualLinesCountBeforeNamespace = substr_count($whitespaceBeforeNamespace, $phpcsFile->eolChar) - 1; if ($actualLinesCountBeforeNamespace === $this->linesCountBeforeNamespace) { return; } $fix = $phpcsFile->addFixableError( sprintf( 'Expected %d line%s before namespace statement, found %d.', $this->linesCountBeforeNamespace, $this->linesCountBeforeNamespace === 1 ? '' : 's', $actualLinesCountBeforeNamespace, ), $namespacePointer, self::CODE_INCORRECT_LINES_COUNT_BEFORE_NAMESPACE, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); if ($tokens[$pointerBeforeNamespace]['code'] === T_OPEN_TAG) { FixerHelper::replace($phpcsFile, $pointerBeforeNamespace, 'eolChar), ); } FixerHelper::removeBetween($phpcsFile, $pointerBeforeNamespace, $namespacePointer); for ($i = 0; $i <= $this->linesCountBeforeNamespace; $i++) { $phpcsFile->fixer->addNewline($pointerBeforeNamespace); } $phpcsFile->fixer->endChangeset(); } private function checkLinesAfterNamespace(File $phpcsFile, int $namespacePointer): void { if (array_key_exists('scope_opener', $phpcsFile->getTokens()[$namespacePointer])) { return; } /** @var int $namespaceSemicolonPointer */ $namespaceSemicolonPointer = TokenHelper::findNextLocal($phpcsFile, T_SEMICOLON, $namespacePointer + 1); $pointerAfterWhitespaceEnd = TokenHelper::findNextNonWhitespace($phpcsFile, $namespaceSemicolonPointer + 1); if ($pointerAfterWhitespaceEnd === null) { return; } $whitespaceAfterNamespace = TokenHelper::getContent($phpcsFile, $namespaceSemicolonPointer + 1, $pointerAfterWhitespaceEnd - 1); $actualLinesCountAfterNamespace = substr_count($whitespaceAfterNamespace, $phpcsFile->eolChar) - 1; if ($actualLinesCountAfterNamespace === $this->linesCountAfterNamespace) { return; } $fix = $phpcsFile->addFixableError( sprintf( 'Expected %d line%s after namespace statement, found %d.', $this->linesCountAfterNamespace, $this->linesCountAfterNamespace === 1 ? '' : 's', $actualLinesCountAfterNamespace, ), $namespacePointer, self::CODE_INCORRECT_LINES_COUNT_AFTER_NAMESPACE, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetween($phpcsFile, $namespaceSemicolonPointer, $pointerAfterWhitespaceEnd); for ($i = 0; $i <= $this->linesCountAfterNamespace; $i++) { $phpcsFile->fixer->addNewline($namespaceSemicolonPointer); } $phpcsFile->fixer->endChangeset(); } } PK41]6CKffXcoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/ReferenceUsedNamesOnlySniff.phpnu[ */ public array $specialExceptionNames = []; /** @var list */ public array $ignoredNames = []; public bool $allowPartialUses = true; /** * If empty, all namespaces are required to be used * * @var list */ public array $namespacesRequiredToUse = []; public bool $allowFullyQualifiedNameForCollidingClasses = false; public bool $allowFullyQualifiedNameForCollidingFunctions = false; public bool $allowFullyQualifiedNameForCollidingConstants = false; /** @var list|null */ private ?array $normalizedSpecialExceptionNames = null; /** @var list|null */ private ?array $normalizedIgnoredNames = null; /** @var list|null */ private ?array $normalizedNamespacesRequiredToUse = null; /** * @return array */ public function register(): array { return [ T_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $openTagPointer */ public function process(File $phpcsFile, $openTagPointer): void { if (TokenHelper::findPrevious($phpcsFile, T_OPEN_TAG, $openTagPointer - 1) !== null) { return; } $namespacePointers = NamespaceHelper::getAllNamespacesPointers($phpcsFile); if ($namespacePointers === [] && !$this->allowWhenNoNamespace) { return; } $tokens = $phpcsFile->getTokens(); $references = $this->getReferences($phpcsFile, $openTagPointer); $definedClassesIndex = []; foreach (ClassHelper::getAllNames($phpcsFile) as $definedClassPointer => $definedClassName) { $definedClassesIndex[strtolower($definedClassName)] = NamespaceHelper::resolveClassName( $phpcsFile, $definedClassName, $definedClassPointer, ); } $definedFunctionsIndex = array_flip( array_map( static fn (string $functionName): string => strtolower($functionName), FunctionHelper::getAllFunctionNames($phpcsFile), ), ); $definedConstantsIndex = array_flip(ConstantHelper::getAllNames($phpcsFile)); $classReferencesIndex = []; $classReferences = array_filter( $references, static fn (stdClass $reference): bool => $reference->source === self::SOURCE_CODE && $reference->isClass, ); foreach ($classReferences as $classReference) { $classReferencesIndex[strtolower($classReference->name)] = NamespaceHelper::resolveName( $phpcsFile, $classReference->name, $classReference->type, $classReference->startPointer, ); } $referenceErrors = []; foreach ($references as $reference) { $useStatements = UseStatementHelper::getUseStatementsForPointer($phpcsFile, $reference->startPointer); $name = $reference->name; /** @var int $startPointer */ $startPointer = $reference->startPointer; $canonicalName = NamespaceHelper::normalizeToCanonicalName($name); $unqualifiedName = NamespaceHelper::getUnqualifiedNameFromFullyQualifiedName($name); if (in_array(strtolower($unqualifiedName), ['true', 'false', 'null'], true)) { continue; } $collidingUseStatementUniqueId = UseStatement::getUniqueId($reference->type, $unqualifiedName); $isPartialUse = false; foreach ($useStatements as $useStatement) { $useStatementName = $useStatement->getAlias() ?? $useStatement->getNameAsReferencedInFile(); if (strpos($name, $useStatementName . '\\') === 0) { $isPartialUse = true; break; } } $isFullyQualified = NamespaceHelper::isFullyQualifiedName($name) || ($namespacePointers === [] && NamespaceHelper::hasNamespace($name) && !$isPartialUse); $isGlobalFallback = !$isFullyQualified && !NamespaceHelper::hasNamespace($name) && $namespacePointers !== [] && !array_key_exists(UseStatement::getUniqueId($reference->type, $name), $useStatements); $isGlobalFunctionFallback = false; if ($reference->isFunction && $isGlobalFallback) { $isGlobalFunctionFallback = !array_key_exists(strtolower($reference->name), $definedFunctionsIndex) && function_exists( $reference->name, ); } $isGlobalConstantFallback = false; if ($reference->isConstant && $isGlobalFallback) { $isGlobalConstantFallback = !array_key_exists($reference->name, $definedConstantsIndex) && defined($reference->name); } if ($isFullyQualified) { if ($reference->isClass && $this->allowFullyQualifiedNameForCollidingClasses) { $lowerCasedUnqualifiedClassName = strtolower($unqualifiedName); if ( array_key_exists($lowerCasedUnqualifiedClassName, $definedClassesIndex) && $canonicalName !== NamespaceHelper::normalizeToCanonicalName( $definedClassesIndex[$lowerCasedUnqualifiedClassName], ) ) { continue; } if ( array_key_exists($lowerCasedUnqualifiedClassName, $classReferencesIndex) && $name !== $classReferencesIndex[$lowerCasedUnqualifiedClassName] ) { continue; } if ( array_key_exists($collidingUseStatementUniqueId, $useStatements) && $canonicalName !== NamespaceHelper::normalizeToCanonicalName( $useStatements[$collidingUseStatementUniqueId]->getFullyQualifiedTypeName(), ) ) { continue; } } elseif ($reference->isFunction && $this->allowFullyQualifiedNameForCollidingFunctions) { $lowerCasedUnqualifiedFunctionName = strtolower($unqualifiedName); if (array_key_exists($lowerCasedUnqualifiedFunctionName, $definedFunctionsIndex)) { continue; } if ( array_key_exists($collidingUseStatementUniqueId, $useStatements) && $canonicalName !== NamespaceHelper::normalizeToCanonicalName( $useStatements[$collidingUseStatementUniqueId]->getFullyQualifiedTypeName(), ) ) { continue; } } elseif ($reference->isConstant && $this->allowFullyQualifiedNameForCollidingConstants) { if (array_key_exists($unqualifiedName, $definedConstantsIndex)) { continue; } if ( array_key_exists($collidingUseStatementUniqueId, $useStatements) && $canonicalName !== NamespaceHelper::normalizeToCanonicalName( $useStatements[$collidingUseStatementUniqueId]->getFullyQualifiedTypeName(), ) ) { continue; } } } if ($isFullyQualified || $isGlobalFunctionFallback || $isGlobalConstantFallback) { if ($isFullyQualified && !$this->isRequiredToBeUsed($name)) { continue; } $isExceptionByName = StringHelper::endsWith($name, 'Exception') || $name === '\Throwable' || (StringHelper::endsWith($name, 'Error') && !NamespaceHelper::hasNamespace($name)) || in_array($canonicalName, $this->getSpecialExceptionNames(), true); $inIgnoredNames = in_array($canonicalName, $this->getIgnoredNames(), true); if ($isExceptionByName && !$inIgnoredNames && $this->allowFullyQualifiedExceptions) { continue; } if ( $isFullyQualified && !NamespaceHelper::hasNamespace($name) && $namespacePointers === [] ) { $label = sprintf( $reference->isConstant ? 'Constant %s' : ($reference->isFunction ? 'Function %s()' : 'Class %s'), $name, ); $fix = $phpcsFile->addFixableError(sprintf( '%s should not be referenced via a fully qualified name, but via an unqualified name without the leading \\, because the file does not have a namespace and the type cannot be put in a use statement.', $label, ), $startPointer, self::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME_WITHOUT_NAMESPACE); if ($fix) { $phpcsFile->fixer->beginChangeset(); if ($reference->source === self::SOURCE_ANNOTATION) { $fixedDocComment = AnnotationHelper::fixAnnotation( $reference->parsedDocComment, $reference->annotation, $reference->nameNode, new IdentifierTypeNode(substr($reference->name, 1)), ); FixerHelper::change( $phpcsFile, $reference->parsedDocComment->getOpenPointer(), $reference->parsedDocComment->getClosePointer(), $fixedDocComment, ); } elseif ($reference->source === self::SOURCE_ANNOTATION_CONSTANT_FETCH) { $fixedDocComment = AnnotationHelper::fixAnnotation( $reference->parsedDocComment, $reference->annotation, $reference->constantFetchNode, new ConstFetchNode(substr($reference->name, 1), $reference->constantFetchNode->name), ); FixerHelper::change( $phpcsFile, $reference->parsedDocComment->getOpenPointer(), $reference->parsedDocComment->getClosePointer(), $fixedDocComment, ); } else { FixerHelper::replace( $phpcsFile, $startPointer, substr($tokens[$startPointer]['content'], 1), ); } $phpcsFile->fixer->endChangeset(); } } else { $shouldBeUsed = NamespaceHelper::hasNamespace($name); if (!$shouldBeUsed) { if ($reference->isFunction) { $shouldBeUsed = $isFullyQualified ? !$this->allowFullyQualifiedGlobalFunctions : !$this->allowFallbackGlobalFunctions; } elseif ($reference->isConstant) { $shouldBeUsed = $isFullyQualified ? !$this->allowFullyQualifiedGlobalConstants : !$this->allowFallbackGlobalConstants; } else { $shouldBeUsed = !$this->allowFullyQualifiedGlobalClasses; } } if (!$shouldBeUsed) { continue; } $referenceErrors[] = (object) [ 'reference' => $reference, 'canonicalName' => $canonicalName, 'isGlobalConstantFallback' => $isGlobalConstantFallback, 'isGlobalFunctionFallback' => $isGlobalFunctionFallback, ]; } } elseif (!$this->allowPartialUses) { if (NamespaceHelper::isQualifiedName($name)) { $phpcsFile->addError(sprintf( 'Partial use statements are not allowed, but referencing %s found.', $name, ), $startPointer, self::CODE_PARTIAL_USE); } } } if (count($referenceErrors) === 0) { return; } $alreadyAddedUses = [ UseStatement::TYPE_CLASS => [], UseStatement::TYPE_FUNCTION => [], UseStatement::TYPE_CONSTANT => [], ]; $phpcsFile->fixer->beginChangeset(); foreach ($referenceErrors as $referenceData) { $reference = $referenceData->reference; /** @var int $startPointer */ $startPointer = $reference->startPointer; $canonicalName = $referenceData->canonicalName; $nameToReference = NamespaceHelper::getUnqualifiedNameFromFullyQualifiedName($reference->name); $canonicalNameToReference = $reference->isConstant ? $nameToReference : strtolower($nameToReference); $isGlobalConstantFallback = $referenceData->isGlobalConstantFallback; $isGlobalFunctionFallback = $referenceData->isGlobalFunctionFallback; $useStatements = UseStatementHelper::getUseStatementsForPointer($phpcsFile, $reference->startPointer); $canBeFixed = array_reduce( $alreadyAddedUses[$reference->type], static function (bool $carry, string $use) use ($canonicalName): bool { $useLastName = strtolower(NamespaceHelper::getLastNamePart($use)); $canonicalLastName = strtolower(NamespaceHelper::getLastNamePart($canonicalName)); return $useLastName === $canonicalLastName ? false : $carry; }, true, ); if ( ( $reference->isClass && array_key_exists($canonicalNameToReference, $definedClassesIndex) && $canonicalName !== NamespaceHelper::normalizeToCanonicalName($definedClassesIndex[$canonicalNameToReference]) ) || ( $reference->isClass && array_key_exists($canonicalNameToReference, $classReferencesIndex) && $canonicalName !== NamespaceHelper::normalizeToCanonicalName($classReferencesIndex[$canonicalNameToReference]) ) || ($reference->isFunction && array_key_exists($canonicalNameToReference, $definedFunctionsIndex)) || ($reference->isConstant && array_key_exists($canonicalNameToReference, $definedConstantsIndex)) ) { $canBeFixed = false; } foreach ($useStatements as $useStatement) { if ($useStatement->getType() !== $reference->type) { continue; } if ($useStatement->getFullyQualifiedTypeName() === $canonicalName) { continue; } if ($useStatement->getCanonicalNameAsReferencedInFile() !== $canonicalNameToReference) { continue; } $canBeFixed = false; break; } $label = sprintf( $reference->isConstant ? 'Constant %s' : ($reference->isFunction ? 'Function %s()' : 'Class %s'), $reference->name, ); $errorCode = $isGlobalConstantFallback || $isGlobalFunctionFallback ? self::CODE_REFERENCE_VIA_FALLBACK_GLOBAL_NAME : self::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME; $errorMessage = $isGlobalConstantFallback || $isGlobalFunctionFallback ? sprintf('%s should not be referenced via a fallback global name, but via a use statement.', $label) : sprintf('%s should not be referenced via a fully qualified name, but via a use statement.', $label); if (!$canBeFixed) { $phpcsFile->addError($errorMessage, $startPointer, $errorCode); continue; } $fix = $phpcsFile->addFixableError($errorMessage, $startPointer, $errorCode); if (!$fix) { continue; } $addUse = !in_array($canonicalName, $alreadyAddedUses[$reference->type], true); if ( $reference->isClass && array_key_exists($canonicalNameToReference, $definedClassesIndex) ) { $addUse = false; } foreach ($useStatements as $useStatement) { if ( $useStatement->getType() !== $reference->type || $useStatement->getFullyQualifiedTypeName() !== $canonicalName ) { continue; } $nameToReference = $useStatement->getNameAsReferencedInFile(); $addUse = false; // Lock the use statement, so it is not modified by other sniffs FixerHelper::replace( $phpcsFile, $useStatement->getPointer(), $phpcsFile->fixer->getTokenContent($useStatement->getPointer()), ); break; } if ($addUse) { $useStatementPlacePointer = $this->getUseStatementPlacePointer($phpcsFile, $openTagPointer, $useStatements); $useTypeName = UseStatement::getTypeName($reference->type); $useTypeFormatted = $useTypeName !== null ? sprintf('%s ', $useTypeName) : ''; $phpcsFile->fixer->addNewline($useStatementPlacePointer); FixerHelper::add( $phpcsFile, $useStatementPlacePointer, sprintf('use %s%s;', $useTypeFormatted, $canonicalName), ); $alreadyAddedUses[$reference->type][] = $canonicalName; } if ($reference->source === self::SOURCE_ANNOTATION) { $fixedDocComment = AnnotationHelper::fixAnnotation( $reference->parsedDocComment, $reference->annotation, $reference->nameNode, new IdentifierTypeNode($nameToReference), ); FixerHelper::change( $phpcsFile, $reference->parsedDocComment->getOpenPointer(), $reference->parsedDocComment->getClosePointer(), $fixedDocComment, ); } elseif ($reference->source === self::SOURCE_ANNOTATION_CONSTANT_FETCH) { $fixedDocComment = AnnotationHelper::fixAnnotation( $reference->parsedDocComment, $reference->annotation, $reference->constantFetchNode, new ConstFetchNode($nameToReference, $reference->constantFetchNode->name), ); FixerHelper::change( $phpcsFile, $reference->parsedDocComment->getOpenPointer(), $reference->parsedDocComment->getClosePointer(), $fixedDocComment, ); } elseif ($reference->source === self::SOURCE_ATTRIBUTE) { $attributeContent = TokenHelper::getContent($phpcsFile, $startPointer, $reference->endPointer); $fixedAttributeContent = preg_replace( '~(?<=\W)' . preg_quote($reference->name, '~') . '(?=\W)~', $nameToReference, $attributeContent, ); FixerHelper::change($phpcsFile, $startPointer, $reference->endPointer, $fixedAttributeContent); } else { FixerHelper::change($phpcsFile, $startPointer, $reference->endPointer, $nameToReference); } } $phpcsFile->fixer->endChangeset(); } /** * @return list */ private function getSpecialExceptionNames(): array { $this->normalizedSpecialExceptionNames ??= SniffSettingsHelper::normalizeArray($this->specialExceptionNames); return $this->normalizedSpecialExceptionNames; } /** * @return list */ private function getIgnoredNames(): array { $this->normalizedIgnoredNames ??= SniffSettingsHelper::normalizeArray($this->ignoredNames); return $this->normalizedIgnoredNames; } /** * @return list */ private function getNamespacesRequiredToUse(): array { $this->normalizedNamespacesRequiredToUse ??= SniffSettingsHelper::normalizeArray($this->namespacesRequiredToUse); return $this->normalizedNamespacesRequiredToUse; } /** * @param array $useStatements */ private function getUseStatementPlacePointer(File $phpcsFile, int $openTagPointer, array $useStatements): int { if (count($useStatements) !== 0) { $lastUseStatement = array_values($useStatements)[count($useStatements) - 1]; /** @var int $useStatementPlacePointer */ $useStatementPlacePointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $lastUseStatement->getPointer() + 1); return $useStatementPlacePointer; } $namespacePointer = TokenHelper::findNext($phpcsFile, T_NAMESPACE, $openTagPointer + 1); if ($namespacePointer !== null) { /** @var int $useStatementPlacePointer */ $useStatementPlacePointer = TokenHelper::findNext($phpcsFile, [T_SEMICOLON, T_OPEN_CURLY_BRACKET], $namespacePointer + 1); return $useStatementPlacePointer; } $tokens = $phpcsFile->getTokens(); $useStatementPlacePointer = $openTagPointer; if ( substr($tokens[$openTagPointer]['content'], -1) !== $phpcsFile->eolChar && $tokens[$openTagPointer + 1]['content'] === $phpcsFile->eolChar ) { // @codeCoverageIgnoreStart $useStatementPlacePointer++; // @codeCoverageIgnoreEnd } $nonWhitespacePointerAfterOpenTag = TokenHelper::findNextNonWhitespace($phpcsFile, $openTagPointer + 1); if (in_array($tokens[$nonWhitespacePointerAfterOpenTag]['code'], Tokens::$commentTokens, true)) { $commentEndPointer = CommentHelper::getCommentEndPointer($phpcsFile, $nonWhitespacePointerAfterOpenTag); if (StringHelper::endsWith($tokens[$commentEndPointer]['content'], $phpcsFile->eolChar)) { $useStatementPlacePointer = $commentEndPointer; } else { $newLineAfterComment = $commentEndPointer + 1; if (array_key_exists($newLineAfterComment, $tokens) && $tokens[$newLineAfterComment]['content'] === $phpcsFile->eolChar) { $pointerAfterCommentEnd = TokenHelper::findNextNonWhitespace($phpcsFile, $newLineAfterComment + 1); if (TokenHelper::findNextContent( $phpcsFile, T_WHITESPACE, $phpcsFile->eolChar, $newLineAfterComment + 1, $pointerAfterCommentEnd, ) !== null) { $useStatementPlacePointer = $commentEndPointer; } } } } $pointerAfter = TokenHelper::findNextEffective($phpcsFile, $useStatementPlacePointer + 1); if ($tokens[$pointerAfter]['code'] === T_DECLARE) { return TokenHelper::findNext($phpcsFile, T_SEMICOLON, $pointerAfter + 1); } return $useStatementPlacePointer; } private function isRequiredToBeUsed(string $name): bool { if ($this->namespacesRequiredToUse === []) { return true; } foreach ($this->getNamespacesRequiredToUse() as $namespace) { if (!NamespaceHelper::isTypeInNamespace($name, $namespace)) { continue; } return true; } return false; } /** * @return list */ private function getReferences(File $phpcsFile, int $openTagPointer): array { $tokens = $phpcsFile->getTokens(); $references = []; foreach (ReferencedNameHelper::getAllReferencedNames($phpcsFile, $openTagPointer) as $referencedName) { $reference = new stdClass(); $reference->source = self::SOURCE_CODE; $reference->name = $referencedName->getNameAsReferencedInFile(); $reference->type = $referencedName->getType(); $reference->startPointer = $referencedName->getStartPointer(); $reference->endPointer = $referencedName->getEndPointer(); $reference->isClass = $referencedName->isClass(); $reference->isConstant = $referencedName->isConstant(); $reference->isFunction = $referencedName->isFunction(); $references[] = $reference; } foreach (ReferencedNameHelper::getAllReferencedNamesInAttributes($phpcsFile, $openTagPointer) as $referencedName) { $reference = new stdClass(); $reference->source = self::SOURCE_ATTRIBUTE; $reference->name = $referencedName->getNameAsReferencedInFile(); $reference->type = $referencedName->getType(); $reference->startPointer = $referencedName->getStartPointer(); $reference->endPointer = $referencedName->getEndPointer(); $reference->isClass = $referencedName->isClass(); $reference->isConstant = $referencedName->isConstant(); $reference->isFunction = $referencedName->isFunction(); $references[] = $reference; } if (!$this->searchAnnotations) { return $references; } $searchAnnotationsPointer = $openTagPointer + 1; while (true) { $docCommentOpenPointer = TokenHelper::findNext($phpcsFile, T_DOC_COMMENT_OPEN_TAG, $searchAnnotationsPointer); if ($docCommentOpenPointer === null) { break; } $parsedDocComment = DocCommentHelper::parseDocComment($phpcsFile, $docCommentOpenPointer); if ($parsedDocComment !== null) { $annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenPointer); foreach ($annotations as $annotation) { $identifierTypeNodes = AnnotationHelper::getAnnotationNodesByType($annotation->getNode(), IdentifierTypeNode::class); foreach ($identifierTypeNodes as $typeHintNode) { $typeHint = $typeHintNode->name; $lowercasedTypeHint = strtolower($typeHint); if ( TypeHintHelper::isSimpleTypeHint($lowercasedTypeHint) || TypeHintHelper::isSimpleUnofficialTypeHints($lowercasedTypeHint) || !TypeHelper::isTypeName($typeHint) ) { continue; } $reference = new stdClass(); $reference->source = self::SOURCE_ANNOTATION; $reference->parsedDocComment = $parsedDocComment; $reference->annotation = $annotation; $reference->nameNode = $typeHintNode; $reference->name = $typeHint; $reference->type = ReferencedName::TYPE_CLASS; $reference->startPointer = $annotation->getStartPointer(); $reference->endPointer = null; $reference->isClass = true; $reference->isConstant = false; $reference->isFunction = false; $references[] = $reference; } $constantFetchNodes = AnnotationHelper::getAnnotationNodesByType($annotation->getNode(), ConstFetchNode::class); foreach ($constantFetchNodes as $constantFetchNode) { $reference = new stdClass(); $reference->source = self::SOURCE_ANNOTATION_CONSTANT_FETCH; $reference->parsedDocComment = $parsedDocComment; $reference->annotation = $annotation; $reference->constantFetchNode = $constantFetchNode; $reference->name = $constantFetchNode->className; $reference->type = ReferencedName::TYPE_CLASS; $reference->startPointer = $annotation->getStartPointer(); $reference->endPointer = null; $reference->isClass = true; $reference->isConstant = false; $reference->isFunction = false; $references[] = $reference; } } } $searchAnnotationsPointer = $tokens[$docCommentOpenPointer]['comment_closer'] + 1; } return $references; } } PK41]\_coding-standard/SlevomatCodingStandard/Sniffs/Namespaces/FullyQualifiedGlobalConstantsSniff.phpnu[isConstant(); } } PK41]5M^coding-standard/SlevomatCodingStandard/Sniffs/Namespaces/UseDoesNotStartWithBackslashSniff.phpnu[ */ public function register(): array { return [ T_USE, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $usePointer */ public function process(File $phpcsFile, $usePointer): void { if (!UseStatementHelper::isImportUse($phpcsFile, $usePointer)) { return; } $tokens = $phpcsFile->getTokens(); /** @var int $nextTokenPointer */ $nextTokenPointer = TokenHelper::findNextEffective($phpcsFile, $usePointer + 1); if ( in_array($tokens[$nextTokenPointer]['code'], TokenHelper::ONLY_NAME_TOKEN_CODES, true) && ( $tokens[$nextTokenPointer]['content'] === 'function' || $tokens[$nextTokenPointer]['content'] === 'const' ) ) { /** @var int $nextTokenPointer */ $nextTokenPointer = TokenHelper::findNextEffective($phpcsFile, $nextTokenPointer + 1); } if (!NamespaceHelper::isFullyQualifiedPointer($phpcsFile, $nextTokenPointer)) { return; } $fix = $phpcsFile->addFixableError( 'Use statement cannot start with a backslash.', $nextTokenPointer, self::CODE_STARTS_WITH_BACKSLASH, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace( $phpcsFile, $nextTokenPointer, ltrim($tokens[$nextTokenPointer]['content'], '\\'), ); $phpcsFile->fixer->endChangeset(); } } PK41]MVcoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/NamespaceDeclarationSniff.phpnu[ */ public function register(): array { return [ T_NAMESPACE, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $namespacePointer */ public function process(File $phpcsFile, $namespacePointer): void { $tokens = $phpcsFile->getTokens(); $pointerAfterNamespace = TokenHelper::findNextEffective($phpcsFile, $namespacePointer + 1); if ($tokens[$pointerAfterNamespace]['code'] === T_NS_SEPARATOR) { return; } $this->checkWhitespaceAfterNamespace($phpcsFile, $namespacePointer); $this->checkDisallowedContentBetweenNamespaceNameAndSemicolon($phpcsFile, $namespacePointer); $this->checkDisallowedBracketedSyntax($phpcsFile, $namespacePointer); } private function checkWhitespaceAfterNamespace(File $phpcsFile, int $namespacePointer): void { $tokens = $phpcsFile->getTokens(); $whitespacePointer = $namespacePointer + 1; if ($tokens[$whitespacePointer]['code'] !== T_WHITESPACE) { $phpcsFile->addError( 'Expected one space after namespace statement.', $namespacePointer, self::CODE_INVALID_WHITESPACE_AFTER_NAMESPACE, ); return; } if ($tokens[$whitespacePointer]['content'] === ' ') { return; } $errorMessage = sprintf('Expected one space after namespace statement, found %d.', strlen($tokens[$whitespacePointer]['content'])); $fix = $phpcsFile->addFixableError($errorMessage, $namespacePointer, self::CODE_INVALID_WHITESPACE_AFTER_NAMESPACE); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $whitespacePointer, ' '); $phpcsFile->fixer->endChangeset(); } private function checkDisallowedContentBetweenNamespaceNameAndSemicolon(File $phpcsFile, int $namespacePointer): void { if (array_key_exists('scope_opener', $phpcsFile->getTokens()[$namespacePointer])) { return; } $namespaceNameStartPointer = TokenHelper::findNextEffective($phpcsFile, $namespacePointer + 1); $namespaceNameEndPointer = TokenHelper::findNextExcluding( $phpcsFile, TokenHelper::NAME_TOKEN_CODES, $namespaceNameStartPointer + 1, ) - 1; /** @var int $namespaceSemicolonPointer */ $namespaceSemicolonPointer = TokenHelper::findNextLocal($phpcsFile, T_SEMICOLON, $namespaceNameEndPointer + 1); if ($namespaceNameEndPointer + 1 === $namespaceSemicolonPointer) { return; } $fix = $phpcsFile->addFixableError( 'Disallowed content between namespace name and semicolon.', $namespacePointer, self::CODE_DISALLOWED_CONTENT_BETWEEN_NAMESPACE_NAME_AND_SEMICOLON, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetween($phpcsFile, $namespaceNameEndPointer, $namespaceSemicolonPointer); $phpcsFile->fixer->endChangeset(); } private function checkDisallowedBracketedSyntax(File $phpcsFile, int $namespacePointer): void { $tokens = $phpcsFile->getTokens(); if (!array_key_exists('scope_opener', $tokens[$namespacePointer])) { return; } $fix = $phpcsFile->addFixableError( 'Bracketed syntax for namespaces is disallowed.', $namespacePointer, self::CODE_DISALLOWED_BRACKETED_SYNTAX, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $tokens[$namespacePointer]['scope_opener'], ';'); FixerHelper::replace($phpcsFile, $tokens[$namespacePointer]['scope_closer'], ''); $phpcsFile->fixer->endChangeset(); } } PK41]<܈EEecoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/FullyQualifiedClassNameInAnnotationSniff.phpnu[ */ public array $ignoredAnnotationNames = []; /** * @return array */ public function register(): array { return [ T_DOC_COMMENT_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $docCommentOpenPointer */ public function process(File $phpcsFile, $docCommentOpenPointer): void { $annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenPointer); $this->ignoredAnnotationNames = SniffSettingsHelper::normalizeArray($this->ignoredAnnotationNames); foreach ($annotations as $annotation) { $identifierTypeNodes = AnnotationHelper::getAnnotationNodesByType($annotation->getNode(), IdentifierTypeNode::class); $annotationName = $annotation->getName(); foreach ($identifierTypeNodes as $typeHintNode) { $typeHint = $typeHintNode->name; $lowercasedTypeHint = strtolower($typeHint); if ( TypeHintHelper::isSimpleTypeHint($lowercasedTypeHint) || TypeHintHelper::isSimpleUnofficialTypeHints($lowercasedTypeHint) || !TypeHelper::isTypeName($typeHint) || TypeHintHelper::isTypeDefinedInAnnotation($phpcsFile, $docCommentOpenPointer, $typeHint) ) { continue; } if (in_array($annotationName, $this->ignoredAnnotationNames, true)) { continue; } $fullyQualifiedTypeHint = TypeHintHelper::getFullyQualifiedTypeHint($phpcsFile, $docCommentOpenPointer, $typeHint); if ($fullyQualifiedTypeHint === $typeHint) { continue; } $fix = $phpcsFile->addFixableError(sprintf( 'Class name %s in %s should be referenced via a fully qualified name.', $fullyQualifiedTypeHint, $annotationName, ), $annotation->getStartPointer(), self::CODE_NON_FULLY_QUALIFIED_CLASS_NAME); if (!$fix) { continue; } $parsedDocComment = DocCommentHelper::parseDocComment($phpcsFile, $docCommentOpenPointer); $fixedDocComment = AnnotationHelper::fixAnnotation( $parsedDocComment, $annotation, $typeHintNode, new IdentifierTypeNode($fullyQualifiedTypeHint), ); $phpcsFile->fixer->beginChangeset(); FixerHelper::change( $phpcsFile, $parsedDocComment->getOpenPointer(), $parsedDocComment->getClosePointer(), $fixedDocComment, ); $phpcsFile->fixer->endChangeset(); } $constantFetchNodes = AnnotationHelper::getAnnotationNodesByType($annotation->getNode(), ConstFetchNode::class); foreach ($constantFetchNodes as $constantFetchNode) { $isClassConstant = $constantFetchNode->className !== ''; $typeHint = $isClassConstant ? $constantFetchNode->className : $constantFetchNode->name; if ($typeHint === 'self') { continue; } $fullyQualifiedTypeHint = $isClassConstant ? NamespaceHelper::resolveClassName($phpcsFile, $typeHint, $docCommentOpenPointer) : NamespaceHelper::resolveName($phpcsFile, $typeHint, ReferencedName::TYPE_CONSTANT, $docCommentOpenPointer); if ($fullyQualifiedTypeHint === $typeHint) { continue; } $fix = $phpcsFile->addFixableError(sprintf( '%s name %s in %s should be referenced via a fully qualified name.', $isClassConstant ? 'Class' : 'Constant', $fullyQualifiedTypeHint, $annotationName, ), $annotation->getStartPointer(), self::CODE_NON_FULLY_QUALIFIED_CLASS_NAME); if (!$fix) { continue; } $fixedConstantFetchNode = PhpDocParserHelper::cloneNode($constantFetchNode); if ($isClassConstant) { $fixedConstantFetchNode->className = $fullyQualifiedTypeHint; } else { $fixedConstantFetchNode->name = $fullyQualifiedTypeHint; } $parsedDocComment = DocCommentHelper::parseDocComment($phpcsFile, $docCommentOpenPointer); $fixedDocComment = AnnotationHelper::fixAnnotation( $parsedDocComment, $annotation, $constantFetchNode, $fixedConstantFetchNode, ); $phpcsFile->fixer->beginChangeset(); FixerHelper::change( $phpcsFile, $parsedDocComment->getOpenPointer(), $parsedDocComment->getClosePointer(), $fixedDocComment, ); $phpcsFile->fixer->endChangeset(); } } } } PK41]5 tkk^coding-standard/SlevomatCodingStandard/Sniffs/Namespaces/UseOnlyWhitelistedNamespacesSniff.phpnu[ */ public array $namespacesRequiredToUse = []; /** @var list|null */ private ?array $normalizedNamespacesRequiredToUse = null; /** * @return array */ public function register(): array { return [ T_USE, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $usePointer */ public function process(File $phpcsFile, $usePointer): void { if (!UseStatementHelper::isImportUse($phpcsFile, $usePointer)) { return; } $className = UseStatementHelper::getFullyQualifiedTypeNameFromUse($phpcsFile, $usePointer); if ($this->allowUseFromRootNamespace && !NamespaceHelper::isQualifiedName($className)) { return; } foreach ($this->getNamespacesRequiredToUse() as $namespace) { if (!NamespaceHelper::isTypeInNamespace($className, $namespace)) { continue; } return; } $phpcsFile->addError(sprintf( 'Type %s should not be used, but referenced via a fully qualified name.', $className, ), $usePointer, self::CODE_NON_FULLY_QUALIFIED); } /** * @return list */ private function getNamespacesRequiredToUse(): array { $this->normalizedNamespacesRequiredToUse ??= SniffSettingsHelper::normalizeArray($this->namespacesRequiredToUse); return $this->normalizedNamespacesRequiredToUse; } } PK41]Ucoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/MultipleUsesPerLineSniff.phpnu[ */ public function register(): array { return [ T_USE, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $usePointer */ public function process(File $phpcsFile, $usePointer): void { if (!UseStatementHelper::isImportUse($phpcsFile, $usePointer)) { return; } $endPointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $usePointer + 1); $commaPointer = TokenHelper::findNext($phpcsFile, T_COMMA, $usePointer + 1, $endPointer); if ($commaPointer === null) { return; } $phpcsFile->addError('Multiple used types per use statement are forbidden.', $commaPointer, self::CODE_MULTIPLE_USES_PER_LINE); } } PK41]7Ҙ:coding-standard/SlevomatCodingStandard/Sniffs/TestCase.phpnu[> $sniffProperties * @param list $codesToCheck * @param list $cliArgs */ protected static function checkFile(string $filePath, array $sniffProperties = [], array $codesToCheck = [], array $cliArgs = []): File { if (defined('PHP_CODESNIFFER_CBF') === false) { define('PHP_CODESNIFFER_CBF', false); } $codeSniffer = new Runner(); $codeSniffer->config = new Config(array_merge(['-s'], $cliArgs)); $codeSniffer->init(); if (count($sniffProperties) > 0) { foreach ($sniffProperties as $name => $value) { $sniffProperties[$name] = [ 'value' => $value, 'scope' => 'sniff', ]; } $codeSniffer->ruleset->ruleset[self::getSniffName()]['properties'] = $sniffProperties; } $sniffClassName = static::getSniffClassName(); /** @var Sniff $sniff */ $sniff = new $sniffClassName(); $codeSniffer->ruleset->sniffs = [$sniffClassName => $sniff]; if (count($codesToCheck) > 0) { foreach (self::getSniffClassReflection()->getConstants() as $constantName => $constantValue) { if (strpos($constantName, 'CODE_') !== 0 || in_array($constantValue, $codesToCheck, true)) { continue; } $codeSniffer->ruleset->ruleset[sprintf('%s.%s', self::getSniffName(), $constantValue)]['severity'] = 0; } } $codeSniffer->ruleset->populateTokenListeners(); $codeSniffer->config->tabWidth = self::TAB_WIDTH; $file = new LocalFile($filePath, $codeSniffer->ruleset, $codeSniffer->config); $file->process(); return $file; } protected static function assertNoSniffErrorInFile(File $phpcsFile): void { $errors = $phpcsFile->getErrors(); $text = sprintf('No errors expected, but %d errors found:', count($errors)); foreach ($errors as $line => $error) { $text .= sprintf( '%sLine %d:%s%s', PHP_EOL, $line, PHP_EOL, self::getFormattedErrors($error), ); } self::assertEmpty($errors, $text); } protected static function assertNoSniffWarningInFile(File $phpcsFile): void { $warnings = $phpcsFile->getWarnings(); $text = sprintf('No warnings expected, but %d warnings found:', count($warnings)); foreach ($warnings as $line => $warning) { $text .= sprintf( '%sLine %d:%s%s', PHP_EOL, $line, PHP_EOL, self::getFormattedErrors($warning), ); } self::assertEmpty($warnings, $text); } protected static function assertSniffError(File $phpcsFile, int $line, string $code, ?string $message = null): void { $errors = $phpcsFile->getErrors(); self::assertTrue(isset($errors[$line]), sprintf('Expected error on line %s, but none found.', $line)); $sniffCode = sprintf('%s.%s', self::getSniffName(), $code); self::assertTrue( self::hasError($errors[$line], $sniffCode, $message), sprintf( 'Expected error %s%s, but none found on line %d.%sErrors found on line %d:%s%s%s', $sniffCode, $message !== null ? sprintf(' with message "%s"', $message) : '', $line, PHP_EOL . PHP_EOL, $line, PHP_EOL, self::getFormattedErrors($errors[$line]), PHP_EOL, ), ); } protected static function assertSniffWarning(File $phpcsFile, int $line, string $code, ?string $message = null): void { $errors = $phpcsFile->getWarnings(); self::assertTrue(isset($errors[$line]), sprintf('Expected warning on line %s, but none found.', $line)); $sniffCode = sprintf('%s.%s', self::getSniffName(), $code); self::assertTrue( self::hasError($errors[$line], $sniffCode, $message), sprintf( 'Expected warning %s%s, but none found on line %d.%sWarnings found on line %d:%s%s%s', $sniffCode, $message !== null ? sprintf(' with message "%s"', $message) : '', $line, PHP_EOL . PHP_EOL, $line, PHP_EOL, self::getFormattedErrors($errors[$line]), PHP_EOL, ), ); } protected static function assertNoSniffError(File $phpcsFile, int $line): void { $errors = $phpcsFile->getErrors(); self::assertFalse( isset($errors[$line]), sprintf( 'Expected no error on line %s, but found:%s%s%s', $line, PHP_EOL . PHP_EOL, isset($errors[$line]) ? self::getFormattedErrors($errors[$line]) : '', PHP_EOL, ), ); } protected static function assertAllFixedInFile(File $phpcsFile): void { $phpcsFile->disableCaching(); $phpcsFile->fixer->fixFile(); self::assertStringEqualsFile(preg_replace('~(\\.php)$~', '.fixed\\1', $phpcsFile->getFilename()), $phpcsFile->fixer->getContents()); } /** * @return class-string */ protected static function getSniffClassName(): string { /** @var class-string $sniffClassName */ $sniffClassName = substr(static::class, 0, -strlen('Test')); return $sniffClassName; } protected static function getSniffName(): string { return Common::getSniffCode(static::getSniffClassName()); } private static function getSniffClassReflection(): ReflectionClass { static $reflections = []; $className = static::getSniffClassName(); return $reflections[$className] ?? $reflections[$className] = new ReflectionClass($className); } /** * @param list> $errorsOnLine */ private static function hasError(array $errorsOnLine, string $sniffCode, ?string $message): bool { $hasError = false; foreach ($errorsOnLine as $errorsOnPosition) { foreach ($errorsOnPosition as $error) { /** @var string $errorSource */ $errorSource = $error['source']; /** @var string $errorMessage */ $errorMessage = $error['message']; if ( $errorSource === $sniffCode && ( $message === null || strpos($errorMessage, $message) !== false ) ) { $hasError = true; break; } } } return $hasError; } /** * @param list> $errors */ private static function getFormattedErrors(array $errors): string { return implode( PHP_EOL, array_map( static fn (array $errors): string => implode( PHP_EOL, array_map(static fn (array $error): string => sprintf("\t%s: %s", $error['source'], $error['message']), $errors), ), $errors, ), ); } } PK41]U[3[3Rcoding-standard/SlevomatCodingStandard/Sniffs/Classes/PropertyDeclarationSniff.phpnu[|null */ public ?array $modifiersOrder = []; public bool $checkPromoted = false; public bool $enableMultipleSpacesBetweenModifiersCheck = false; /** @var array>|null */ private ?array $normalizedModifiersOrder = null; /** * @return array */ public function register(): array { return TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $modifierPointer */ public function process(File $phpcsFile, $modifierPointer): void { $tokens = $phpcsFile->getTokens(); $asPointer = TokenHelper::findPreviousEffective($phpcsFile, $modifierPointer - 1); if ($tokens[$asPointer]['code'] === T_AS) { return; } $nextPointer = TokenHelper::findNextEffective($phpcsFile, $modifierPointer + 1); if (in_array($tokens[$nextPointer]['code'], TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES, true)) { // We don't want to report the same property multiple times return; } if ($tokens[$modifierPointer]['code'] === T_STATIC) { if ($tokens[$nextPointer]['code'] === T_DOUBLE_COLON) { // Ignore static:: return; } if ($tokens[$nextPointer]['code'] === T_OPEN_PARENTHESIS) { // Ignore static() return; } if (in_array($tokens[$nextPointer]['code'], [T_OPEN_CURLY_BRACKET, T_SEMICOLON, T_TYPE_UNION], true)) { // Ignore "static" as return type hint of method return; } } // Ignore other class members with same mofidiers $propertyPointer = TokenHelper::findNext($phpcsFile, [T_CLASS, T_FUNCTION, T_CONST, T_VARIABLE], $modifierPointer + 1); if ($propertyPointer === null || $tokens[$propertyPointer]['code'] !== T_VARIABLE) { return; } if (!PropertyHelper::isProperty($phpcsFile, $propertyPointer, $this->checkPromoted)) { return; } $firstModifierPointer = PropertyHelper::getStartPointer($phpcsFile, $propertyPointer); $this->checkModifiersOrder($phpcsFile, $propertyPointer, $firstModifierPointer, $modifierPointer); $this->checkSpacesBetweenModifiers($phpcsFile, $propertyPointer, $firstModifierPointer, $modifierPointer); $this->checkTypeHintSpacing($phpcsFile, $propertyPointer, $modifierPointer); } private function checkModifiersOrder(File $phpcsFile, int $propertyPointer, int $firstModifierPointer, int $lastModifierPointer): void { $modifiersPointers = TokenHelper::findNextAll( $phpcsFile, TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES, $firstModifierPointer, $lastModifierPointer + 1, ); if (count($modifiersPointers) < 2) { return; } $tokens = $phpcsFile->getTokens(); $modifiersGroups = $this->getNormalizedModifiersOrder(); $expectedModifiersPositions = []; foreach ($modifiersPointers as $modifierPointer) { $position = 0; for ($i = 0; $i < count($modifiersGroups); $i++) { $modifierPositionInGroup = array_search($tokens[$modifierPointer]['code'], $modifiersGroups[$i], true); if ($modifierPositionInGroup !== false) { $expectedModifiersPositions[$modifierPointer] = $position + $modifierPositionInGroup; continue 2; } $position += count($modifiersGroups[$i]); } // Modifier position is not defined so add it to the end $expectedModifiersPositions[$modifierPointer] = $position; } $error = false; for ($i = 1; $i < count($modifiersPointers); $i++) { for ($j = 0; $j < $i; $j++) { if ($expectedModifiersPositions[$modifiersPointers[$i]] < $expectedModifiersPositions[$modifiersPointers[$j]]) { $error = true; break; } } } if (!$error) { return; } $actualModifiers = array_map(static fn (int $modifierPointer): string => $tokens[$modifierPointer]['content'], $modifiersPointers); $actualModifiersFormatted = implode(' ', $actualModifiers); asort($expectedModifiersPositions); $expectedModifiers = array_map( static fn (int $modifierPointer): string => $tokens[$modifierPointer]['content'], array_keys($expectedModifiersPositions), ); $expectedModifiersFormatted = implode(' ', $expectedModifiers); $fix = $phpcsFile->addFixableError( sprintf( 'Incorrect order of modifiers "%s" of property %s, expected "%s".', $actualModifiersFormatted, $tokens[$propertyPointer]['content'], $expectedModifiersFormatted, ), $firstModifierPointer, self::CODE_INCORRECT_ORDER_OF_MODIFIERS, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $firstModifierPointer, $lastModifierPointer, $expectedModifiersFormatted); $phpcsFile->fixer->endChangeset(); } private function checkSpacesBetweenModifiers( File $phpcsFile, int $propertyPointer, int $firstModifierPointer, int $lastModifierPointer ): void { if (!$this->enableMultipleSpacesBetweenModifiersCheck) { return; } $modifiersPointers = TokenHelper::findNextAll( $phpcsFile, TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES, $firstModifierPointer, $lastModifierPointer + 1, ); if (count($modifiersPointers) < 2) { return; } $tokens = $phpcsFile->getTokens(); $error = false; for ($i = 0; $i < count($modifiersPointers) - 1; $i++) { $whitespace = TokenHelper::getContent($phpcsFile, $modifiersPointers[$i] + 1, $modifiersPointers[$i + 1] - 1); if ($whitespace !== ' ') { $error = true; break; } } if (!$error) { return; } $fix = $phpcsFile->addFixableError( sprintf('There must be exactly one space between modifiers of property %s.', $tokens[$propertyPointer]['content']), $firstModifierPointer, self::CODE_MULTIPLE_SPACES_BETWEEN_MODIFIERS, ); if (!$fix) { return; } $expectedModifiers = array_map( static fn (int $modifierPointer): string => $tokens[$modifierPointer]['content'], $modifiersPointers, ); $expectedModifiersFormatted = implode(' ', $expectedModifiers); $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $firstModifierPointer, $lastModifierPointer, $expectedModifiersFormatted); $phpcsFile->fixer->endChangeset(); } private function checkTypeHintSpacing(File $phpcsFile, int $propertyPointer, int $lastModifierPointer): void { $typeHintEndPointer = TokenHelper::findPrevious( $phpcsFile, TokenHelper::TYPE_HINT_TOKEN_CODES, $propertyPointer - 1, $lastModifierPointer, ); if ($typeHintEndPointer === null) { return; } $tokens = $phpcsFile->getTokens(); $typeHintStartPointer = TypeHintHelper::getStartPointer($phpcsFile, $typeHintEndPointer); $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $typeHintStartPointer - 1, $lastModifierPointer); $nullabilitySymbolPointer = $previousPointer !== null && $tokens[$previousPointer]['code'] === T_NULLABLE ? $previousPointer : null; $propertyName = $tokens[$propertyPointer]['content']; if ($tokens[$lastModifierPointer + 1]['code'] !== T_WHITESPACE) { $errorMessage = sprintf('There must be exactly one space before type hint nullability symbol of property %s.', $propertyName); $errorCode = self::CODE_NO_SPACE_BEFORE_NULLABILITY_SYMBOL; $fix = $phpcsFile->addFixableError($errorMessage, $typeHintEndPointer, $errorCode); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::add($phpcsFile, $lastModifierPointer, ' '); $phpcsFile->fixer->endChangeset(); } } elseif ($tokens[$lastModifierPointer + 1]['content'] !== ' ') { if ($nullabilitySymbolPointer !== null) { $errorMessage = sprintf( 'There must be exactly one space before type hint nullability symbol of property %s.', $propertyName, ); $errorCode = self::CODE_MULTIPLE_SPACES_BEFORE_NULLABILITY_SYMBOL; } else { $errorMessage = sprintf('There must be exactly one space before type hint of property %s.', $propertyName); $errorCode = self::CODE_MULTIPLE_SPACES_BEFORE_TYPE_HINT; } $fix = $phpcsFile->addFixableError($errorMessage, $lastModifierPointer, $errorCode); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $lastModifierPointer + 1, ' '); $phpcsFile->fixer->endChangeset(); } } if ($tokens[$typeHintEndPointer + 1]['code'] !== T_WHITESPACE) { $fix = $phpcsFile->addFixableError( sprintf('There must be exactly one space between type hint and property %s.', $propertyName), $typeHintEndPointer, self::CODE_NO_SPACE_BETWEEN_TYPE_HINT_AND_PROPERTY, ); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::add($phpcsFile, $typeHintEndPointer, ' '); $phpcsFile->fixer->endChangeset(); } } elseif ($tokens[$typeHintEndPointer + 1]['content'] !== ' ') { $fix = $phpcsFile->addFixableError( sprintf('There must be exactly one space between type hint and property %s.', $propertyName), $typeHintEndPointer, self::CODE_MULTIPLE_SPACES_BETWEEN_TYPE_HINT_AND_PROPERTY, ); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $typeHintEndPointer + 1, ' '); $phpcsFile->fixer->endChangeset(); } } if ($nullabilitySymbolPointer === null) { return; } if ($nullabilitySymbolPointer + 1 === $typeHintStartPointer) { return; } $fix = $phpcsFile->addFixableError( sprintf('There must be no whitespace between type hint nullability symbol and type hint of property %s.', $propertyName), $typeHintStartPointer, self::CODE_WHITESPACE_AFTER_NULLABILITY_SYMBOL, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $nullabilitySymbolPointer + 1, ''); $phpcsFile->fixer->endChangeset(); } /** * @return array> */ private function getNormalizedModifiersOrder(): array { if ($this->normalizedModifiersOrder === null) { $modifiersGroups = SniffSettingsHelper::normalizeArray($this->modifiersOrder); if ($modifiersGroups === []) { $modifiersGroups = [ 'final, abstract', 'var, public, public(set), protected, protected(set), private, private(set)', 'static, readonly', ]; } $this->normalizedModifiersOrder = []; $mapping = [ 'final' => T_FINAL, 'abstract' => T_ABSTRACT, 'var' => T_VAR, 'public' => T_PUBLIC, 'public(set)' => T_PUBLIC_SET, 'protected' => T_PROTECTED, 'protected(set)' => T_PROTECTED_SET, 'private' => T_PRIVATE, 'private(set)' => T_PRIVATE_SET, 'static' => T_STATIC, 'readonly' => T_READONLY, ]; foreach ($modifiersGroups as $modifiersGroupNo => $modifiersGroup) { $this->normalizedModifiersOrder[$modifiersGroupNo] = []; /** @var list $modifiers */ $modifiers = preg_split('~\\s*,\\s*~', strtolower($modifiersGroup)); foreach ($modifiers as $modifier) { if (!array_key_exists($modifier, $mapping)) { throw new UnexpectedValueException(sprintf('Unknown property modifier "%s".', $modifier)); } $this->normalizedModifiersOrder[$modifiersGroupNo][] = $mapping[$modifier]; } } } return $this->normalizedModifiersOrder; } } PK41]RddNcoding-standard/SlevomatCodingStandard/Sniffs/Classes/EnumCaseSpacingSniff.phpnu[ */ public function register(): array { return [T_ENUM_CASE]; } protected function isNextMemberValid(File $phpcsFile, int $pointer): bool { $tokens = $phpcsFile->getTokens(); if ($tokens[$pointer]['code'] === T_ENUM_CASE) { return true; } $nextPointer = TokenHelper::findNext($phpcsFile, [T_FUNCTION, T_CONST, T_VARIABLE, T_USE, T_ENUM_CASE], $pointer + 1); return $nextPointer !== null && $tokens[$nextPointer]['code'] === T_ENUM_CASE; } protected function addError(File $phpcsFile, int $pointer, int $minExpectedLines, int $maxExpectedLines, int $found): bool { if ($minExpectedLines === $maxExpectedLines) { $errorMessage = $minExpectedLines === 1 ? 'Expected 1 blank line after enum case, found %3$d.' : 'Expected %2$d blank lines after enum case, found %3$d.'; } else { $errorMessage = 'Expected %1$d to %2$d blank lines after enum case, found %3$d.'; } $error = sprintf($errorMessage, $minExpectedLines, $maxExpectedLines, $found); return $phpcsFile->addFixableError($error, $pointer, self::CODE_INCORRECT_COUNT_OF_BLANK_LINES_AFTER_ENUM_CASE); } } PK41]A9" " Vcoding-standard/SlevomatCodingStandard/Sniffs/Classes/ForbiddenPublicPropertySniff.phpnu[ */ public function register(): array { return TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { $tokens = $phpcsFile->getTokens(); $asPointer = TokenHelper::findPreviousEffective($phpcsFile, $pointer - 1); if ($tokens[$asPointer]['code'] === T_AS) { return; } $nextPointer = TokenHelper::findNextEffective($phpcsFile, $pointer + 1); if (in_array($tokens[$nextPointer]['code'], TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES, true)) { // We don't want to report the same property multiple times return; } // Ignore other class members with same mofidiers $propertyPointer = TokenHelper::findNext($phpcsFile, [T_VARIABLE, T_CONST, T_FUNCTION, T_CLASS], $pointer + 1); if ( $propertyPointer === null || $tokens[$propertyPointer]['code'] !== T_VARIABLE || !PropertyHelper::isProperty($phpcsFile, $propertyPointer, $this->checkPromoted) ) { return; } // Skip sniff classes, they have public properties for configuration (unfortunately) if ($this->isSniffClass($phpcsFile, $propertyPointer)) { return; } $propertyStartPointer = PropertyHelper::getStartPointer($phpcsFile, $propertyPointer); $modifiersPointers = TokenHelper::findNextAll( $phpcsFile, TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES, $propertyStartPointer, $propertyPointer, ); $modifiersCodes = array_map(static fn (int $modifierPointer) => $tokens[$modifierPointer]['code'], $modifiersPointers); if (in_array(T_PROTECTED, $modifiersCodes, true) || in_array(T_PRIVATE, $modifiersCodes, true)) { return; } if ($this->allowReadonly && in_array(T_READONLY, $modifiersCodes, true)) { return; } if ( $this->allowNonPublicSet && ( in_array(T_PROTECTED_SET, $modifiersCodes, true) || in_array(T_PRIVATE_SET, $modifiersCodes, true) ) ) { return; } $phpcsFile->addError( 'Do not use public properties. Use method access instead.', $propertyPointer, self::CODE_FORBIDDEN_PUBLIC_PROPERTY, ); } private function isSniffClass(File $phpcsFile, int $position): bool { $classTokenPosition = ClassHelper::getClassPointer($phpcsFile, $position); $classNameToken = ClassHelper::getName($phpcsFile, $classTokenPosition); return StringHelper::endsWith($classNameToken, 'Sniff'); } } PK41] ?({{Zcoding-standard/SlevomatCodingStandard/Sniffs/Classes/EmptyLinesAroundClassBracesSniff.phpnu[ */ public function register(): array { return array_values(Tokens::$ooScopeTokens); } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $stackPointer */ public function process(File $phpcsFile, $stackPointer): void { $this->linesCountAfterOpeningBrace = SniffSettingsHelper::normalizeInteger($this->linesCountAfterOpeningBrace); $this->linesCountBeforeClosingBrace = SniffSettingsHelper::normalizeInteger($this->linesCountBeforeClosingBrace); $this->processOpeningBrace($phpcsFile, $stackPointer); $this->processClosingBrace($phpcsFile, $stackPointer); } private function processOpeningBrace(File $phpcsFile, int $stackPointer): void { $tokens = $phpcsFile->getTokens(); $typeToken = $tokens[$stackPointer]; $openerPointer = $typeToken['scope_opener']; $openerToken = $tokens[$openerPointer]; $nextPointerAfterOpeningBrace = TokenHelper::findNextNonWhitespace($phpcsFile, $openerPointer + 1); $nextTokenAfterOpeningBrace = $tokens[$nextPointerAfterOpeningBrace]; $lines = $nextTokenAfterOpeningBrace['line'] - $openerToken['line'] - 1; if ($lines === $this->linesCountAfterOpeningBrace) { return; } if ($this->linesCountAfterOpeningBrace === 1) { $fix = $phpcsFile->addFixableError( sprintf('There must be one empty line after %s opening brace.', $typeToken['content']), $openerPointer, $lines === 0 ? self::CODE_NO_EMPTY_LINE_AFTER_OPENING_BRACE : self::CODE_MULTIPLE_EMPTY_LINES_AFTER_OPENING_BRACE, ); } else { $fix = $phpcsFile->addFixableError(sprintf( 'There must be exactly %d empty lines after %s opening brace.', $this->linesCountAfterOpeningBrace, $typeToken['content'], ), $openerPointer, self::CODE_INCORRECT_EMPTY_LINES_AFTER_OPENING_BRACE); } if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); if ($lines < $this->linesCountAfterOpeningBrace) { for ($i = $lines; $i < $this->linesCountAfterOpeningBrace; $i++) { $phpcsFile->fixer->addNewline($openerPointer); } } else { for ($i = $openerPointer + $this->linesCountAfterOpeningBrace + 2; $i < $nextPointerAfterOpeningBrace; $i++) { if ($phpcsFile->fixer->getTokenContent($i) !== $phpcsFile->eolChar) { break; } FixerHelper::replace($phpcsFile, $i, ''); } } $phpcsFile->fixer->endChangeset(); } private function processClosingBrace(File $phpcsFile, int $stackPointer): void { $tokens = $phpcsFile->getTokens(); $typeToken = $tokens[$stackPointer]; $closerPointer = $typeToken['scope_closer']; $closerToken = $tokens[$closerPointer]; $previousPointerBeforeClosingBrace = TokenHelper::findPreviousNonWhitespace($phpcsFile, $closerPointer - 1); $previousTokenBeforeClosingBrace = $tokens[$previousPointerBeforeClosingBrace]; $lines = $closerToken['line'] - $previousTokenBeforeClosingBrace['line'] - 1; if ($lines === $this->linesCountBeforeClosingBrace) { return; } if ($this->linesCountBeforeClosingBrace === 1) { $fix = $phpcsFile->addFixableError( sprintf('There must be one empty line before %s closing brace.', $typeToken['content']), $closerPointer, $lines === 0 ? self::CODE_NO_EMPTY_LINE_BEFORE_CLOSING_BRACE : self::CODE_MULTIPLE_EMPTY_LINES_BEFORE_CLOSING_BRACE, ); } else { $fix = $phpcsFile->addFixableError(sprintf( 'There must be exactly %d empty lines before %s closing brace.', $this->linesCountBeforeClosingBrace, $typeToken['content'], ), $closerPointer, self::CODE_INCORRECT_EMPTY_LINES_BEFORE_CLOSING_BRACE); } if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); if ($lines < $this->linesCountBeforeClosingBrace) { for ($i = $lines; $i < $this->linesCountBeforeClosingBrace; $i++) { $phpcsFile->fixer->addNewlineBefore($closerPointer); } } else { FixerHelper::removeBetween( $phpcsFile, $previousPointerBeforeClosingBrace + $this->linesCountBeforeClosingBrace + 1, $closerPointer, ); } $phpcsFile->fixer->endChangeset(); } } PK41]RZ Vcoding-standard/SlevomatCodingStandard/Sniffs/Classes/ClassConstantVisibilitySniff.phpnu[ */ public function register(): array { return [ T_CONST, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $constantPointer */ public function process(File $phpcsFile, $constantPointer): void { $tokens = $phpcsFile->getTokens(); if (count($tokens[$constantPointer]['conditions']) === 0) { return; } /** @var int $classPointer */ $classPointer = array_keys($tokens[$constantPointer]['conditions'])[count($tokens[$constantPointer]['conditions']) - 1]; if (!in_array($tokens[$classPointer]['code'], Tokens::$ooScopeTokens, true)) { return; } $visibilityPointer = TokenHelper::findPreviousEffective($phpcsFile, $constantPointer - 1); if ($tokens[$visibilityPointer]['code'] === T_FINAL) { $visibilityPointer = TokenHelper::findPreviousEffective($phpcsFile, $visibilityPointer - 1); } if (in_array($tokens[$visibilityPointer]['code'], [T_PUBLIC, T_PROTECTED, T_PRIVATE], true)) { return; } $equalSignPointer = TokenHelper::findNext($phpcsFile, T_EQUAL, $constantPointer + 1); $namePointer = TokenHelper::findPreviousEffective($phpcsFile, $equalSignPointer - 1); $message = sprintf( 'Constant %s::%s visibility missing.', ClassHelper::getFullyQualifiedName($phpcsFile, $classPointer), $tokens[$namePointer]['content'], ); if ($this->fixable) { $fix = $phpcsFile->addFixableError($message, $constantPointer, self::CODE_MISSING_CONSTANT_VISIBILITY); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::addBefore($phpcsFile, $constantPointer, 'public '); $phpcsFile->fixer->endChangeset(); } } else { $phpcsFile->addError($message, $constantPointer, self::CODE_MISSING_CONSTANT_VISIBILITY); } } } PK41]hqw22Xcoding-standard/SlevomatCodingStandard/Sniffs/Classes/UnsupportedClassGroupException.phpnu[ */ public function register(): array { return [T_OBJECT_OPERATOR]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $objectOperatorPointer */ public function process(File $phpcsFile, $objectOperatorPointer): void { $tokens = $phpcsFile->getTokens(); $curlyBracketOpenerPointer = TokenHelper::findNextEffective($phpcsFile, $objectOperatorPointer + 1); if ($tokens[$curlyBracketOpenerPointer]['code'] !== T_OPEN_CURLY_BRACKET) { return; } $curlyBracketCloserPointer = $tokens[$curlyBracketOpenerPointer]['bracket_closer']; if (TokenHelper::findNextExcluding( $phpcsFile, T_CONSTANT_ENCAPSED_STRING, $curlyBracketOpenerPointer + 1, $curlyBracketCloserPointer, ) !== null) { return; } $pointerAfterCurlyBracketCloser = TokenHelper::findNextEffective($phpcsFile, $curlyBracketCloserPointer + 1); if ($tokens[$pointerAfterCurlyBracketCloser]['code'] === T_OPEN_PARENTHESIS) { return; } if (preg_match( '~^(["\'])([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\1$~', $tokens[$curlyBracketOpenerPointer + 1]['content'], $matches, ) !== 1) { return; } $fix = $phpcsFile->addFixableError( 'String expression property fetch is disallowed, use identifier property fetch.', $curlyBracketOpenerPointer, self::CODE_DISALLOWED_STRING_EXPRESSION_PROPERTY_FETCH, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $curlyBracketOpenerPointer, $curlyBracketCloserPointer, $matches[2]); $phpcsFile->fixer->endChangeset(); } } PK41]2 dcoding-standard/SlevomatCodingStandard/Sniffs/Classes/AbstractPropertyConstantAndEnumCaseSpacing.phpnu[minLinesCountBeforeWithComment = SniffSettingsHelper::normalizeInteger($this->minLinesCountBeforeWithComment); $this->maxLinesCountBeforeWithComment = SniffSettingsHelper::normalizeInteger($this->maxLinesCountBeforeWithComment); $this->minLinesCountBeforeWithoutComment = SniffSettingsHelper::normalizeInteger($this->minLinesCountBeforeWithoutComment); $this->maxLinesCountBeforeWithoutComment = SniffSettingsHelper::normalizeInteger($this->maxLinesCountBeforeWithoutComment); $this->minLinesCountBeforeMultiline = SniffSettingsHelper::normalizeNullableInteger($this->minLinesCountBeforeMultiline); $this->maxLinesCountBeforeMultiline = SniffSettingsHelper::normalizeNullableInteger($this->maxLinesCountBeforeMultiline); $tokens = $phpcsFile->getTokens(); $classPointer = ClassHelper::getClassPointer($phpcsFile, $pointer); $endPointer = $this->getEndPointer($phpcsFile, $pointer); $firstOnLinePointer = TokenHelper::findFirstTokenOnNextLine($phpcsFile, $endPointer); assert($firstOnLinePointer !== null); $nextFunctionPointer = TokenHelper::findNext( $phpcsFile, [T_FUNCTION, T_ENUM_CASE, T_CONST, T_VARIABLE, T_USE], $firstOnLinePointer + 1, ); if ( $nextFunctionPointer === null || $tokens[$nextFunctionPointer]['code'] === T_FUNCTION || $tokens[$nextFunctionPointer]['conditions'] !== $tokens[$pointer]['conditions'] ) { return $nextFunctionPointer ?? $firstOnLinePointer; } $types = [T_COMMENT, T_DOC_COMMENT_OPEN_TAG, T_ATTRIBUTE, T_ENUM_CASE, T_CONST, T_USE, ...TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES]; $nextPointer = TokenHelper::findNext($phpcsFile, $types, $firstOnLinePointer + 1, $tokens[$classPointer]['scope_closer']); if (!$this->isNextMemberValid($phpcsFile, $nextPointer)) { return $nextPointer; } $linesBetween = $tokens[$nextPointer]['line'] - $tokens[$endPointer]['line'] - 1; if (in_array($tokens[$nextPointer]['code'], [T_DOC_COMMENT_OPEN_TAG, T_COMMENT, T_ATTRIBUTE], true)) { $minExpectedLines = $this->minLinesCountBeforeWithComment; $maxExpectedLines = $this->maxLinesCountBeforeWithComment; } else { $minExpectedLines = $this->minLinesCountBeforeWithoutComment; $maxExpectedLines = $this->maxLinesCountBeforeWithoutComment; } if ( $this->minLinesCountBeforeMultiline !== null && !$this instanceof EnumCaseSpacingSniff && $tokens[$pointer]['line'] !== $tokens[$endPointer]['line'] ) { $minExpectedLines = max($minExpectedLines, $this->minLinesCountBeforeMultiline); $maxExpectedLines = max($minExpectedLines, $maxExpectedLines); } if ( $this->maxLinesCountBeforeMultiline !== null && !$this instanceof EnumCaseSpacingSniff && $tokens[$pointer]['line'] !== $tokens[$endPointer]['line'] ) { $maxExpectedLines = max($minExpectedLines, $this->maxLinesCountBeforeMultiline); } if ($linesBetween >= $minExpectedLines && $linesBetween <= $maxExpectedLines) { return $firstOnLinePointer; } $fix = $this->addError($phpcsFile, $pointer, $minExpectedLines, $maxExpectedLines, $linesBetween); if (!$fix) { return $firstOnLinePointer; } if ($linesBetween > $maxExpectedLines) { $lastPointerOnLine = TokenHelper::findLastTokenOnLine($phpcsFile, $endPointer); $firstPointerOnNextLine = TokenHelper::findFirstTokenOnLine($phpcsFile, $nextPointer); $phpcsFile->fixer->beginChangeset(); if ($maxExpectedLines > 0) { FixerHelper::add( $phpcsFile, $lastPointerOnLine, str_repeat($phpcsFile->eolChar, $maxExpectedLines), ); } FixerHelper::removeBetween($phpcsFile, $lastPointerOnLine, $firstPointerOnNextLine); $phpcsFile->fixer->endChangeset(); } elseif ($linesBetween < $minExpectedLines) { $phpcsFile->fixer->beginChangeset(); for ($i = 0; $i < $minExpectedLines - $linesBetween; $i++) { $phpcsFile->fixer->addNewlineBefore($firstOnLinePointer); } $phpcsFile->fixer->endChangeset(); } return $firstOnLinePointer; } private function getEndPointer(File $phpcsFile, int $pointer): int { $tokens = $phpcsFile->getTokens(); $endPointer = TokenHelper::findNext($phpcsFile, [T_SEMICOLON, T_OPEN_CURLY_BRACKET], $pointer + 1); return $tokens[$endPointer]['code'] === T_OPEN_CURLY_BRACKET ? $tokens[$endPointer]['bracket_closer'] : $endPointer; } } PK41]KJ J Ncoding-standard/SlevomatCodingStandard/Sniffs/Classes/ConstantSpacingSniff.phpnu[ */ public function register(): array { return [T_CONST]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $constantPointer */ public function process(File $phpcsFile, $constantPointer): int { $tokens = $phpcsFile->getTokens(); if ($tokens[$constantPointer]['conditions'] === []) { return $constantPointer; } /** @var int $classPointer */ $classPointer = array_keys($tokens[$constantPointer]['conditions'])[count($tokens[$constantPointer]['conditions']) - 1]; if (!in_array($tokens[$classPointer]['code'], Tokens::$ooScopeTokens, true)) { return $constantPointer; } return parent::process($phpcsFile, $constantPointer); } protected function isNextMemberValid(File $phpcsFile, int $pointer): bool { $tokens = $phpcsFile->getTokens(); if ($tokens[$pointer]['code'] === T_CONST) { return true; } $nextPointer = TokenHelper::findNext($phpcsFile, [T_FUNCTION, T_ENUM_CASE, T_CONST, T_VARIABLE, T_USE], $pointer + 1); return $nextPointer !== null && $tokens[$nextPointer]['code'] === T_CONST; } protected function addError(File $phpcsFile, int $pointer, int $minExpectedLines, int $maxExpectedLines, int $found): bool { if ($minExpectedLines === $maxExpectedLines) { $errorMessage = $minExpectedLines === 1 ? 'Expected 1 blank line after constant, found %3$d.' : 'Expected %2$d blank lines after constant, found %3$d.'; } else { $errorMessage = 'Expected %1$d to %2$d blank lines after constant, found %3$d.'; } $error = sprintf($errorMessage, $minExpectedLines, $maxExpectedLines, $found); return $phpcsFile->addFixableError($error, $pointer, self::CODE_INCORRECT_COUNT_OF_BLANK_LINES_AFTER_CONSTANT); } } PK41]ñ]Ycoding-standard/SlevomatCodingStandard/Sniffs/Classes/SuperfluousInterfaceNamingSniff.phpnu[ */ public function register(): array { return [ T_INTERFACE, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $interfacePointer */ public function process(File $phpcsFile, $interfacePointer): void { $interfaceName = ClassHelper::getName($phpcsFile, $interfacePointer); $this->checkPrefix($phpcsFile, $interfacePointer, $interfaceName); $this->checkSuffix($phpcsFile, $interfacePointer, $interfaceName); } private function checkPrefix(File $phpcsFile, int $interfacePointer, string $interfaceName): void { $prefix = substr($interfaceName, 0, 9); if (strtolower($prefix) !== 'interface') { return; } $phpcsFile->addError(sprintf('Superfluous prefix "%s".', $prefix), $interfacePointer, self::CODE_SUPERFLUOUS_PREFIX); } private function checkSuffix(File $phpcsFile, int $interfacePointer, string $interfaceName): void { $suffix = substr($interfaceName, -9); if (strtolower($suffix) !== 'interface') { return; } $phpcsFile->addError(sprintf('Superfluous suffix "%s".', $suffix), $interfacePointer, self::CODE_SUPERFLUOUS_SUFFIX); } } PK41]\h ""Qcoding-standard/SlevomatCodingStandard/Sniffs/Classes/ClassMemberSpacingSniff.phpnu[ */ public function register(): array { return TokenHelper::CLASS_TYPE_WITH_ANONYMOUS_CLASS_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $classPointer */ public function process(File $phpcsFile, $classPointer): void { $this->linesCountBetweenMembers = SniffSettingsHelper::normalizeInteger($this->linesCountBetweenMembers); $tokens = $phpcsFile->getTokens(); $memberPointer = null; do { $previousMemberPointer = $memberPointer; $memberPointer = $this->findNextMember( $phpcsFile, $classPointer, $previousMemberPointer ?? $tokens[$classPointer]['scope_opener'], ); if ($memberPointer === null) { break; } if ($previousMemberPointer === null) { continue; } if ($tokens[$previousMemberPointer]['code'] === $tokens[$memberPointer]['code']) { continue; } $previousMemberEndPointer = $this->getMemberEndPointer($phpcsFile, $previousMemberPointer); $hasCommentWithNewLineAfterPreviousMember = false; $commentPointerAfterPreviousMember = TokenHelper::findNextNonWhitespace($phpcsFile, $previousMemberEndPointer + 1); if ( in_array($tokens[$commentPointerAfterPreviousMember]['code'], TokenHelper::INLINE_COMMENT_TOKEN_CODES, true) && ( $tokens[$previousMemberEndPointer]['line'] === $tokens[$commentPointerAfterPreviousMember]['line'] || $tokens[$previousMemberEndPointer]['line'] + 1 === $tokens[$commentPointerAfterPreviousMember]['line'] ) ) { $previousMemberEndPointer = CommentHelper::getCommentEndPointer($phpcsFile, $commentPointerAfterPreviousMember); if (StringHelper::endsWith($tokens[$commentPointerAfterPreviousMember]['content'], $phpcsFile->eolChar)) { $hasCommentWithNewLineAfterPreviousMember = true; } } $memberStartPointer = $this->getMemberStartPointer($phpcsFile, $memberPointer, $previousMemberEndPointer); $actualLinesCount = $tokens[$memberStartPointer]['line'] - $tokens[$previousMemberEndPointer]['line'] - 1; if ($actualLinesCount === $this->linesCountBetweenMembers) { continue; } $errorMessage = $this->linesCountBetweenMembers === 1 ? 'Expected 1 blank line between class members, found %2$d.' : 'Expected %1$d blank lines between class members, found %2$d.'; $firstPointerOnMemberLine = TokenHelper::findFirstTokenOnLine($phpcsFile, $memberStartPointer); $nonWhitespaceBetweenMembersPointer = TokenHelper::findNextNonWhitespace( $phpcsFile, $previousMemberEndPointer + 1, $firstPointerOnMemberLine, ); $errorParameters = [ sprintf($errorMessage, $this->linesCountBetweenMembers, $actualLinesCount), $memberPointer, self::CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS, ]; if ($nonWhitespaceBetweenMembersPointer !== null) { $phpcsFile->addError(...$errorParameters); continue; } $fix = $phpcsFile->addFixableError(...$errorParameters); if (!$fix) { continue; } $newLines = str_repeat( $phpcsFile->eolChar, $this->linesCountBetweenMembers + ($hasCommentWithNewLineAfterPreviousMember ? 0 : 1), ); $phpcsFile->fixer->beginChangeset(); FixerHelper::add($phpcsFile, $previousMemberEndPointer, $newLines); FixerHelper::removeBetween($phpcsFile, $previousMemberEndPointer, $firstPointerOnMemberLine); $phpcsFile->fixer->endChangeset(); } while (true); } private function findNextMember(File $phpcsFile, int $classPointer, int $previousMemberPointer): ?int { $tokens = $phpcsFile->getTokens(); $memberTokenCodes = [T_USE, T_CONST, T_FUNCTION, T_ENUM_CASE, ...TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES]; $memberPointer = $previousMemberPointer; do { $memberPointer = TokenHelper::findNext( $phpcsFile, $memberTokenCodes, $memberPointer + 1, $tokens[$classPointer]['scope_closer'], ); if ($memberPointer === null) { return null; } if ($tokens[$memberPointer]['code'] === T_USE) { if (!UseStatementHelper::isTraitUse($phpcsFile, $memberPointer)) { continue; } } elseif (in_array($tokens[$memberPointer]['code'], TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES, true)) { $asPointer = TokenHelper::findPreviousEffective($phpcsFile, $memberPointer - 1); if ($tokens[$asPointer]['code'] === T_AS) { continue; } $propertyPointer = TokenHelper::findNext($phpcsFile, [T_VARIABLE, T_FUNCTION, T_CONST], $memberPointer + 1); if ( $propertyPointer === null || $tokens[$propertyPointer]['code'] !== T_VARIABLE || !PropertyHelper::isProperty($phpcsFile, $propertyPointer) ) { continue; } $memberPointer = $propertyPointer; } if (ScopeHelper::isInSameScope($phpcsFile, $memberPointer, $previousMemberPointer)) { break; } } while (true); return $memberPointer; } private function getMemberStartPointer(File $phpcsFile, int $memberPointer, int $previousMemberEndPointer): int { $tokens = $phpcsFile->getTokens(); $memberFirstCodePointer = $this->getMemberFirstCodePointer($phpcsFile, $memberPointer); do { if ($memberFirstCodePointer <= $previousMemberEndPointer) { return TokenHelper::findNextNonWhitespace($phpcsFile, $memberFirstCodePointer + 1); } $pointerBefore = TokenHelper::findPreviousNonWhitespace($phpcsFile, $memberFirstCodePointer - 1); if ($tokens[$pointerBefore]['code'] === T_ATTRIBUTE_END) { $memberFirstCodePointer = $tokens[$pointerBefore]['attribute_opener']; continue; } if (in_array($tokens[$pointerBefore]['code'], Tokens::$commentTokens, true)) { $pointerBeforeComment = TokenHelper::findPreviousEffective($phpcsFile, $pointerBefore - 1); if ($tokens[$pointerBeforeComment]['line'] !== $tokens[$pointerBefore]['line']) { $memberFirstCodePointer = array_key_exists('comment_opener', $tokens[$pointerBefore]) ? $tokens[$pointerBefore]['comment_opener'] : CommentHelper::getMultilineCommentStartPointer($phpcsFile, $pointerBefore); continue; } } break; } while (true); return $memberFirstCodePointer; } private function getMemberFirstCodePointer(File $phpcsFile, int $memberPointer): int { $tokens = $phpcsFile->getTokens(); if ($tokens[$memberPointer]['code'] === T_USE) { return $memberPointer; } $endTokenCodes = [T_SEMICOLON, T_CLOSE_CURLY_BRACKET]; $startOrEndTokenCodes = [...TokenHelper::MODIFIERS_TOKEN_CODES, ...$endTokenCodes]; $firstCodePointer = $memberPointer; $previousFirstCodePointer = $memberPointer; do { /** @var int $firstCodePointer */ $firstCodePointer = TokenHelper::findPrevious($phpcsFile, $startOrEndTokenCodes, $firstCodePointer - 1); if (in_array($tokens[$firstCodePointer]['code'], $endTokenCodes, true)) { break; } $previousFirstCodePointer = $firstCodePointer; } while (true); return $previousFirstCodePointer; } private function getMemberEndPointer(File $phpcsFile, int $memberPointer): int { $tokens = $phpcsFile->getTokens(); if ( $tokens[$memberPointer]['code'] === T_USE // Property with hooks || $tokens[$memberPointer]['code'] === T_VARIABLE ) { $pointer = TokenHelper::findNextLocal($phpcsFile, [T_SEMICOLON, T_OPEN_CURLY_BRACKET], $memberPointer + 1); return $tokens[$pointer]['code'] === T_OPEN_CURLY_BRACKET ? $tokens[$pointer]['bracket_closer'] : $pointer; } if ($tokens[$memberPointer]['code'] === T_FUNCTION && !FunctionHelper::isAbstract($phpcsFile, $memberPointer)) { return $tokens[$memberPointer]['scope_closer']; } return TokenHelper::findNext($phpcsFile, T_SEMICOLON, $memberPointer + 1); } } PK41])m)mMcoding-standard/SlevomatCodingStandard/Sniffs/Classes/ClassStructureSniff.phpnu[ [ self::GROUP_PUBLIC_CONSTANTS, self::GROUP_PROTECTED_CONSTANTS, self::GROUP_PRIVATE_CONSTANTS, ], self::GROUP_SHORTCUT_STATIC_PROPERTIES => [ self::GROUP_PUBLIC_STATIC_PROPERTIES, self::GROUP_PROTECTED_STATIC_PROPERTIES, self::GROUP_PRIVATE_STATIC_PROPERTIES, ], self::GROUP_SHORTCUT_PROPERTIES => [ self::GROUP_SHORTCUT_STATIC_PROPERTIES, self::GROUP_PUBLIC_PROPERTIES, self::GROUP_PROTECTED_PROPERTIES, self::GROUP_PRIVATE_PROPERTIES, ], self::GROUP_SHORTCUT_PUBLIC_METHODS => [ self::GROUP_PUBLIC_FINAL_METHODS, self::GROUP_PUBLIC_STATIC_FINAL_METHODS, self::GROUP_PUBLIC_ABSTRACT_METHODS, self::GROUP_PUBLIC_STATIC_ABSTRACT_METHODS, self::GROUP_PUBLIC_STATIC_METHODS, self::GROUP_PUBLIC_METHODS, ], self::GROUP_SHORTCUT_PROTECTED_METHODS => [ self::GROUP_PROTECTED_FINAL_METHODS, self::GROUP_PROTECTED_STATIC_FINAL_METHODS, self::GROUP_PROTECTED_ABSTRACT_METHODS, self::GROUP_PROTECTED_STATIC_ABSTRACT_METHODS, self::GROUP_PROTECTED_STATIC_METHODS, self::GROUP_PROTECTED_METHODS, ], self::GROUP_SHORTCUT_PRIVATE_METHODS => [ self::GROUP_PRIVATE_STATIC_METHODS, self::GROUP_PRIVATE_METHODS, ], self::GROUP_SHORTCUT_FINAL_METHODS => [ self::GROUP_PUBLIC_FINAL_METHODS, self::GROUP_PROTECTED_FINAL_METHODS, self::GROUP_PUBLIC_STATIC_FINAL_METHODS, self::GROUP_PROTECTED_STATIC_FINAL_METHODS, ], self::GROUP_SHORTCUT_ABSTRACT_METHODS => [ self::GROUP_PUBLIC_ABSTRACT_METHODS, self::GROUP_PROTECTED_ABSTRACT_METHODS, self::GROUP_PUBLIC_STATIC_ABSTRACT_METHODS, self::GROUP_PROTECTED_STATIC_ABSTRACT_METHODS, ], self::GROUP_SHORTCUT_STATIC_METHODS => [ self::GROUP_STATIC_CONSTRUCTORS, self::GROUP_PUBLIC_STATIC_FINAL_METHODS, self::GROUP_PROTECTED_STATIC_FINAL_METHODS, self::GROUP_PUBLIC_STATIC_ABSTRACT_METHODS, self::GROUP_PROTECTED_STATIC_ABSTRACT_METHODS, self::GROUP_PUBLIC_STATIC_METHODS, self::GROUP_PROTECTED_STATIC_METHODS, self::GROUP_PRIVATE_STATIC_METHODS, ], self::GROUP_SHORTCUT_METHODS => [ self::GROUP_SHORTCUT_FINAL_METHODS, self::GROUP_SHORTCUT_ABSTRACT_METHODS, self::GROUP_SHORTCUT_STATIC_METHODS, self::GROUP_CONSTRUCTOR, self::GROUP_DESTRUCTOR, self::GROUP_PUBLIC_METHODS, self::GROUP_PROTECTED_METHODS, self::GROUP_PRIVATE_METHODS, self::GROUP_MAGIC_METHODS, ], ]; private const SPECIAL_METHODS = [ '__construct' => self::GROUP_CONSTRUCTOR, '__destruct' => self::GROUP_DESTRUCTOR, '__call' => self::GROUP_MAGIC_METHODS, '__callstatic' => self::GROUP_MAGIC_METHODS, '__get' => self::GROUP_MAGIC_METHODS, '__set' => self::GROUP_MAGIC_METHODS, '__isset' => self::GROUP_MAGIC_METHODS, '__unset' => self::GROUP_MAGIC_METHODS, '__sleep' => self::GROUP_MAGIC_METHODS, '__wakeup' => self::GROUP_MAGIC_METHODS, '__serialize' => self::GROUP_MAGIC_METHODS, '__unserialize' => self::GROUP_MAGIC_METHODS, '__tostring' => self::GROUP_MAGIC_METHODS, '__invoke' => self::GROUP_INVOKE_METHOD, '__set_state' => self::GROUP_MAGIC_METHODS, '__clone' => self::GROUP_MAGIC_METHODS, '__debuginfo' => self::GROUP_MAGIC_METHODS, ]; /** @var array */ public array $methodGroups = []; /** @var list */ public array $groups = []; /** @var array, annotations: array}>>|null */ private ?array $normalizedMethodGroups = null; /** @var array|null */ private ?array $normalizedGroups = null; /** * @return array */ public function register(): array { return array_values(Tokens::$ooScopeTokens); } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): int { $tokens = $phpcsFile->getTokens(); $rootScopeToken = $tokens[$pointer]; assert(array_key_exists('scope_opener', $rootScopeToken)); $groupsOrder = $this->getNormalizedGroups(); $groupLastMemberPointer = $rootScopeToken['scope_opener']; $expectedGroup = null; $groupsFirstMembers = []; while (true) { $nextGroup = $this->findNextGroup($phpcsFile, $groupLastMemberPointer, $rootScopeToken); if ($nextGroup === null) { break; } [$groupFirstMemberPointer, $groupLastMemberPointer, $group] = $nextGroup; // Use "magic methods" group for __invoke() when "invoke" group is not explicitly defined if ($group === self::GROUP_INVOKE_METHOD && !array_key_exists($group, $groupsOrder)) { $group = self::GROUP_MAGIC_METHODS; } if ($groupsOrder[$group] >= ($expectedGroup !== null ? $groupsOrder[$expectedGroup] : 0)) { $groupsFirstMembers[$group] = $groupFirstMemberPointer; $expectedGroup = $group; continue; } $expectedGroups = array_filter( $groupsOrder, static fn (int $order): bool => $order >= $groupsOrder[$expectedGroup], ); $fix = $phpcsFile->addFixableError( sprintf( 'The placement of "%s" group is invalid. Last group was "%s" and one of these is expected after it: %s', $group, $expectedGroup, implode(', ', array_keys($expectedGroups)), ), $groupFirstMemberPointer, self::CODE_INCORRECT_GROUP_ORDER, ); if (!$fix) { continue; } foreach ($groupsFirstMembers as $memberGroup => $firstMemberPointer) { if ($groupsOrder[$memberGroup] <= $groupsOrder[$group]) { continue; } $this->fixIncorrectGroupOrder($phpcsFile, $groupFirstMemberPointer, $groupLastMemberPointer, $firstMemberPointer); // run the sniff again to fix the rest of the groups return $pointer - 1; } } return $pointer + 1; } /** * @param array{scope_closer: int, level: int} $rootScopeToken * @return array{int, int, string}|null */ private function findNextGroup(File $phpcsFile, int $pointer, array $rootScopeToken): ?array { $tokens = $phpcsFile->getTokens(); $currentTokenPointer = $pointer; while (true) { $currentTokenPointer = TokenHelper::findNext( $phpcsFile, [T_USE, T_ENUM_CASE, T_CONST, T_VARIABLE, T_FUNCTION], $currentTokenPointer + 1, $rootScopeToken['scope_closer'], ); if ($currentTokenPointer === null) { break; } $currentToken = $tokens[$currentTokenPointer]; if ($currentToken['code'] === T_VARIABLE && !PropertyHelper::isProperty($phpcsFile, $currentTokenPointer)) { continue; } if ($currentToken['level'] - $rootScopeToken['level'] !== 1) { continue; } $group = $this->getGroupForToken($phpcsFile, $currentTokenPointer); if (!isset($currentGroup)) { $currentGroup = $group; $groupFirstMemberPointer = $currentTokenPointer; } if ($group !== $currentGroup) { break; } $groupLastMemberPointer = $currentTokenPointer; $currentTokenPointer = $currentToken['code'] === T_VARIABLE // Skip to the end of the property definition ? PropertyHelper::getEndPointer($phpcsFile, $currentTokenPointer) : ($currentToken['scope_closer'] ?? $currentTokenPointer); } if (!isset($currentGroup)) { return null; } assert(isset($groupFirstMemberPointer) === true); assert(isset($groupLastMemberPointer) === true); return [$groupFirstMemberPointer, $groupLastMemberPointer, $currentGroup]; } private function getGroupForToken(File $phpcsFile, int $pointer): string { $tokens = $phpcsFile->getTokens(); switch ($tokens[$pointer]['code']) { case T_USE: return self::GROUP_USES; case T_ENUM_CASE: return self::GROUP_ENUM_CASES; case T_CONST: switch ($this->getVisibilityForToken($phpcsFile, $pointer)) { case T_PUBLIC: return self::GROUP_PUBLIC_CONSTANTS; case T_PROTECTED: return self::GROUP_PROTECTED_CONSTANTS; } return self::GROUP_PRIVATE_CONSTANTS; case T_FUNCTION: $name = strtolower(FunctionHelper::getName($phpcsFile, $pointer)); if (array_key_exists($name, self::SPECIAL_METHODS)) { return self::SPECIAL_METHODS[$name]; } $methodGroup = $this->resolveMethodGroup($phpcsFile, $pointer, $name); if ($methodGroup !== null) { return $methodGroup; } $visibility = $this->getVisibilityForToken($phpcsFile, $pointer); $isStatic = $this->isMemberStatic($phpcsFile, $pointer); $isFinal = $this->isMethodFinal($phpcsFile, $pointer); if ($this->isMethodAbstract($phpcsFile, $pointer)) { if ($visibility === T_PUBLIC) { return $isStatic ? self::GROUP_PUBLIC_STATIC_ABSTRACT_METHODS : self::GROUP_PUBLIC_ABSTRACT_METHODS; } return $isStatic ? self::GROUP_PROTECTED_STATIC_ABSTRACT_METHODS : self::GROUP_PROTECTED_ABSTRACT_METHODS; } if ($isStatic && $visibility === T_PUBLIC && $this->isStaticConstructor($phpcsFile, $pointer)) { return self::GROUP_STATIC_CONSTRUCTORS; } switch ($visibility) { case T_PUBLIC: if ($isFinal) { return $isStatic ? self::GROUP_PUBLIC_STATIC_FINAL_METHODS : self::GROUP_PUBLIC_FINAL_METHODS; } return $isStatic ? self::GROUP_PUBLIC_STATIC_METHODS : self::GROUP_PUBLIC_METHODS; case T_PROTECTED: if ($isFinal) { return $isStatic ? self::GROUP_PROTECTED_STATIC_FINAL_METHODS : self::GROUP_PROTECTED_FINAL_METHODS; } return $isStatic ? self::GROUP_PROTECTED_STATIC_METHODS : self::GROUP_PROTECTED_METHODS; } return $isStatic ? self::GROUP_PRIVATE_STATIC_METHODS : self::GROUP_PRIVATE_METHODS; default: $isStatic = $this->isMemberStatic($phpcsFile, $pointer); $visibility = $this->getVisibilityForToken($phpcsFile, $pointer); switch ($visibility) { case T_PUBLIC: case T_PUBLIC_SET: return $isStatic ? self::GROUP_PUBLIC_STATIC_PROPERTIES : self::GROUP_PUBLIC_PROPERTIES; case T_PROTECTED: return $isStatic ? self::GROUP_PROTECTED_STATIC_PROPERTIES : self::GROUP_PROTECTED_PROPERTIES; default: return $isStatic ? self::GROUP_PRIVATE_STATIC_PROPERTIES : self::GROUP_PRIVATE_PROPERTIES; } } } private function resolveMethodGroup(File $phpcsFile, int $pointer, string $method): ?string { foreach ($this->getNormalizedMethodGroups() as $group => $methodRequirements) { foreach ($methodRequirements as $methodRequirement) { if ($methodRequirement['name'] !== null) { $requiredName = strtolower($methodRequirement['name']); if (StringHelper::endsWith($requiredName, '*')) { $methodNamePrefix = substr($requiredName, 0, -1); if ($method === $methodNamePrefix || !StringHelper::startsWith($method, $methodNamePrefix)) { continue; } } elseif ($method !== $requiredName) { continue; } } if ( $this->hasRequiredAnnotations($phpcsFile, $pointer, $methodRequirement['annotations']) && $this->hasRequiredAttributes($phpcsFile, $pointer, $methodRequirement['attributes']) ) { return $group; } } } return null; } /** * @param array $requiredAnnotations */ private function hasRequiredAnnotations(File $phpcsFile, int $pointer, array $requiredAnnotations): bool { if ($requiredAnnotations === []) { return true; } $annotations = []; foreach (AnnotationHelper::getAnnotations($phpcsFile, $pointer) as $annotation) { $annotations[$annotation->getName()] = true; } foreach ($requiredAnnotations as $requiredAnnotation) { if (!array_key_exists('@' . $requiredAnnotation, $annotations)) { return false; } } return true; } /** * @param array $requiredAttributes */ private function hasRequiredAttributes(File $phpcsFile, int $pointer, array $requiredAttributes): bool { if ($requiredAttributes === []) { return true; } $attributesClassNames = $this->getAttributeClassNamesForToken($phpcsFile, $pointer); foreach ($requiredAttributes as $requiredAttribute) { if (!array_key_exists(strtolower($requiredAttribute), $attributesClassNames)) { return false; } } return true; } /** * @return array */ private function getAttributeClassNamesForToken(File $phpcsFile, int $pointer): array { $attributes = []; foreach (AttributeHelper::getAttributes($phpcsFile, $pointer) as $attribute) { $attributes[strtolower(ltrim($attribute->getFullyQualifiedName(), '\\'))] = $attribute->getFullyQualifiedName(); } return $attributes; } /** * @return int|string */ private function getVisibilityForToken(File $phpcsFile, int $pointer) { $tokens = $phpcsFile->getTokens(); $previousPointer = $pointer - 1; $endTokenCodes = [T_OPEN_CURLY_BRACKET, T_CLOSE_CURLY_BRACKET, T_SEMICOLON]; $tokenCodesToSearch = [...array_values(Tokens::$scopeModifiers), ...$endTokenCodes]; do { $previousPointer = TokenHelper::findPrevious($phpcsFile, $tokenCodesToSearch, $previousPointer - 1); if (in_array($tokens[$previousPointer]['code'], $endTokenCodes, true)) { // No visibility modifier found -> public return T_PUBLIC; } if (in_array($tokens[$previousPointer]['code'], [T_PROTECTED_SET, T_PRIVATE_SET], true)) { continue; } return $tokens[$previousPointer]['code']; } while (true); } private function isMemberStatic(File $phpcsFile, int $pointer): bool { $previousPointer = TokenHelper::findPrevious( $phpcsFile, [T_OPEN_CURLY_BRACKET, T_CLOSE_CURLY_BRACKET, T_SEMICOLON, T_STATIC], $pointer - 1, ); return $phpcsFile->getTokens()[$previousPointer]['code'] === T_STATIC; } private function isMethodFinal(File $phpcsFile, int $pointer): bool { $previousPointer = TokenHelper::findPrevious( $phpcsFile, [T_OPEN_CURLY_BRACKET, T_CLOSE_CURLY_BRACKET, T_SEMICOLON, T_FINAL], $pointer - 1, ); return $phpcsFile->getTokens()[$previousPointer]['code'] === T_FINAL; } private function isMethodAbstract(File $phpcsFile, int $pointer): bool { $previousPointer = TokenHelper::findPrevious( $phpcsFile, [T_OPEN_CURLY_BRACKET, T_CLOSE_CURLY_BRACKET, T_SEMICOLON, T_ABSTRACT], $pointer - 1, ); return $phpcsFile->getTokens()[$previousPointer]['code'] === T_ABSTRACT; } private function isStaticConstructor(File $phpcsFile, int $pointer): bool { $parentClassName = $this->getParentClassName($phpcsFile, $pointer); $returnTypeHint = FunctionHelper::findReturnTypeHint($phpcsFile, $pointer); if ($returnTypeHint !== null) { return in_array($returnTypeHint->getTypeHintWithoutNullabilitySymbol(), ['self', $parentClassName], true); } $returnAnnotation = FunctionHelper::findReturnAnnotation($phpcsFile, $pointer); if ($returnAnnotation === null) { return false; } return in_array((string) $returnAnnotation->getValue()->type, ['static', 'self', $parentClassName], true); } private function getParentClassName(File $phpcsFile, int $pointer): string { $classPointer = TokenHelper::findPrevious($phpcsFile, Tokens::$ooScopeTokens, $pointer - 1); assert($classPointer !== null); return ClassHelper::getName($phpcsFile, $classPointer); } private function fixIncorrectGroupOrder( File $file, int $groupFirstMemberPointer, int $groupLastMemberPointer, int $nextGroupMemberPointer ): void { $previousMemberEndPointer = $this->findPreviousMemberEndPointer($file, $groupFirstMemberPointer); $groupStartPointer = $this->findGroupStartPointer($file, $groupFirstMemberPointer, $previousMemberEndPointer); $groupEndPointer = $this->findGroupEndPointer($file, $groupLastMemberPointer); $groupContent = TokenHelper::getContent($file, $groupStartPointer, $groupEndPointer); $nextGroupMemberStartPointer = $this->findGroupStartPointer($file, $nextGroupMemberPointer); $file->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($file, $groupStartPointer, $groupEndPointer); $linesBetween = $this->removeBlankLinesAfterMember($file, $previousMemberEndPointer, $groupStartPointer); $newLines = str_repeat($file->eolChar, $linesBetween); FixerHelper::addBefore($file, $nextGroupMemberStartPointer, $groupContent . $newLines); $file->fixer->endChangeset(); } private function findPreviousMemberEndPointer(File $phpcsFile, int $memberPointer): int { $endTypes = [T_OPEN_CURLY_BRACKET, T_CLOSE_CURLY_BRACKET, T_SEMICOLON]; $previousMemberEndPointer = TokenHelper::findPrevious($phpcsFile, $endTypes, $memberPointer - 1); assert($previousMemberEndPointer !== null); return $previousMemberEndPointer; } private function findGroupStartPointer(File $phpcsFile, int $memberPointer, ?int $previousMemberEndPointer = null): int { $startPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $memberPointer - 1); if ($startPointer === null) { $previousMemberEndPointer ??= $this->findPreviousMemberEndPointer($phpcsFile, $memberPointer); $startPointer = TokenHelper::findNextEffective($phpcsFile, $previousMemberEndPointer + 1); assert($startPointer !== null); } $types = [T_OPEN_CURLY_BRACKET, T_CLOSE_CURLY_BRACKET, T_SEMICOLON]; return (int) $phpcsFile->findFirstOnLine($types, $startPointer, true); } private function findGroupEndPointer(File $phpcsFile, int $memberPointer): int { $tokens = $phpcsFile->getTokens(); if ($tokens[$memberPointer]['code'] === T_FUNCTION && !FunctionHelper::isAbstract($phpcsFile, $memberPointer)) { return $tokens[$memberPointer]['scope_closer']; } if ($tokens[$memberPointer]['code'] === T_USE && array_key_exists('scope_closer', $tokens[$memberPointer])) { return $tokens[$memberPointer]['scope_closer']; } $endPointer = TokenHelper::findNext($phpcsFile, [T_SEMICOLON, T_OPEN_CURLY_BRACKET], $memberPointer + 1); return $tokens[$endPointer]['code'] === T_OPEN_CURLY_BRACKET ? $tokens[$endPointer]['bracket_closer'] : $endPointer; } private function removeBlankLinesAfterMember(File $phpcsFile, int $memberEndPointer, int $endPointer): int { $whitespacePointer = $memberEndPointer; $linesToRemove = 0; while (true) { $whitespacePointer = TokenHelper::findNext($phpcsFile, T_WHITESPACE, $whitespacePointer, $endPointer); if ($whitespacePointer === null) { break; } $linesToRemove++; FixerHelper::replace($phpcsFile, $whitespacePointer, ''); $whitespacePointer++; } return $linesToRemove; } /** * @return array, annotations: array}>> */ private function getNormalizedMethodGroups(): array { if ($this->normalizedMethodGroups === null) { $this->normalizedMethodGroups = []; $methodGroups = SniffSettingsHelper::normalizeAssociativeArray($this->methodGroups); foreach ($methodGroups as $group => $groupDefinition) { $group = strtolower((string) $group); $this->normalizedMethodGroups[$group] = []; $methodDefinitions = preg_split('~\\s*,\\s*~', (string) $groupDefinition, -1, PREG_SPLIT_NO_EMPTY); /** @var list $methodDefinitions */ foreach ($methodDefinitions as $methodDefinition) { $tokens = preg_split('~(?=[#@])~', $methodDefinition); /** @var non-empty-list $tokens */ $method = array_shift($tokens); $methodRequirement = [ 'name' => $method !== '' ? $method : null, 'attributes' => [], 'annotations' => [], ]; foreach ($tokens as $token) { $key = $token[0] === '#' ? 'attributes' : 'annotations'; $methodRequirement[$key][] = substr($token, 1); } $this->normalizedMethodGroups[$group][] = $methodRequirement; } } } return $this->normalizedMethodGroups; } /** * @return array */ private function getNormalizedGroups(): array { if ($this->normalizedGroups === null) { $supportedGroups = [ self::GROUP_USES, self::GROUP_ENUM_CASES, self::GROUP_PUBLIC_CONSTANTS, self::GROUP_PROTECTED_CONSTANTS, self::GROUP_PRIVATE_CONSTANTS, self::GROUP_PUBLIC_PROPERTIES, self::GROUP_PUBLIC_STATIC_PROPERTIES, self::GROUP_PROTECTED_PROPERTIES, self::GROUP_PROTECTED_STATIC_PROPERTIES, self::GROUP_PRIVATE_PROPERTIES, self::GROUP_PRIVATE_STATIC_PROPERTIES, self::GROUP_PUBLIC_STATIC_FINAL_METHODS, self::GROUP_PUBLIC_STATIC_ABSTRACT_METHODS, self::GROUP_PROTECTED_STATIC_FINAL_METHODS, self::GROUP_PROTECTED_STATIC_ABSTRACT_METHODS, self::GROUP_PUBLIC_FINAL_METHODS, self::GROUP_PUBLIC_ABSTRACT_METHODS, self::GROUP_PROTECTED_FINAL_METHODS, self::GROUP_PROTECTED_ABSTRACT_METHODS, self::GROUP_CONSTRUCTOR, self::GROUP_STATIC_CONSTRUCTORS, self::GROUP_DESTRUCTOR, self::GROUP_PUBLIC_METHODS, self::GROUP_PUBLIC_STATIC_METHODS, self::GROUP_PROTECTED_METHODS, self::GROUP_PROTECTED_STATIC_METHODS, self::GROUP_PRIVATE_METHODS, self::GROUP_PRIVATE_STATIC_METHODS, self::GROUP_MAGIC_METHODS, ]; $normalizedMethodGroups = $this->getNormalizedMethodGroups(); $normalizedGroupsWithShortcuts = []; $order = 1; foreach (SniffSettingsHelper::normalizeArray($this->groups) as $groupsString) { /** @var list $groups */ $groups = preg_split('~\\s*,\\s*~', strtolower($groupsString), -1, PREG_SPLIT_NO_EMPTY); foreach ($groups as $groupOrShortcut) { $groupOrShortcut = preg_replace('~\\s+~', ' ', $groupOrShortcut); if ( !in_array($groupOrShortcut, $supportedGroups, true) && !array_key_exists($groupOrShortcut, self::SHORTCUTS) && $groupOrShortcut !== self::GROUP_INVOKE_METHOD && !array_key_exists($groupOrShortcut, $normalizedMethodGroups) ) { throw new UnsupportedClassGroupException($groupOrShortcut); } $normalizedGroupsWithShortcuts[$groupOrShortcut] = $order; } $order++; } $normalizedGroups = []; foreach ($normalizedGroupsWithShortcuts as $groupOrShortcut => $groupOrder) { if ( in_array($groupOrShortcut, $supportedGroups, true) || $groupOrShortcut === self::GROUP_INVOKE_METHOD || array_key_exists($groupOrShortcut, $normalizedMethodGroups) ) { $normalizedGroups[$groupOrShortcut] = $groupOrder; } else { foreach ($this->unpackShortcut($groupOrShortcut, $supportedGroups) as $group) { if ( array_key_exists($group, $normalizedGroupsWithShortcuts) || array_key_exists($group, $normalizedGroups) ) { continue; } $normalizedGroups[$group] = $groupOrder; } } } if ($normalizedGroups === [] && $normalizedMethodGroups === []) { $normalizedGroups = array_flip($supportedGroups); } else { $missingGroups = array_diff( array_merge($supportedGroups, array_keys($normalizedMethodGroups)), array_keys($normalizedGroups), ); if ($missingGroups !== []) { throw new MissingClassGroupsException(array_values($missingGroups)); } } $this->normalizedGroups = $normalizedGroups; } return $this->normalizedGroups; } /** * @param array $supportedGroups * @return array */ private function unpackShortcut(string $shortcut, array $supportedGroups): array { $groups = []; foreach (self::SHORTCUTS[$shortcut] as $groupOrShortcut) { if (in_array($groupOrShortcut, $supportedGroups, true)) { $groups[] = $groupOrShortcut; } elseif ( !array_key_exists($groupOrShortcut, self::SHORTCUTS) && in_array($groupOrShortcut, self::SHORTCUTS[self::GROUP_SHORTCUT_FINAL_METHODS], true) ) { // Nothing } else { $groups = array_merge($groups, $this->unpackShortcut($groupOrShortcut, $supportedGroups)); } } return $groups; } } PK41]&Ucoding-standard/SlevomatCodingStandard/Sniffs/Classes/SuperfluousTraitNamingSniff.phpnu[ */ public function register(): array { return [ T_TRAIT, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $traitPointer */ public function process(File $phpcsFile, $traitPointer): void { $traitName = ClassHelper::getName($phpcsFile, $traitPointer); $this->checkSuffix($phpcsFile, $traitPointer, $traitName); } private function checkSuffix(File $phpcsFile, int $traitPointer, string $traitName): void { $suffix = substr($traitName, -5); if (strtolower($suffix) !== 'trait') { return; } $phpcsFile->addError(sprintf('Superfluous suffix "%s".', $suffix), $traitPointer, self::CODE_SUPERFLUOUS_SUFFIX); } } PK41]ő / /bcoding-standard/SlevomatCodingStandard/Sniffs/Classes/RequireConstructorPropertyPromotionSniff.phpnu[ */ public function register(): array { return [T_FUNCTION]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $functionPointer */ public function process(File $phpcsFile, $functionPointer): void { $this->enable = SniffSettingsHelper::isEnabledByPhpVersion($this->enable, 80000); if (!$this->enable) { return; } $tokens = $phpcsFile->getTokens(); $namePointer = TokenHelper::findNextEffective($phpcsFile, $functionPointer + 1); if (strtolower($tokens[$namePointer]['content']) !== '__construct') { return; } if (FunctionHelper::isAbstract($phpcsFile, $functionPointer)) { return; } $parameterPointers = $this->getParameterPointers($phpcsFile, $functionPointer); if (count($parameterPointers) === 0) { return; } $parameterWithoutPromotionPointers = []; foreach ($parameterPointers as $parameterPointer) { $pointerBefore = TokenHelper::findPrevious($phpcsFile, [T_COMMA, T_OPEN_PARENTHESIS], $parameterPointer - 1); $modifierPointer = TokenHelper::findNextEffective($phpcsFile, $pointerBefore + 1); if (in_array($tokens[$modifierPointer]['code'], TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES, true)) { continue; } $pointerBefore = TokenHelper::findPreviousEffective($phpcsFile, $parameterPointer - 1); if ($tokens[$pointerBefore]['code'] === T_ELLIPSIS) { continue; } if ($tokens[$pointerBefore]['code'] === T_BITWISE_AND) { $pointerBefore = TokenHelper::findPreviousEffective($phpcsFile, $pointerBefore - 1); } if ($tokens[$pointerBefore]['code'] === T_CALLABLE) { continue; } $parameterWithoutPromotionPointers[] = $parameterPointer; } if (count($parameterWithoutPromotionPointers) === 0) { return; } /** @var int $classPointer */ $classPointer = FunctionHelper::findClassPointer($phpcsFile, $functionPointer); $propertyPointers = $this->getPropertyPointers($phpcsFile, $classPointer); if (count($propertyPointers) === 0) { return; } foreach ($parameterWithoutPromotionPointers as $parameterPointer) { $parameterName = $tokens[$parameterPointer]['content']; foreach ($propertyPointers as $propertyPointer) { $propertyName = $tokens[$propertyPointer]['content']; if ($parameterName !== $propertyName) { continue; } $propertyEndPointer = PropertyHelper::getEndPointer($phpcsFile, $propertyPointer); if ($tokens[$propertyEndPointer]['code'] === T_CLOSE_CURLY_BRACKET) { // Ignore property with hooks continue; } if ($this->isPropertyDocCommentUseful($phpcsFile, $propertyPointer)) { continue; } if ($this->isPropertyWithAttribute($phpcsFile, $propertyPointer)) { continue; } $propertyTypeHint = PropertyHelper::findTypeHint($phpcsFile, $propertyPointer); $parameterTypeHint = FunctionHelper::getParametersTypeHints($phpcsFile, $functionPointer)[$parameterName]; if (!$this->areTypeHintEqual($parameterTypeHint, $propertyTypeHint)) { continue; } $assignmentPointer = $this->getAssignment($phpcsFile, $functionPointer, $parameterName); if ($assignmentPointer === null) { continue; } if ($this->isParameterModifiedBeforeAssignment($phpcsFile, $functionPointer, $parameterName, $assignmentPointer)) { continue; } $fix = $phpcsFile->addFixableError( sprintf('Required promotion of property %s.', $propertyName), $propertyPointer, self::CODE_REQUIRED_CONSTRUCTOR_PROPERTY_PROMOTION, ); if (!$fix) { continue; } $propertyDocCommentOpenerPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $propertyPointer); $pointerBeforeProperty = TokenHelper::findFirstTokenOnLine( $phpcsFile, $propertyDocCommentOpenerPointer ?? $propertyPointer, ); $propertyStartPointer = PropertyHelper::getStartPointer($phpcsFile, $propertyPointer); $propertyEndPointer = PropertyHelper::getEndPointer($phpcsFile, $propertyPointer); $modifiersPointers = TokenHelper::findNextAll( $phpcsFile, TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES, $propertyStartPointer, $propertyPointer, ); $modifiers = TokenHelper::getContent($phpcsFile, $modifiersPointers[0], $modifiersPointers[count($modifiersPointers) - 1]); $propertyEqualPointer = TokenHelper::findNext($phpcsFile, T_EQUAL, $propertyPointer + 1, $propertyEndPointer); $propertyDefaultValue = $propertyEqualPointer !== null ? trim(TokenHelper::getContent($phpcsFile, $propertyEqualPointer + 1, $propertyEndPointer - 1)) : null; $propertyEndPointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $propertyPointer + 1); $pointerAfterProperty = TokenHelper::findFirstTokenOnLine( $phpcsFile, TokenHelper::findNextNonWhitespace($phpcsFile, $propertyEndPointer + 1), ); $pointerBeforeParameterStart = TokenHelper::findPrevious( $phpcsFile, [T_COMMA, T_OPEN_PARENTHESIS, T_ATTRIBUTE_END], $parameterPointer - 1, ); $parameterStartPointer = TokenHelper::findNextEffective($phpcsFile, $pointerBeforeParameterStart + 1); $parameterEqualPointer = TokenHelper::findNextEffective($phpcsFile, $parameterPointer + 1); $parameterHasDefaultValue = $tokens[$parameterEqualPointer]['code'] === T_EQUAL; $pointerBeforeAssignment = TokenHelper::findFirstTokenOnLine($phpcsFile, $assignmentPointer - 1); $pointerAfterAssignment = TokenHelper::findLastTokenOnLine($phpcsFile, $assignmentPointer); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $pointerBeforeProperty, $pointerAfterProperty - 1); FixerHelper::addBefore($phpcsFile, $parameterStartPointer, sprintf('%s ', $modifiers)); if (!$parameterHasDefaultValue && $propertyDefaultValue !== null) { FixerHelper::add( $phpcsFile, $parameterPointer, sprintf(' = %s', $propertyDefaultValue), ); } FixerHelper::removeBetweenIncluding($phpcsFile, $pointerBeforeAssignment, $pointerAfterAssignment); $phpcsFile->fixer->endChangeset(); } } } private function getAssignment(File $phpcsFile, int $constructorPointer, string $parameterName): ?int { $tokens = $phpcsFile->getTokens(); $parameterNameWithoutDollar = substr($parameterName, 1); for ($i = $tokens[$constructorPointer]['scope_opener'] + 1; $i < $tokens[$constructorPointer]['scope_closer']; $i++) { if ($tokens[$i]['content'] !== '$this') { continue; } $objectOperatorPointer = TokenHelper::findNextEffective($phpcsFile, $i + 1); if ($tokens[$objectOperatorPointer]['code'] !== T_OBJECT_OPERATOR) { continue; } $namePointer = TokenHelper::findNextEffective($phpcsFile, $objectOperatorPointer + 1); if ($tokens[$namePointer]['content'] !== $parameterNameWithoutDollar) { continue; } $equalPointer = TokenHelper::findNextEffective($phpcsFile, $namePointer + 1); if ($tokens[$equalPointer]['code'] !== T_EQUAL) { continue; } $variablePointer = TokenHelper::findNextEffective($phpcsFile, $equalPointer + 1); if ($tokens[$variablePointer]['content'] !== $parameterName) { continue; } $semicolonPointer = TokenHelper::findNextEffective($phpcsFile, $variablePointer + 1); if ($tokens[$semicolonPointer]['code'] !== T_SEMICOLON) { continue; } foreach (array_reverse($tokens[$semicolonPointer]['conditions']) as $conditionTokenCode) { if (in_array($conditionTokenCode, [T_IF, T_ELSEIF, T_ELSE, T_SWITCH], true)) { return null; } } return $i; } return null; } /** * @return list */ private function getParameterPointers(File $phpcsFile, int $functionPointer): array { $tokens = $phpcsFile->getTokens(); return TokenHelper::findNextAll( $phpcsFile, T_VARIABLE, $tokens[$functionPointer]['parenthesis_opener'] + 1, $tokens[$functionPointer]['parenthesis_closer'], ); } /** * @return list */ private function getPropertyPointers(File $phpcsFile, int $classPointer): array { $tokens = $phpcsFile->getTokens(); return array_values(array_filter( TokenHelper::findNextAll( $phpcsFile, T_VARIABLE, $tokens[$classPointer]['scope_opener'] + 1, $tokens[$classPointer]['scope_closer'], ), static fn (int $variablePointer): bool => PropertyHelper::isProperty($phpcsFile, $variablePointer), )); } private function isPropertyDocCommentUseful(File $phpcsFile, int $propertyPointer): bool { if (DocCommentHelper::hasDocCommentDescription($phpcsFile, $propertyPointer)) { return true; } foreach (AnnotationHelper::getAnnotations($phpcsFile, $propertyPointer) as $annotation) { $annotationValue = $annotation->getValue(); if (!$annotationValue instanceof VarTagValueNode) { return true; } if ($annotationValue->description !== '') { return true; } } return false; } private function isPropertyWithAttribute(File $phpcsFile, int $propertyPointer): bool { $tokens = $phpcsFile->getTokens(); $previousPointer = TokenHelper::findPrevious( $phpcsFile, [T_ATTRIBUTE_END, T_SEMICOLON, T_OPEN_CURLY_BRACKET, T_CLOSE_CURLY_BRACKET], $propertyPointer - 1, ); return $tokens[$previousPointer]['code'] === T_ATTRIBUTE_END; } private function areTypeHintEqual(?TypeHint $parameterTypeHint, ?TypeHint $propertyTypeHint): bool { if ($parameterTypeHint === null && $propertyTypeHint === null) { return true; } if ($parameterTypeHint === null || $propertyTypeHint === null) { return false; } return $parameterTypeHint->getTypeHint() === $propertyTypeHint->getTypeHint(); } private function isParameterModifiedBeforeAssignment( File $phpcsFile, int $functionPointer, string $parameterName, int $assignmentPointer ): bool { $tokens = $phpcsFile->getTokens(); for ($i = $assignmentPointer - 1; $i > $tokens[$functionPointer]['scope_opener']; $i--) { if ($tokens[$i]['code'] !== T_VARIABLE) { continue; } if ($tokens[$i]['content'] !== $parameterName) { continue; } $nextPointer = TokenHelper::findNextEffective($phpcsFile, $i + 1); if (in_array($tokens[$nextPointer]['code'], Tokens::$assignmentTokens, true)) { return true; } if ($tokens[$nextPointer]['code'] === T_INC) { return true; } $previousPointer = TokenHelper::findNextEffective($phpcsFile, $i - 1); if ($tokens[$previousPointer]['code'] === T_DEC) { return true; } } return false; } } PK41][$$Ycoding-standard/SlevomatCodingStandard/Sniffs/Classes/SuperfluousExceptionNamingSniff.phpnu[ */ public function register(): array { return [ T_CLASS, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $classPointer */ public function process(File $phpcsFile, $classPointer): void { $className = ClassHelper::getName($phpcsFile, $classPointer); $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $classPointer - 1); if ($phpcsFile->getTokens()[$previousPointer]['code'] === T_ABSTRACT) { return; } if (strtolower($className) === 'exception') { return; } $suffix = substr($className, -9); if (strtolower($suffix) !== 'exception') { return; } $phpcsFile->addError(sprintf('Superfluous suffix "%s".', $suffix), $classPointer, self::CODE_SUPERFLUOUS_SUFFIX); } } PK41]uJcoding-standard/SlevomatCodingStandard/Sniffs/Classes/ClassLengthSniff.phpnu[ */ public function register(): array { return array_values(Tokens::$ooScopeTokens); } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { $this->maxLinesLength = SniffSettingsHelper::normalizeInteger($this->maxLinesLength); $flags = array_keys(array_filter([ FunctionHelper::LINE_INCLUDE_COMMENT => $this->includeComments, FunctionHelper::LINE_INCLUDE_WHITESPACE => $this->includeWhitespace, ])); $flags = array_reduce($flags, static fn ($carry, $flag): int => $carry | $flag, 0); $length = FunctionHelper::getLineCount($phpcsFile, $pointer, $flags); if ($length <= $this->maxLinesLength) { return; } $errorMessage = sprintf('Your class is too long. Currently using %d lines. Can be up to %d lines.', $length, $this->maxLinesLength); $phpcsFile->addError($errorMessage, $pointer, self::CODE_CLASS_TOO_LONG); } } PK41]K\^coding-standard/SlevomatCodingStandard/Sniffs/Classes/RequireMultiLineMethodSignatureSniff.phpnu[ */ public array $includedMethodPatterns = []; /** @var list|null */ public ?array $includedMethodNormalizedPatterns = null; /** @var list */ public array $excludedMethodPatterns = []; /** @var list|null */ public ?array $excludedMethodNormalizedPatterns = null; /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $methodPointer */ public function process(File $phpcsFile, $methodPointer): void { $this->minLineLength = SniffSettingsHelper::normalizeNullableInteger($this->minLineLength); $this->minParametersCount = SniffSettingsHelper::normalizeNullableInteger($this->minParametersCount); if ($this->minLineLength !== null && $this->minParametersCount !== null) { throw new UnexpectedValueException('Either minLineLength or minParametersCount can be set.'); } // Maintain backward compatibility if no configuration provided if ($this->minLineLength === null && $this->minParametersCount === null) { $this->minLineLength = self::DEFAULT_MIN_LINE_LENGTH; } if (!FunctionHelper::isMethod($phpcsFile, $methodPointer)) { return; } $tokens = $phpcsFile->getTokens(); [$signatureStartPointer, $signatureEndPointer] = $this->getSignatureStartAndEndPointers($phpcsFile, $methodPointer); if ($tokens[$signatureStartPointer]['line'] < $tokens[$signatureEndPointer]['line']) { return; } $parameters = $phpcsFile->getMethodParameters($methodPointer); $parametersCount = count($parameters); if ($parametersCount === 0) { return; } $signature = $this->getSignature($phpcsFile, $signatureStartPointer, $signatureEndPointer); $methodName = FunctionHelper::getName($phpcsFile, $methodPointer); if ( count($this->includedMethodPatterns) !== 0 && !$this->isMethodNameInPatterns($methodName, $this->getIncludedMethodNormalizedPatterns()) ) { return; } if ( count($this->excludedMethodPatterns) !== 0 && $this->isMethodNameInPatterns($methodName, $this->getExcludedMethodNormalizedPatterns()) ) { return; } $splitPromotedProperties = false; if ($this->withPromotedProperties) { foreach ($parameters as $parameter) { if (isset($parameter['property_visibility'])) { $splitPromotedProperties = true; break; } } } if (!$splitPromotedProperties) { if ($this->minLineLength !== null && $this->minLineLength !== 0 && strlen($signature) < $this->minLineLength) { return; } if ($this->minParametersCount !== null && $parametersCount < $this->minParametersCount) { return; } } $error = sprintf('Signature of method "%s" should be split to more lines so each parameter is on its own line.', $methodName); $fix = $phpcsFile->addFixableError($error, $methodPointer, self::CODE_REQUIRED_MULTI_LINE_SIGNATURE); if (!$fix) { return; } $indentation = $tokens[$signatureStartPointer]['content']; $phpcsFile->fixer->beginChangeset(); foreach ($parameters as $parameter) { $pointerBeforeParameter = TokenHelper::findPrevious( $phpcsFile, T_COMMA, $parameter['token'] - 1, $tokens[$methodPointer]['parenthesis_opener'], ); $pointerBeforeParameter ??= $tokens[$methodPointer]['parenthesis_opener']; FixerHelper::add( $phpcsFile, $pointerBeforeParameter, $phpcsFile->eolChar . IndentationHelper::addIndentation($phpcsFile, $indentation), ); FixerHelper::removeWhitespaceAfter($phpcsFile, $pointerBeforeParameter); } FixerHelper::addBefore($phpcsFile, $tokens[$methodPointer]['parenthesis_closer'], $phpcsFile->eolChar . $indentation); $phpcsFile->fixer->endChangeset(); } /** * @param list $normalizedPatterns */ private function isMethodNameInPatterns(string $methodName, array $normalizedPatterns): bool { foreach ($normalizedPatterns as $pattern) { if (!SniffSettingsHelper::isValidRegularExpression($pattern)) { throw new Exception(sprintf('%s is not valid PCRE pattern.', $pattern)); } if (preg_match($pattern, $methodName) !== 0) { return true; } } return false; } /** * @return list */ private function getIncludedMethodNormalizedPatterns(): array { $this->includedMethodNormalizedPatterns ??= SniffSettingsHelper::normalizeArray($this->includedMethodPatterns); return $this->includedMethodNormalizedPatterns; } /** * @return list */ private function getExcludedMethodNormalizedPatterns(): array { $this->excludedMethodNormalizedPatterns ??= SniffSettingsHelper::normalizeArray($this->excludedMethodPatterns); return $this->excludedMethodNormalizedPatterns; } } PK41]O^coding-standard/SlevomatCodingStandard/Sniffs/Classes/DisallowMultiPropertyDefinitionSniff.phpnu[ */ public function register(): array { return TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $modifierPointer */ public function process(File $phpcsFile, $modifierPointer): void { $tokens = $phpcsFile->getTokens(); $asPointer = TokenHelper::findPreviousEffective($phpcsFile, $modifierPointer - 1); if ($tokens[$asPointer]['code'] === T_AS) { return; } $nextPointer = TokenHelper::findNextEffective($phpcsFile, $modifierPointer + 1); if (in_array($tokens[$nextPointer]['code'], TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES, true)) { // We don't want to report the same property multiple times return; } // Ignore other class members with same mofidiers $propertyPointer = TokenHelper::findNext($phpcsFile, [T_VARIABLE, T_CONST, T_FUNCTION, T_CLASS], $modifierPointer + 1); if ( $propertyPointer === null || $tokens[$propertyPointer]['code'] !== T_VARIABLE || !PropertyHelper::isProperty($phpcsFile, $propertyPointer) ) { return; } $semicolonPointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $propertyPointer + 1); $commaPointers = []; $nextPointer = $propertyPointer; do { $nextPointer = TokenHelper::findNext($phpcsFile, [T_COMMA, T_OPEN_SHORT_ARRAY, T_ARRAY], $nextPointer + 1, $semicolonPointer); if ($nextPointer === null) { break; } if ($tokens[$nextPointer]['code'] === T_OPEN_SHORT_ARRAY) { $nextPointer = $tokens[$nextPointer]['bracket_closer']; continue; } if ($tokens[$nextPointer]['code'] === T_ARRAY) { $nextPointer = $tokens[$nextPointer]['parenthesis_closer']; continue; } $commaPointers[] = $nextPointer; } while (true); if (count($commaPointers) === 0) { return; } $fix = $phpcsFile->addFixableError( 'Use of multi property definition is disallowed.', $modifierPointer, self::CODE_DISALLOWED_MULTI_PROPERTY_DEFINITION, ); if (!$fix) { return; } $propertyStartPointer = PropertyHelper::getStartPointer($phpcsFile, $propertyPointer); $pointerBeforeProperty = TokenHelper::findPreviousEffective($phpcsFile, $propertyPointer - 1); $pointerBeforeSemicolon = TokenHelper::findPreviousEffective($phpcsFile, $semicolonPointer - 1); $indentation = IndentationHelper::getIndentation($phpcsFile, $propertyStartPointer); $docCommentPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $propertyPointer); $docComment = $docCommentPointer !== null ? trim(TokenHelper::getContent($phpcsFile, $docCommentPointer, $tokens[$docCommentPointer]['comment_closer'])) : null; $data = []; foreach ($commaPointers as $commaPointer) { $data[$commaPointer] = [ 'pointerBeforeComma' => TokenHelper::findPreviousEffective($phpcsFile, $commaPointer - 1), 'pointerAfterComma' => TokenHelper::findNextEffective($phpcsFile, $commaPointer + 1), ]; } $propertyContent = TokenHelper::getContent($phpcsFile, $propertyStartPointer, $pointerBeforeProperty); $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $pointerBeforeProperty + 1, $propertyPointer - 1, ' '); foreach ($commaPointers as $commaPointer) { FixerHelper::removeBetween($phpcsFile, $data[$commaPointer]['pointerBeforeComma'], $commaPointer); FixerHelper::replace( $phpcsFile, $commaPointer, sprintf( ';%s%s%s%s ', $phpcsFile->eolChar, $docComment !== null ? sprintf('%s%s%s', $indentation, $docComment, $phpcsFile->eolChar) : '', $indentation, $propertyContent, ), ); FixerHelper::removeBetween($phpcsFile, $commaPointer, $data[$commaPointer]['pointerAfterComma']); } FixerHelper::removeBetween($phpcsFile, $pointerBeforeSemicolon, $semicolonPointer); $phpcsFile->fixer->endChangeset(); } } PK41]eYu u Ncoding-standard/SlevomatCodingStandard/Sniffs/Classes/PropertySpacingSniff.phpnu[ */ public function register(): array { return TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): int { $tokens = $phpcsFile->getTokens(); $asPointer = TokenHelper::findPreviousEffective($phpcsFile, $pointer - 1); if ($tokens[$asPointer]['code'] === T_AS) { return $pointer; } $nextPointer = TokenHelper::findNextEffective($phpcsFile, $pointer + 1); if (in_array($tokens[$nextPointer]['code'], TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES, true)) { // We don't want to report the same property multiple times return $nextPointer; } // Ignore other class members with same mofidiers $propertyPointer = TokenHelper::findNext($phpcsFile, [T_VARIABLE, T_FUNCTION, T_CONST, T_CLASS], $pointer + 1); if ( $propertyPointer === null || $tokens[$propertyPointer]['code'] !== T_VARIABLE || !PropertyHelper::isProperty($phpcsFile, $propertyPointer) ) { return $propertyPointer ?? $pointer; } return parent::process($phpcsFile, $propertyPointer); } protected function isNextMemberValid(File $phpcsFile, int $pointer): bool { $nextPointer = TokenHelper::findNext($phpcsFile, [T_FUNCTION, T_VARIABLE], $pointer + 1); return $nextPointer !== null && $phpcsFile->getTokens()[$nextPointer]['code'] === T_VARIABLE; } protected function addError(File $phpcsFile, int $pointer, int $minExpectedLines, int $maxExpectedLines, int $found): bool { if ($minExpectedLines === $maxExpectedLines) { $errorMessage = $minExpectedLines === 1 ? 'Expected 1 blank line after property, found %3$d.' : 'Expected %2$d blank lines after property, found %3$d.'; } else { $errorMessage = 'Expected %1$d to %2$d blank lines after property, found %3$d.'; } $error = sprintf($errorMessage, $minExpectedLines, $maxExpectedLines, $found); return $phpcsFile->addFixableError($error, $pointer, self::CODE_INCORRECT_COUNT_OF_BLANK_LINES_AFTER_PROPERTY); } } PK41]~$::]coding-standard/SlevomatCodingStandard/Sniffs/Classes/SuperfluousAbstractClassNamingSniff.phpnu[ */ public function register(): array { return [ T_CLASS, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $classPointer */ public function process(File $phpcsFile, $classPointer): void { $className = ClassHelper::getName($phpcsFile, $classPointer); $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $classPointer - 1); if ($phpcsFile->getTokens()[$previousPointer]['code'] !== T_ABSTRACT) { return; } $this->checkPrefix($phpcsFile, $classPointer, $className); $this->checkSuffix($phpcsFile, $classPointer, $className); } private function checkPrefix(File $phpcsFile, int $classPointer, string $className): void { $prefix = substr($className, 0, 8); if (strtolower($prefix) !== 'abstract') { return; } $phpcsFile->addError(sprintf('Superfluous prefix "%s".', $prefix), $classPointer, self::CODE_SUPERFLUOUS_PREFIX); } private function checkSuffix(File $phpcsFile, int $classPointer, string $className): void { $suffix = substr($className, -8); if (strtolower($suffix) !== 'abstract') { return; } $phpcsFile->addError(sprintf('Superfluous suffix "%s".', $suffix), $classPointer, self::CODE_SUPERFLUOUS_SUFFIX); } } PK41]kn| | Pcoding-standard/SlevomatCodingStandard/Sniffs/Classes/ParentCallSpacingSniff.phpnu[linesCountBefore = SniffSettingsHelper::normalizeInteger($this->linesCountBefore); $this->linesCountBeforeFirst = SniffSettingsHelper::normalizeInteger($this->linesCountBeforeFirst); $this->linesCountAfter = SniffSettingsHelper::normalizeInteger($this->linesCountAfter); $this->linesCountAfterLast = SniffSettingsHelper::normalizeInteger($this->linesCountAfterLast); $tokens = $phpcsFile->getTokens(); if (array_key_exists('nested_parenthesis', $tokens[$parentPointer])) { return; } $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $parentPointer - 1); if (in_array($tokens[$previousPointer]['code'], array_merge(Tokens::$castTokens, [T_ASPERAND]), true)) { $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $previousPointer - 1); } $tokensToIgnore = array_merge( Tokens::$assignmentTokens, Tokens::$equalityTokens, Tokens::$booleanOperators, [T_RETURN, T_YIELD, T_YIELD_FROM, T_COLON, T_STRING_CONCAT, T_INLINE_THEN, T_INLINE_ELSE, T_COALESCE, T_MATCH_ARROW], ); if (in_array($tokens[$previousPointer]['code'], $tokensToIgnore, true)) { return; } $previousShortArrayOpenerPointer = TokenHelper::findPrevious($phpcsFile, T_OPEN_SHORT_ARRAY, $parentPointer - 1); if ($previousShortArrayOpenerPointer !== null && $tokens[$previousShortArrayOpenerPointer]['bracket_closer'] > $parentPointer) { return; } parent::process($phpcsFile, $parentPointer); } /** * @return list */ protected function getSupportedKeywords(): array { return [self::KEYWORD_PARENT]; } /** * @return list */ protected function getKeywordsToCheck(): array { return [self::KEYWORD_PARENT]; } protected function getLinesCountBefore(): int { return $this->linesCountBefore; } /** * @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter */ protected function getLinesCountBeforeFirst(File $phpcsFile, int $parentPointer): int { return $this->linesCountBeforeFirst; } protected function getLinesCountAfter(): int { return $this->linesCountAfter; } /** * @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter */ protected function getLinesCountAfterLast(File $phpcsFile, int $parentPointer, int $parentEndPointer): int { return $this->linesCountAfterLast; } } PK41][>|Lcoding-standard/SlevomatCodingStandard/Sniffs/Classes/MethodSpacingSniff.phpnu[ */ public function register(): array { return [T_FUNCTION]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $methodPointer */ public function process(File $phpcsFile, $methodPointer): void { $this->minLinesCount = SniffSettingsHelper::normalizeInteger($this->minLinesCount); $this->maxLinesCount = SniffSettingsHelper::normalizeInteger($this->maxLinesCount); if (!FunctionHelper::isMethod($phpcsFile, $methodPointer)) { return; } $tokens = $phpcsFile->getTokens(); $methodEndPointer = array_key_exists('scope_closer', $tokens[$methodPointer]) ? $tokens[$methodPointer]['scope_closer'] : TokenHelper::findNext($phpcsFile, T_SEMICOLON, $methodPointer + 1); $classPointer = ClassHelper::getClassPointer($phpcsFile, $methodPointer); $nextMethodPointer = TokenHelper::findNext($phpcsFile, T_FUNCTION, $methodEndPointer + 1, $tokens[$classPointer]['scope_closer']); if ($nextMethodPointer === null) { return; } $nextMethodAttributeStartPointer = null; $nextMethodDocCommentStartPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $nextMethodPointer); if ( $nextMethodDocCommentStartPointer !== null && $tokens[$tokens[$nextMethodDocCommentStartPointer]['comment_closer']]['line'] + 1 !== $tokens[$nextMethodPointer]['line'] ) { $nextMethodDocCommentStartPointer = null; } else { $nextMethodAttributeStartPointer = TokenHelper::findPrevious( $phpcsFile, T_ATTRIBUTE, $nextMethodPointer - 1, $methodEndPointer, ); if ($nextMethodAttributeStartPointer !== null) { do { $pointerBefore = TokenHelper::findPreviousNonWhitespace( $phpcsFile, $nextMethodAttributeStartPointer - 1, $methodEndPointer, ); if ($tokens[$pointerBefore]['code'] === T_ATTRIBUTE_END) { $nextMethodAttributeStartPointer = $tokens[$pointerBefore]['attribute_opener']; continue; } break; } while (true); } } $nextMethodFirstLinePointer = $tokens[$nextMethodPointer]['line'] === $tokens[$methodEndPointer]['line'] ? TokenHelper::findNextEffective($phpcsFile, $methodEndPointer + 1) : TokenHelper::findFirstTokenOnLine( $phpcsFile, $nextMethodDocCommentStartPointer ?? $nextMethodAttributeStartPointer ?? $nextMethodPointer, ); if (TokenHelper::findNextNonWhitespace($phpcsFile, $methodEndPointer + 1, $nextMethodFirstLinePointer) !== null) { return; } $linesBetween = $tokens[$nextMethodFirstLinePointer]['line'] !== $tokens[$methodEndPointer]['line'] ? $tokens[$nextMethodFirstLinePointer]['line'] - $tokens[$methodEndPointer]['line'] - 1 : null; if ($linesBetween !== null && $linesBetween >= $this->minLinesCount && $linesBetween <= $this->maxLinesCount) { return; } if ($this->minLinesCount === $this->maxLinesCount) { $errorMessage = $this->minLinesCount === 1 ? 'Expected 1 blank line after method, found %3$d.' : 'Expected %2$d blank lines after method, found %3$d.'; } else { $errorMessage = 'Expected %1$d to %2$d blank lines after method, found %3$d.'; } $fix = $phpcsFile->addFixableError( sprintf($errorMessage, $this->minLinesCount, $this->maxLinesCount, $linesBetween ?? 0), $methodPointer, self::CODE_INCORRECT_LINES_COUNT_BETWEEN_METHODS, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); if ($linesBetween === null) { FixerHelper::add( $phpcsFile, $methodEndPointer, $phpcsFile->eolChar . str_repeat($phpcsFile->eolChar, $this->minLinesCount) . IndentationHelper::getIndentation( $phpcsFile, TokenHelper::findFirstNonWhitespaceOnLine($phpcsFile, $methodPointer), ), ); FixerHelper::removeBetween($phpcsFile, $methodEndPointer, $nextMethodFirstLinePointer); } elseif ($linesBetween > $this->maxLinesCount) { FixerHelper::add( $phpcsFile, $methodEndPointer, str_repeat($phpcsFile->eolChar, $this->maxLinesCount + 1), ); $firstPointerOnNextMethodLine = TokenHelper::findFirstTokenOnLine($phpcsFile, $nextMethodFirstLinePointer); FixerHelper::removeBetween($phpcsFile, $methodEndPointer, $firstPointerOnNextMethodLine); } else { FixerHelper::add( $phpcsFile, $methodEndPointer, str_repeat($phpcsFile->eolChar, $this->minLinesCount - $linesBetween), ); } $phpcsFile->fixer->endChangeset(); } } PK41]Jcgg^coding-standard/SlevomatCodingStandard/Sniffs/Classes/DisallowMultiConstantDefinitionSniff.phpnu[ */ public function register(): array { return [T_CONST]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $constantPointer */ public function process(File $phpcsFile, $constantPointer): void { $tokens = $phpcsFile->getTokens(); $semicolonPointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $constantPointer + 1); $commaPointers = []; $nextPointer = $constantPointer; do { $nextPointer = TokenHelper::findNext($phpcsFile, [T_COMMA, T_OPEN_SHORT_ARRAY], $nextPointer + 1, $semicolonPointer); if ($nextPointer === null) { break; } if ($tokens[$nextPointer]['code'] === T_OPEN_SHORT_ARRAY) { $nextPointer = $tokens[$nextPointer]['bracket_closer']; continue; } $commaPointers[] = $nextPointer; } while (true); if (count($commaPointers) === 0) { return; } $fix = $phpcsFile->addFixableError( 'Use of multi constant definition is disallowed.', $constantPointer, self::CODE_DISALLOWED_MULTI_CONSTANT_DEFINITION, ); if (!$fix) { return; } $possibleVisibilityPointer = TokenHelper::findPreviousEffective($phpcsFile, $constantPointer - 1); $visibilityPointer = in_array($tokens[$possibleVisibilityPointer]['code'], Tokens::$scopeModifiers, true) ? $possibleVisibilityPointer : null; $visibility = $visibilityPointer !== null ? $tokens[$possibleVisibilityPointer]['content'] : null; $pointerAfterConst = TokenHelper::findNextEffective($phpcsFile, $constantPointer + 1); $pointerBeforeSemicolon = TokenHelper::findPreviousEffective($phpcsFile, $semicolonPointer - 1); $indentation = IndentationHelper::getIndentation($phpcsFile, $visibilityPointer ?? $constantPointer); $docCommentPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $constantPointer); $docComment = $docCommentPointer !== null ? trim(TokenHelper::getContent($phpcsFile, $docCommentPointer, $tokens[$docCommentPointer]['comment_closer'])) : null; $data = []; foreach ($commaPointers as $commaPointer) { $data[$commaPointer] = [ 'pointerBeforeComma' => TokenHelper::findPreviousEffective($phpcsFile, $commaPointer - 1), 'pointerAfterComma' => TokenHelper::findNextEffective($phpcsFile, $commaPointer + 1), ]; } $phpcsFile->fixer->beginChangeset(); FixerHelper::add($phpcsFile, $constantPointer, ' '); FixerHelper::removeBetween($phpcsFile, $constantPointer, $pointerAfterConst); foreach ($commaPointers as $commaPointer) { FixerHelper::removeBetween($phpcsFile, $data[$commaPointer]['pointerBeforeComma'], $commaPointer); FixerHelper::replace( $phpcsFile, $commaPointer, sprintf( ';%s%s%s%sconst ', $phpcsFile->eolChar, $docComment !== null ? sprintf('%s%s%s', $indentation, $docComment, $phpcsFile->eolChar) : '', $indentation, $visibility !== null ? sprintf('%s ', $visibility) : '', ), ); FixerHelper::removeBetween($phpcsFile, $commaPointer, $data[$commaPointer]['pointerAfterComma']); } FixerHelper::removeBetween($phpcsFile, $pointerBeforeSemicolon, $semicolonPointer); $phpcsFile->fixer->endChangeset(); } } PK41] vWcoding-standard/SlevomatCodingStandard/Sniffs/Classes/ModernClassNameReferenceSniff.phpnu[ */ public function register(): array { return [ T_CLASS_C, ...TokenHelper::ONLY_NAME_TOKEN_CODES, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { $this->enableOnObjects = SniffSettingsHelper::isEnabledByPhpVersion($this->enableOnObjects, 80000); $tokens = $phpcsFile->getTokens(); if ($tokens[$pointer]['code'] === T_CLASS_C) { $this->checkMagicConstant($phpcsFile, $pointer); return; } $this->checkFunctionCall($phpcsFile, $pointer); } private function checkMagicConstant(File $phpcsFile, int $pointer): void { $fix = $phpcsFile->addFixableError( 'Class name referenced via magic constant.', $pointer, self::CODE_CLASS_NAME_REFERENCED_VIA_MAGIC_CONSTANT, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $pointer, 'self::class'); $phpcsFile->fixer->endChangeset(); } private function checkFunctionCall(File $phpcsFile, int $functionPointer): void { $tokens = $phpcsFile->getTokens(); $functionName = ltrim(strtolower($tokens[$functionPointer]['content']), '\\'); $functionNames = [ 'get_class', 'get_parent_class', 'get_called_class', ]; if (!in_array($functionName, $functionNames, true)) { return; } $openParenthesisPointer = TokenHelper::findNextEffective($phpcsFile, $functionPointer + 1); if ($tokens[$openParenthesisPointer]['code'] !== T_OPEN_PARENTHESIS) { return; } $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $functionPointer - 1); if (in_array($tokens[$previousPointer]['code'], [T_OBJECT_OPERATOR, T_DOUBLE_COLON, T_FUNCTION], true)) { return; } $parameterPointer = TokenHelper::findNextEffective( $phpcsFile, $openParenthesisPointer + 1, $tokens[$openParenthesisPointer]['parenthesis_closer'], ); $isObjectParameter = static function () use ($phpcsFile, $tokens, $openParenthesisPointer, $parameterPointer): bool { if ($tokens[$parameterPointer]['code'] !== T_VARIABLE) { return false; } $pointerAfterParameterPointer = TokenHelper::findNextEffective($phpcsFile, $parameterPointer + 1); return $pointerAfterParameterPointer === $tokens[$openParenthesisPointer]['parenthesis_closer']; }; $isThisParameter = static function () use ($tokens, $parameterPointer, $isObjectParameter): bool { if (!$isObjectParameter()) { return false; } $parameterName = strtolower($tokens[$parameterPointer]['content']); return $parameterName === '$this'; }; if ($functionName === 'get_class') { if ($parameterPointer === null) { $fixedContent = 'self::class'; } elseif ($isThisParameter()) { $fixedContent = 'static::class'; } elseif ($this->enableOnObjects && $isObjectParameter()) { $fixedContent = sprintf('%s::class', $tokens[$parameterPointer]['content']); } else { return; } } elseif ($functionName === 'get_parent_class') { if ($parameterPointer !== null) { if (!$isThisParameter()) { return; } $classPointer = FunctionHelper::findClassPointer($phpcsFile, $functionPointer); if ($classPointer === null || !ClassHelper::isFinal($phpcsFile, $classPointer)) { return; } } $fixedContent = 'parent::class'; } else { $fixedContent = 'static::class'; } $fix = $phpcsFile->addFixableError( sprintf('Class name referenced via call of function %s().', $functionName), $functionPointer, self::CODE_CLASS_NAME_REFERENCED_VIA_FUNCTION_CALL, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); if ($tokens[$functionPointer - 1]['code'] === T_NS_SEPARATOR) { FixerHelper::replace($phpcsFile, $functionPointer - 1, ''); } FixerHelper::change($phpcsFile, $functionPointer, $tokens[$openParenthesisPointer]['parenthesis_closer'], $fixedContent); $phpcsFile->fixer->endChangeset(); } } PK41]O<Ucoding-standard/SlevomatCodingStandard/Sniffs/Classes/SuperfluousErrorNamingSniff.phpnu[ */ public function register(): array { return [ T_CLASS, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $classPointer */ public function process(File $phpcsFile, $classPointer): void { $className = ClassHelper::getName($phpcsFile, $classPointer); $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $classPointer - 1); if ($phpcsFile->getTokens()[$previousPointer]['code'] === T_ABSTRACT) { return; } if (strtolower($className) === 'error') { return; } $suffix = substr($className, -5); if (strtolower($suffix) !== 'error') { return; } $phpcsFile->addError(sprintf('Superfluous suffix "%s".', $suffix), $classPointer, self::CODE_SUPERFLUOUS_SUFFIX); } } PK41],Ucoding-standard/SlevomatCodingStandard/Sniffs/Classes/RequireAbstractOrFinalSniff.phpnu[ */ public function register(): array { return [ T_CLASS, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $classPointer */ public function process(File $phpcsFile, $classPointer): void { $tokens = $phpcsFile->getTokens(); $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $classPointer - 1); if ($tokens[$previousPointer]['code'] === T_READONLY) { $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $previousPointer - 1); } if (in_array($tokens[$previousPointer]['code'], [T_ABSTRACT, T_FINAL], true)) { return; } $fix = $phpcsFile->addFixableError( 'All classes should be declared using either the "abstract" or "final" keyword.', $classPointer, self::CODE_NO_ABSTRACT_OR_FINAL, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::addBefore($phpcsFile, $classPointer, 'final '); $phpcsFile->fixer->endChangeset(); } } PK41]+0ccoding-standard/SlevomatCodingStandard/Sniffs/Classes/DisallowConstructorPropertyPromotionSniff.phpnu[ */ public function register(): array { return [T_FUNCTION]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $functionPointer */ public function process(File $phpcsFile, $functionPointer): void { $tokens = $phpcsFile->getTokens(); $namePointer = TokenHelper::findNextEffective($phpcsFile, $functionPointer + 1); if (strtolower($tokens[$namePointer]['content']) !== '__construct') { return; } $modifierPointers = TokenHelper::findNextAll( $phpcsFile, [...array_values(Tokens::$scopeModifiers), T_READONLY], $tokens[$functionPointer]['parenthesis_opener'] + 1, $tokens[$functionPointer]['parenthesis_closer'], ); if ($modifierPointers === []) { return; } foreach ($modifierPointers as $modifierPointer) { $variablePointer = TokenHelper::findNext($phpcsFile, T_VARIABLE, $modifierPointer + 1); $phpcsFile->addError( sprintf( 'Constructor property promotion is disallowed, promotion of property %s found.', $tokens[$variablePointer]['content'], ), $variablePointer, self::CODE_DISALLOWED_CONSTRUCTOR_PROPERTY_PROMOTION, ); } } } PK41]; Rcoding-standard/SlevomatCodingStandard/Sniffs/Classes/TraitUseDeclarationSniff.phpnu[ */ public function register(): array { return [ T_CLASS, T_ANON_CLASS, T_TRAIT, T_ENUM, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $classPointer */ public function process(File $phpcsFile, $classPointer): void { $usePointers = ClassHelper::getTraitUsePointers($phpcsFile, $classPointer); foreach ($usePointers as $usePointer) { $this->checkDeclaration($phpcsFile, $usePointer); } } private function checkDeclaration(File $phpcsFile, int $usePointer): void { $commaPointer = TokenHelper::findNextLocal($phpcsFile, T_COMMA, $usePointer + 1); if ($commaPointer === null) { return; } $endPointer = TokenHelper::findNext($phpcsFile, [T_OPEN_CURLY_BRACKET, T_SEMICOLON], $usePointer + 1); $tokens = $phpcsFile->getTokens(); if ($tokens[$endPointer]['code'] === T_OPEN_CURLY_BRACKET) { $phpcsFile->addError( 'Multiple traits per use statement are forbidden.', $usePointer, self::CODE_MULTIPLE_TRAITS_PER_DECLARATION, ); return; } $fix = $phpcsFile->addFixableError( 'Multiple traits per use statement are forbidden.', $usePointer, self::CODE_MULTIPLE_TRAITS_PER_DECLARATION, ); if (!$fix) { return; } $indentation = ''; $currentPointer = $usePointer - 1; while ( $tokens[$currentPointer]['code'] === T_WHITESPACE && $tokens[$currentPointer]['content'] !== $phpcsFile->eolChar ) { $indentation .= $tokens[$currentPointer]['content']; $currentPointer--; } $phpcsFile->fixer->beginChangeset(); $otherCommaPointers = TokenHelper::findNextAll($phpcsFile, T_COMMA, $usePointer + 1, $endPointer); foreach ($otherCommaPointers as $otherCommaPointer) { $pointerAfterComma = TokenHelper::findNextEffective($phpcsFile, $otherCommaPointer + 1); FixerHelper::change( $phpcsFile, $otherCommaPointer, $pointerAfterComma - 1, sprintf(';%s%suse ', $phpcsFile->eolChar, $indentation), ); } $phpcsFile->fixer->endChangeset(); } } PK41]&:y y Tcoding-standard/SlevomatCodingStandard/Sniffs/Classes/BackedEnumTypeSpacingSniff.phpnu[ */ public function register(): array { return [T_ENUM]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $enumPointer */ public function process(File $phpcsFile, $enumPointer): void { $this->spacesCountBeforeColon = SniffSettingsHelper::normalizeInteger($this->spacesCountBeforeColon); $this->spacesCountBeforeType = SniffSettingsHelper::normalizeInteger($this->spacesCountBeforeType); $tokens = $phpcsFile->getTokens(); $colonPointer = TokenHelper::findNext($phpcsFile, T_COLON, $enumPointer + 1, $tokens[$enumPointer]['scope_opener']); if ($colonPointer === null) { return; } $this->checkSpacesBeforeColon($phpcsFile, $colonPointer); $this->checkSpacesBeforeType($phpcsFile, $colonPointer); } public function checkSpacesBeforeColon(File $phpcsFile, int $colonPointer): void { $namePointer = TokenHelper::findPreviousEffective($phpcsFile, $colonPointer - 1); $whitespace = TokenHelper::getContent($phpcsFile, $namePointer + 1, $colonPointer - 1); if ($this->spacesCountBeforeColon === strlen($whitespace)) { return; } $fix = $phpcsFile->addFixableError( $this->formatErrorMessage('before colon', $this->spacesCountBeforeColon), $colonPointer, self::CODE_INCORRECT_SPACES_BEFORE_COLON, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetween($phpcsFile, $namePointer, $colonPointer); FixerHelper::addBefore($phpcsFile, $colonPointer, str_repeat(' ', $this->spacesCountBeforeColon)); $phpcsFile->fixer->endChangeset(); } public function checkSpacesBeforeType(File $phpcsFile, int $colonPointer): void { $typePointer = TokenHelper::findNextEffective($phpcsFile, $colonPointer + 1); $whitespace = TokenHelper::getContent($phpcsFile, $colonPointer + 1, $typePointer - 1); if ($this->spacesCountBeforeType === strlen($whitespace)) { return; } $fix = $phpcsFile->addFixableError( $this->formatErrorMessage('before type', $this->spacesCountBeforeType), $typePointer, self::CODE_INCORRECT_SPACES_BEFORE_TYPE, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetween($phpcsFile, $colonPointer, $typePointer); FixerHelper::addBefore($phpcsFile, $typePointer, str_repeat(' ', $this->spacesCountBeforeType)); $phpcsFile->fixer->endChangeset(); } private function formatErrorMessage(string $suffix, int $requiredSpaces): string { return $requiredSpaces === 0 ? sprintf('There must be no whitespace %s.', $suffix) : sprintf('There must be exactly %d whitespace%s %s.', $requiredSpaces, $requiredSpaces !== 1 ? 's' : '', $suffix); } } PK41] {< Scoding-standard/SlevomatCodingStandard/Sniffs/Classes/RequireSelfReferenceSniff.phpnu[ */ public function register(): array { return [ T_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $openTagPointer */ public function process(File $phpcsFile, $openTagPointer): void { if (TokenHelper::findPrevious($phpcsFile, T_OPEN_TAG, $openTagPointer - 1) !== null) { return; } $tokens = $phpcsFile->getTokens(); $referencedNames = array_merge( ReferencedNameHelper::getAllReferencedNames($phpcsFile, $openTagPointer), ReferencedNameHelper::getAllReferencedNamesInAttributes($phpcsFile, $openTagPointer), ); foreach ($referencedNames as $referencedName) { if (!$referencedName->isClass()) { continue; } $anonymousClassPointer = TokenHelper::findPrevious($phpcsFile, T_ANON_CLASS, $referencedName->getStartPointer() - 1); if ( $anonymousClassPointer !== null && $tokens[$anonymousClassPointer]['scope_closer'] > $referencedName->getEndPointer() ) { continue; } $classPointer = ClassHelper::getClassPointer($phpcsFile, $referencedName->getStartPointer()); if ($classPointer === null) { continue; } $className = ClassHelper::getFullyQualifiedName($phpcsFile, $classPointer); $resolvedName = NamespaceHelper::resolveClassName( $phpcsFile, $referencedName->getNameAsReferencedInFile(), $referencedName->getStartPointer(), ); if ($className !== $resolvedName) { continue; } $fix = $phpcsFile->addFixableError( '"self" for local reference is required.', $referencedName->getStartPointer(), self::CODE_REQUIRED_SELF_REFERENCE, ); if (!$fix) { continue; } $inAttribute = $tokens[$referencedName->getStartPointer()]['code'] === T_ATTRIBUTE; $phpcsFile->fixer->beginChangeset(); if ($inAttribute) { $attributeContent = TokenHelper::getContent( $phpcsFile, $referencedName->getStartPointer(), $referencedName->getEndPointer(), ); $fixedAttributeContent = preg_replace( '~(?<=\W)' . preg_quote($referencedName->getNameAsReferencedInFile(), '~') . '(?=\W)~', 'self', $attributeContent, ); FixerHelper::replace( $phpcsFile, $referencedName->getStartPointer(), $fixedAttributeContent, ); } else { FixerHelper::replace($phpcsFile, $referencedName->getStartPointer(), 'self'); } FixerHelper::removeBetweenIncluding($phpcsFile, $referencedName->getStartPointer() + 1, $referencedName->getEndPointer()); $phpcsFile->fixer->endChangeset(); } } } PK41])  Qcoding-standard/SlevomatCodingStandard/Sniffs/Classes/AbstractMethodSignature.phpnu[ */ public function register(): array { return [T_FUNCTION]; } /** * @return array */ protected function getSignatureStartAndEndPointers(File $phpcsFile, int $methodPointer): array { $signatureStartPointer = TokenHelper::findFirstTokenOnLine($phpcsFile, $methodPointer); /** @var int $pointerAfterSignatureEnd */ $pointerAfterSignatureEnd = TokenHelper::findNext($phpcsFile, [T_OPEN_CURLY_BRACKET, T_SEMICOLON], $methodPointer + 1); if ($phpcsFile->getTokens()[$pointerAfterSignatureEnd]['code'] === T_SEMICOLON) { return [$signatureStartPointer, $pointerAfterSignatureEnd]; } /** @var int $signatureEndPointer */ $signatureEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $pointerAfterSignatureEnd - 1); return [$signatureStartPointer, $signatureEndPointer]; } protected function getSignature(File $phpcsFile, int $signatureStartPointer, int $signatureEndPointer): string { $signature = TokenHelper::getContent($phpcsFile, $signatureStartPointer, $signatureEndPointer); $signature = preg_replace(sprintf('~%s[ \t]*~', $phpcsFile->eolChar), ' ', $signature); assert(is_string($signature)); $signature = str_replace(['( ', ' )'], ['(', ')'], $signature); $signature = rtrim($signature); return $signature; } } PK41] HUcoding-standard/SlevomatCodingStandard/Sniffs/Classes/MissingClassGroupsException.phpnu[ $groups */ public function __construct(array $groups) { parent::__construct( sprintf( 'You need configure all class groups. These groups are missing from your configuration: %s.', implode(', ', $groups), ), ); } } PK41]>&&Ncoding-standard/SlevomatCodingStandard/Sniffs/Classes/TraitUseSpacingSniff.phpnu[ */ public function register(): array { return [ T_CLASS, T_ANON_CLASS, T_TRAIT, T_ENUM, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $classPointer */ public function process(File $phpcsFile, $classPointer): void { $this->linesCountBeforeFirstUse = SniffSettingsHelper::normalizeInteger($this->linesCountBeforeFirstUse); $this->linesCountBeforeFirstUseWhenFirstInClass = SniffSettingsHelper::normalizeNullableInteger( $this->linesCountBeforeFirstUseWhenFirstInClass, ); $this->linesCountBetweenUses = SniffSettingsHelper::normalizeInteger($this->linesCountBetweenUses); $this->linesCountAfterLastUse = SniffSettingsHelper::normalizeNullableInteger($this->linesCountAfterLastUse); $this->linesCountAfterLastUseWhenLastInClass = SniffSettingsHelper::normalizeInteger($this->linesCountAfterLastUseWhenLastInClass); $usePointers = ClassHelper::getTraitUsePointers($phpcsFile, $classPointer); if (count($usePointers) === 0) { return; } $this->checkLinesBeforeFirstUse($phpcsFile, $usePointers[0]); $this->checkLinesAfterLastUse($phpcsFile, $usePointers[count($usePointers) - 1]); $this->checkLinesBetweenUses($phpcsFile, $usePointers); } private function checkLinesBeforeFirstUse(File $phpcsFile, int $firstUsePointer): void { $tokens = $phpcsFile->getTokens(); $useStartPointer = $firstUsePointer; /** @var int $pointerBeforeFirstUse */ $pointerBeforeFirstUse = TokenHelper::findPreviousNonWhitespace($phpcsFile, $firstUsePointer - 1); if (in_array($tokens[$pointerBeforeFirstUse]['code'], Tokens::$commentTokens, true)) { $pointerBeforeFirstUse = TokenHelper::findPreviousEffective($phpcsFile, $pointerBeforeFirstUse - 1); $useStartPointer = TokenHelper::findNext($phpcsFile, Tokens::$commentTokens, $pointerBeforeFirstUse + 1); } $isAtTheStartOfClass = $tokens[$pointerBeforeFirstUse]['code'] === T_OPEN_CURLY_BRACKET; $whitespaceBeforeFirstUse = ''; if ($pointerBeforeFirstUse + 1 !== $firstUsePointer) { $whitespaceBeforeFirstUse .= TokenHelper::getContent($phpcsFile, $pointerBeforeFirstUse + 1, $useStartPointer - 1); } $requiredLinesCountBeforeFirstUse = $this->linesCountBeforeFirstUse; if ( $isAtTheStartOfClass && $this->linesCountBeforeFirstUseWhenFirstInClass !== null ) { $requiredLinesCountBeforeFirstUse = $this->linesCountBeforeFirstUseWhenFirstInClass; } $actualLinesCountBeforeFirstUse = substr_count($whitespaceBeforeFirstUse, $phpcsFile->eolChar) - 1; if ($actualLinesCountBeforeFirstUse === $requiredLinesCountBeforeFirstUse) { return; } $fix = $phpcsFile->addFixableError( sprintf( 'Expected %d line%s before first use statement, found %d.', $requiredLinesCountBeforeFirstUse, $requiredLinesCountBeforeFirstUse === 1 ? '' : 's', $actualLinesCountBeforeFirstUse, ), $firstUsePointer, self::CODE_INCORRECT_LINES_COUNT_BEFORE_FIRST_USE, ); if (!$fix) { return; } $pointerBeforeIndentation = TokenHelper::findPreviousContent( $phpcsFile, T_WHITESPACE, $phpcsFile->eolChar, $firstUsePointer, $pointerBeforeFirstUse, ); $phpcsFile->fixer->beginChangeset(); if ($pointerBeforeIndentation !== null) { FixerHelper::removeBetweenIncluding($phpcsFile, $pointerBeforeFirstUse + 1, $pointerBeforeIndentation); } for ($i = 0; $i <= $requiredLinesCountBeforeFirstUse; $i++) { $phpcsFile->fixer->addNewline($pointerBeforeFirstUse); } $phpcsFile->fixer->endChangeset(); } private function checkLinesAfterLastUse(File $phpcsFile, int $lastUsePointer): void { $tokens = $phpcsFile->getTokens(); /** @var int $lastUseEndPointer */ $lastUseEndPointer = TokenHelper::findNextLocal($phpcsFile, [T_SEMICOLON, T_OPEN_CURLY_BRACKET], $lastUsePointer + 1); if ($tokens[$lastUseEndPointer]['code'] === T_OPEN_CURLY_BRACKET) { $lastUseEndPointer = $tokens[$lastUseEndPointer]['bracket_closer']; } $pointerAfterLastUse = TokenHelper::findNextEffective($phpcsFile, $lastUseEndPointer + 1); $isAtTheEndOfClass = $tokens[$pointerAfterLastUse]['code'] === T_CLOSE_CURLY_BRACKET; $whitespaceEnd = TokenHelper::findNextNonWhitespace($phpcsFile, $lastUseEndPointer + 1) - 1; if ($lastUseEndPointer !== $whitespaceEnd && $tokens[$whitespaceEnd]['content'] !== $phpcsFile->eolChar) { $lastEolPointer = TokenHelper::findPreviousContent( $phpcsFile, T_WHITESPACE, $phpcsFile->eolChar, $whitespaceEnd - 1, $lastUseEndPointer, ); $whitespaceEnd = $lastEolPointer ?? $lastUseEndPointer; } $whitespaceAfterLastUse = TokenHelper::getContent($phpcsFile, $lastUseEndPointer + 1, $whitespaceEnd); $requiredLinesCountAfterLastUse = $isAtTheEndOfClass ? $this->linesCountAfterLastUseWhenLastInClass : $this->linesCountAfterLastUse; if ($requiredLinesCountAfterLastUse === null) { return; } $actualLinesCountAfterLastUse = substr_count($whitespaceAfterLastUse, $phpcsFile->eolChar) - 1; if ($actualLinesCountAfterLastUse === $requiredLinesCountAfterLastUse) { return; } $fix = $phpcsFile->addFixableError( sprintf( 'Expected %d line%s after last use statement, found %d.', $requiredLinesCountAfterLastUse, $requiredLinesCountAfterLastUse === 1 ? '' : 's', $actualLinesCountAfterLastUse, ), $lastUsePointer, self::CODE_INCORRECT_LINES_COUNT_AFTER_LAST_USE, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $lastUseEndPointer + 1, $whitespaceEnd); for ($i = 0; $i <= $requiredLinesCountAfterLastUse; $i++) { $phpcsFile->fixer->addNewline($lastUseEndPointer); } $phpcsFile->fixer->endChangeset(); } /** * @param list $usePointers */ private function checkLinesBetweenUses(File $phpcsFile, array $usePointers): void { if (count($usePointers) === 1) { return; } $tokens = $phpcsFile->getTokens(); $previousUsePointer = null; foreach ($usePointers as $usePointer) { if ($previousUsePointer === null) { $previousUsePointer = $usePointer; continue; } /** @var int $previousUseEndPointer */ $previousUseEndPointer = TokenHelper::findNextLocal($phpcsFile, [T_SEMICOLON, T_OPEN_CURLY_BRACKET], $previousUsePointer + 1); if ($tokens[$previousUseEndPointer]['code'] === T_OPEN_CURLY_BRACKET) { /** @var int $previousUseEndPointer */ $previousUseEndPointer = $tokens[$previousUseEndPointer]['bracket_closer']; } $useStartPointer = $usePointer; $pointerBeforeUse = TokenHelper::findPreviousNonWhitespace($phpcsFile, $usePointer - 1); if (in_array($tokens[$pointerBeforeUse]['code'], Tokens::$commentTokens, true)) { $useStartPointer = TokenHelper::findNext( $phpcsFile, Tokens::$commentTokens, TokenHelper::findPreviousEffective($phpcsFile, $pointerBeforeUse - 1) + 1, ); } $actualLinesCountAfterPreviousUse = $tokens[$useStartPointer]['line'] - $tokens[$previousUseEndPointer]['line'] - 1; if ($actualLinesCountAfterPreviousUse === $this->linesCountBetweenUses) { $previousUsePointer = $usePointer; continue; } $errorParameters = [ sprintf( 'Expected %d line%s between same types of use statement, found %d.', $this->linesCountBetweenUses, $this->linesCountBetweenUses === 1 ? '' : 's', $actualLinesCountAfterPreviousUse, ), $usePointer, self::CODE_INCORRECT_LINES_COUNT_BETWEEN_USES, ]; $pointerBeforeUse = TokenHelper::findPreviousEffective($phpcsFile, $usePointer - 1); if ($previousUseEndPointer !== $pointerBeforeUse) { $phpcsFile->addError(...$errorParameters); $previousUsePointer = $usePointer; continue; } $fix = $phpcsFile->addFixableError(...$errorParameters); if (!$fix) { $previousUsePointer = $usePointer; continue; } $pointerBeforeIndentation = TokenHelper::findPreviousContent( $phpcsFile, T_WHITESPACE, $phpcsFile->eolChar, $usePointer, $previousUseEndPointer, ); $phpcsFile->fixer->beginChangeset(); if ($pointerBeforeIndentation !== null) { FixerHelper::removeBetweenIncluding($phpcsFile, $previousUseEndPointer + 1, $pointerBeforeIndentation); } for ($i = 0; $i <= $this->linesCountBetweenUses; $i++) { $phpcsFile->fixer->addNewline($previousUseEndPointer); } $phpcsFile->fixer->endChangeset(); $previousUsePointer = $usePointer; } } } PK41]Pa**dcoding-standard/SlevomatCodingStandard/Sniffs/Classes/DisallowLateStaticBindingForConstantsSniff.phpnu[ */ public function register(): array { return [ T_STATIC, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $staticPointer */ public function process(File $phpcsFile, $staticPointer): void { $tokens = $phpcsFile->getTokens(); $doubleColonPointer = TokenHelper::findNextEffective($phpcsFile, $staticPointer + 1); if ($tokens[$doubleColonPointer]['code'] !== T_DOUBLE_COLON) { return; } $stringPointer = TokenHelper::findNextEffective($phpcsFile, $doubleColonPointer + 1); if ($tokens[$stringPointer]['code'] !== T_STRING) { return; } if (strtolower($tokens[$stringPointer]['content']) === 'class') { return; } $pointerAfterString = TokenHelper::findNextEffective($phpcsFile, $stringPointer + 1); if ($tokens[$pointerAfterString]['code'] === T_OPEN_PARENTHESIS) { return; } $fix = $phpcsFile->addFixableError( 'Late static binding for constants is disallowed.', $staticPointer, self::CODE_DISALLOWED_LATE_STATIC_BINDING_FOR_CONSTANT, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $staticPointer, 'self'); $phpcsFile->fixer->endChangeset(); } } PK41]EZr _coding-standard/SlevomatCodingStandard/Sniffs/Classes/RequireSingleLineMethodSignatureSniff.phpnu[ */ public array $includedMethodPatterns = []; /** @var list|null */ public ?array $includedMethodNormalizedPatterns = null; /** @var list */ public array $excludedMethodPatterns = []; /** @var list|null */ public ?array $excludedMethodNormalizedPatterns = null; /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $methodPointer */ public function process(File $phpcsFile, $methodPointer): void { $this->maxLineLength = SniffSettingsHelper::normalizeInteger($this->maxLineLength); if (!FunctionHelper::isMethod($phpcsFile, $methodPointer)) { return; } $tokens = $phpcsFile->getTokens(); [$signatureStartPointer, $signatureEndPointer] = $this->getSignatureStartAndEndPointers($phpcsFile, $methodPointer); if ($tokens[$signatureStartPointer]['line'] === $tokens[$signatureEndPointer]['line']) { return; } $signature = $this->getSignature($phpcsFile, $signatureStartPointer, $signatureEndPointer); $methodName = FunctionHelper::getName($phpcsFile, $methodPointer); if ( count($this->includedMethodPatterns) !== 0 && !$this->isMethodNameInPatterns($methodName, $this->getIncludedMethodNormalizedPatterns()) ) { return; } if ( count($this->excludedMethodPatterns) !== 0 && $this->isMethodNameInPatterns($methodName, $this->getExcludedMethodNormalizedPatterns()) ) { return; } if ($this->maxLineLength !== 0 && strlen($signature) > $this->maxLineLength) { return; } $error = sprintf('Signature of method "%s" should be placed on a single line.', $methodName); $fix = $phpcsFile->addFixableError($error, $methodPointer, self::CODE_REQUIRED_SINGLE_LINE_SIGNATURE); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $signatureStartPointer, $signatureEndPointer, $signature); $phpcsFile->fixer->endChangeset(); } /** * @param list $normalizedPatterns */ private function isMethodNameInPatterns(string $methodName, array $normalizedPatterns): bool { foreach ($normalizedPatterns as $pattern) { if (!SniffSettingsHelper::isValidRegularExpression($pattern)) { throw new Exception(sprintf('%s is not valid PCRE pattern.', $pattern)); } if (preg_match($pattern, $methodName) !== 0) { return true; } } return false; } /** * @return list */ private function getIncludedMethodNormalizedPatterns(): array { $this->includedMethodNormalizedPatterns ??= SniffSettingsHelper::normalizeArray($this->includedMethodPatterns); return $this->includedMethodNormalizedPatterns; } /** * @return list */ private function getExcludedMethodNormalizedPatterns(): array { $this->excludedMethodNormalizedPatterns ??= SniffSettingsHelper::normalizeArray($this->excludedMethodPatterns); return $this->excludedMethodNormalizedPatterns; } } PK41]6*1  Wcoding-standard/SlevomatCodingStandard/Sniffs/Classes/UselessLateStaticBindingSniff.phpnu[ */ public function register(): array { return [ T_STATIC, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $staticPointer */ public function process(File $phpcsFile, $staticPointer): void { $tokens = $phpcsFile->getTokens(); $doubleColonPointer = TokenHelper::findNextEffective($phpcsFile, $staticPointer + 1); if ($tokens[$doubleColonPointer]['code'] !== T_DOUBLE_COLON) { return; } $classPointer = null; foreach (array_reverse($tokens[$staticPointer]['conditions'], true) as $conditionPointer => $conditionTokenCode) { if (!in_array($conditionTokenCode, Tokens::$ooScopeTokens, true)) { continue; } $classPointer = $conditionPointer; break; } if ($classPointer === null || !ClassHelper::isFinal($phpcsFile, $classPointer)) { return; } $fix = $phpcsFile->addFixableError( 'Useless late static binding because class is final.', $staticPointer, self::CODE_USELESS_LATE_STATIC_BINDING, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $staticPointer, 'self'); $phpcsFile->fixer->endChangeset(); } } PK41]>3 Kcoding-standard/SlevomatCodingStandard/Sniffs/PHP/ForbiddenClassesSniff.phpnu[ */ public array $forbiddenClasses = []; /** @var array */ public array $forbiddenExtends = []; /** @var array */ public array $forbiddenInterfaces = []; /** @var array */ public array $forbiddenTraits = []; /** @var list */ private static array $keywordReferences = ['self', 'parent', 'static']; /** * @return array */ public function register(): array { $searchTokens = []; if (count($this->forbiddenClasses) > 0) { $this->forbiddenClasses = self::normalizeInputOption($this->forbiddenClasses); $searchTokens[] = T_NEW; $searchTokens[] = T_DOUBLE_COLON; } if (count($this->forbiddenExtends) > 0) { $this->forbiddenExtends = self::normalizeInputOption($this->forbiddenExtends); $searchTokens[] = T_EXTENDS; } if (count($this->forbiddenInterfaces) > 0) { $this->forbiddenInterfaces = self::normalizeInputOption($this->forbiddenInterfaces); $searchTokens[] = T_IMPLEMENTS; } if (count($this->forbiddenTraits) > 0) { $this->forbiddenTraits = self::normalizeInputOption($this->forbiddenTraits); $searchTokens[] = T_USE; } return $searchTokens; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $tokenPointer */ public function process(File $phpcsFile, $tokenPointer): void { $tokens = $phpcsFile->getTokens(); $token = $tokens[$tokenPointer]; $nameTokens = [...TokenHelper::NAME_TOKEN_CODES, ...TokenHelper::INEFFECTIVE_TOKEN_CODES]; if ( $token['code'] === T_IMPLEMENTS || ( $token['code'] === T_USE && UseStatementHelper::isTraitUse($phpcsFile, $tokenPointer) ) ) { $endTokenPointer = TokenHelper::findNext( $phpcsFile, [T_SEMICOLON, T_OPEN_CURLY_BRACKET], $tokenPointer, ); $references = $this->getAllReferences($phpcsFile, $tokenPointer, $endTokenPointer); if ($token['code'] === T_IMPLEMENTS) { $this->checkReferences($phpcsFile, $tokenPointer, $references, $this->forbiddenInterfaces); } else { // Fixer does not work when traits contains aliases $this->checkReferences( $phpcsFile, $tokenPointer, $references, $this->forbiddenTraits, $tokens[$endTokenPointer]['code'] !== T_OPEN_CURLY_BRACKET, ); } } elseif (in_array($token['code'], [T_NEW, T_EXTENDS], true)) { $endTokenPointer = TokenHelper::findNextExcluding($phpcsFile, $nameTokens, $tokenPointer + 1); $references = $this->getAllReferences($phpcsFile, $tokenPointer, $endTokenPointer); $this->checkReferences( $phpcsFile, $tokenPointer, $references, $token['code'] === T_NEW ? $this->forbiddenClasses : $this->forbiddenExtends, ); } elseif ($token['code'] === T_DOUBLE_COLON && !$this->isTraitsConflictResolutionToken($token)) { $startTokenPointer = TokenHelper::findPreviousExcluding($phpcsFile, $nameTokens, $tokenPointer - 1); $references = $this->getAllReferences($phpcsFile, $startTokenPointer, $tokenPointer); $this->checkReferences($phpcsFile, $tokenPointer, $references, $this->forbiddenClasses); } } /** * @param list $references * @param array $forbiddenNames */ private function checkReferences( File $phpcsFile, int $tokenPointer, array $references, array $forbiddenNames, bool $isFixable = true ): void { $token = $phpcsFile->getTokens()[$tokenPointer]; $details = [ T_NEW => ['class', self::CODE_FORBIDDEN_CLASS], T_DOUBLE_COLON => ['class', self::CODE_FORBIDDEN_CLASS], T_EXTENDS => ['as a parent class', self::CODE_FORBIDDEN_PARENT_CLASS], T_IMPLEMENTS => ['interface', self::CODE_FORBIDDEN_INTERFACE], T_USE => ['trait', self::CODE_FORBIDDEN_TRAIT], ]; foreach ($references as $reference) { if (!array_key_exists($reference['fullyQualifiedName'], $forbiddenNames)) { continue; } $alternative = $forbiddenNames[$reference['fullyQualifiedName']]; [$nameType, $code] = $details[$token['code']]; if ($alternative === null) { $phpcsFile->addError( sprintf('Usage of %s %s is forbidden.', $reference['fullyQualifiedName'], $nameType), $reference['startPointer'], $code, ); } elseif (!$isFixable) { $phpcsFile->addError( sprintf( 'Usage of %s %s is forbidden, use %s instead.', $reference['fullyQualifiedName'], $nameType, $alternative, ), $reference['startPointer'], $code, ); } else { $fix = $phpcsFile->addFixableError( sprintf( 'Usage of %s %s is forbidden, use %s instead.', $reference['fullyQualifiedName'], $nameType, $alternative, ), $reference['startPointer'], $code, ); if (!$fix) { continue; } $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $reference['startPointer'], $reference['endPointer'], $alternative); $phpcsFile->fixer->endChangeset(); } } } /** * @param array|int|string> $token */ private function isTraitsConflictResolutionToken(array $token): bool { return is_array($token['conditions']) && array_pop($token['conditions']) === T_USE; } /** * @return list */ private function getAllReferences(File $phpcsFile, int $startPointer, int $endPointer): array { // Always ignore first token $startPointer++; $references = []; while ($startPointer < $endPointer) { $nextComma = TokenHelper::findNext($phpcsFile, [T_COMMA], $startPointer + 1); $nextSeparator = min($endPointer, $nextComma ?? PHP_INT_MAX); $reference = ReferencedNameHelper::getReferenceName($phpcsFile, $startPointer, $nextSeparator - 1); if ( strlen($reference) !== 0 && !in_array(strtolower($reference), self::$keywordReferences, true) ) { $references[] = [ 'fullyQualifiedName' => NamespaceHelper::resolveClassName($phpcsFile, $reference, $startPointer), 'startPointer' => TokenHelper::findNextEffective($phpcsFile, $startPointer, $endPointer), 'endPointer' => TokenHelper::findPreviousEffective($phpcsFile, $nextSeparator - 1, $startPointer), ]; } $startPointer = $nextSeparator + 1; } return $references; } /** * @param array $option * @return array */ private static function normalizeInputOption(array $option): array { $forbiddenClasses = []; foreach ($option as $forbiddenClass => $alternative) { $forbiddenClasses[self::normalizeClassName($forbiddenClass)] = self::normalizeClassName($alternative); } return $forbiddenClasses; } private static function normalizeClassName(?string $typeName): ?string { if ($typeName === null || strlen($typeName) === 0 || strtolower($typeName) === 'null') { return null; } return NamespaceHelper::getFullyQualifiedTypeName($typeName); } } PK41]ܻJJMcoding-standard/SlevomatCodingStandard/Sniffs/PHP/UselessParenthesesSniff.phpnu[ 1, T_MULTIPLY => 2, T_DIVIDE => 2, T_MODULUS => 3, T_PLUS => 4, T_MINUS => 4, T_STRING_CONCAT => 5, ]; public bool $ignoreComplexTernaryConditions = false; /** * @return array */ public function register(): array { return [ T_OPEN_PARENTHESIS, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $parenthesisOpenerPointer */ public function process(File $phpcsFile, $parenthesisOpenerPointer): void { $tokens = $phpcsFile->getTokens(); if (array_key_exists('parenthesis_owner', $tokens[$parenthesisOpenerPointer])) { return; } if (!array_key_exists('parenthesis_closer', $tokens[$parenthesisOpenerPointer])) { return; } /** @var int $pointerBeforeParenthesisOpener */ $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1); if (in_array($tokens[$pointerBeforeParenthesisOpener]['code'], [ ...TokenHelper::NAME_TOKEN_CODES, T_VARIABLE, T_ISSET, T_UNSET, T_EMPTY, T_CLOSURE, T_FN, T_USE, T_ANON_CLASS, T_NEW, T_SELF, T_STATIC, T_PARENT, T_EXIT, T_CLOSE_PARENTHESIS, T_EVAL, T_LIST, T_INCLUDE, T_INCLUDE_ONCE, T_REQUIRE, T_REQUIRE_ONCE, T_INT_CAST, T_DOUBLE_CAST, T_STRING_CAST, T_ARRAY_CAST, T_OBJECT_CAST, T_BOOL_CAST, T_UNSET_CAST, T_MATCH, T_BITWISE_NOT, ], true,)) { return; } /** @var int $pointerAfterParenthesisOpener */ $pointerAfterParenthesisOpener = TokenHelper::findNextEffective($phpcsFile, $parenthesisOpenerPointer + 1); if (in_array( $tokens[$pointerAfterParenthesisOpener]['code'], [T_CLONE, T_YIELD, T_YIELD_FROM, T_REQUIRE, T_REQUIRE_ONCE, T_INCLUDE, T_INCLUDE_ONCE, T_ARRAY_CAST], true, )) { return; } if (TokenHelper::findNext( $phpcsFile, T_EQUAL, $parenthesisOpenerPointer + 1, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'], ) !== null) { return; } $pointerAfterParenthesisCloser = TokenHelper::findNextEffective( $phpcsFile, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'] + 1, ); if ( $pointerAfterParenthesisCloser !== null && $tokens[$pointerAfterParenthesisCloser]['code'] === T_OPEN_PARENTHESIS ) { return; } if (IdentificatorHelper::findStartPointer($phpcsFile, $pointerBeforeParenthesisOpener) !== null) { return; } $this->checkParenthesesAroundConditionInTernaryOperator($phpcsFile, $parenthesisOpenerPointer); $this->checkParenthesesAroundCaseInSwitch($phpcsFile, $parenthesisOpenerPointer); $this->checkParenthesesAroundVariableOrFunctionCall($phpcsFile, $parenthesisOpenerPointer); $this->checkParenthesesAroundString($phpcsFile, $parenthesisOpenerPointer); $this->checkParenthesesAroundOperators($phpcsFile, $parenthesisOpenerPointer); $this->checkParenthesesAroundNew($phpcsFile, $parenthesisOpenerPointer); } private function checkParenthesesAroundConditionInTernaryOperator(File $phpcsFile, int $parenthesisOpenerPointer): void { $tokens = $phpcsFile->getTokens(); $parenthesisCloserPointer = $tokens[$parenthesisOpenerPointer]['parenthesis_closer']; $ternaryOperatorPointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisCloserPointer + 1); if ($tokens[$ternaryOperatorPointer]['code'] !== T_INLINE_THEN) { return; } if (TokenHelper::findNext( $phpcsFile, [T_LOGICAL_AND, T_LOGICAL_OR, T_LOGICAL_XOR], $parenthesisOpenerPointer + 1, $parenthesisCloserPointer, ) !== null) { return; } $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1); if ($tokens[$pointerBeforeParenthesisOpener]['code'] === T_BOOLEAN_NOT) { return; } if (in_array($tokens[$pointerBeforeParenthesisOpener]['code'], Tokens::$comparisonTokens, true)) { return; } if (in_array($tokens[$pointerBeforeParenthesisOpener]['code'], Tokens::$booleanOperators, true)) { return; } if ($this->ignoreComplexTernaryConditions) { if (TokenHelper::findNext( $phpcsFile, Tokens::$booleanOperators, $parenthesisOpenerPointer + 1, $parenthesisCloserPointer, ) !== null) { return; } if (TokenHelper::findNextContent( $phpcsFile, T_WHITESPACE, $phpcsFile->eolChar, $parenthesisOpenerPointer + 1, $parenthesisCloserPointer, ) !== null) { return; } } $contentStartPointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisOpenerPointer + 1); $contentEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisCloserPointer - 1); for ($i = $contentStartPointer; $i <= $contentEndPointer; $i++) { if ($tokens[$i]['code'] === T_INLINE_THEN) { return; } } $fix = $phpcsFile->addFixableError('Useless parentheses.', $parenthesisOpenerPointer, self::CODE_USELESS_PARENTHESES); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $parenthesisOpenerPointer, $contentStartPointer - 1); FixerHelper::removeBetweenIncluding($phpcsFile, $contentEndPointer + 1, $parenthesisCloserPointer); $phpcsFile->fixer->endChangeset(); } private function checkParenthesesAroundCaseInSwitch(File $phpcsFile, int $parenthesisOpenerPointer): void { $tokens = $phpcsFile->getTokens(); $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1); if ($tokens[$pointerBeforeParenthesisOpener]['code'] !== T_CASE) { return; } $pointerAfterParenthesisCloser = TokenHelper::findNextEffective( $phpcsFile, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'] + 1, ); if ($tokens[$pointerAfterParenthesisCloser]['code'] !== T_COLON) { return; } $fix = $phpcsFile->addFixableError('Useless parentheses.', $parenthesisOpenerPointer, self::CODE_USELESS_PARENTHESES); if (!$fix) { return; } $contentStartPointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisOpenerPointer + 1); $contentEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'] - 1); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $parenthesisOpenerPointer, $contentStartPointer - 1); FixerHelper::removeBetweenIncluding($phpcsFile, $contentEndPointer + 1, $tokens[$parenthesisOpenerPointer]['parenthesis_closer']); $phpcsFile->fixer->endChangeset(); } private function checkParenthesesAroundVariableOrFunctionCall(File $phpcsFile, int $parenthesisOpenerPointer): void { $tokens = $phpcsFile->getTokens(); $pointerAfterParenthesis = TokenHelper::findNextEffective($phpcsFile, $parenthesisOpenerPointer + 1); if ($tokens[$pointerAfterParenthesis]['code'] === T_NEW) { // Check in other method return; } if ($tokens[$pointerAfterParenthesis]['code'] === T_OPEN_PARENTHESIS) { return; } $operatorsPointers = TokenHelper::findNextAll( $phpcsFile, self::OPERATORS, $parenthesisOpenerPointer + 1, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'], ); if ($operatorsPointers !== []) { return; } $casePointer = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1); if ($tokens[$casePointer]['code'] === T_CASE) { return; } $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1); if (in_array($tokens[$pointerBeforeParenthesisOpener]['code'], Tokens::$booleanOperators, true)) { return; } $pointerAfterParenthesisCloser = TokenHelper::findNextEffective( $phpcsFile, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'] + 1, ); if (in_array($tokens[$pointerAfterParenthesisCloser]['code'], [T_INLINE_THEN, T_OPEN_PARENTHESIS, T_SR], true)) { return; } /** @var int $contentStartPointer */ $contentStartPointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisOpenerPointer + 1); if ($tokens[$contentStartPointer]['code'] === T_CONSTANT_ENCAPSED_STRING) { return; } $notBooleanNotOperatorPointer = $contentStartPointer; if ($tokens[$contentStartPointer]['code'] === T_BOOLEAN_NOT) { /** @var int $notBooleanNotOperatorPointer */ $notBooleanNotOperatorPointer = TokenHelper::findNextEffective($phpcsFile, $contentStartPointer + 1); } if (in_array( $tokens[$notBooleanNotOperatorPointer]['code'], [T_SELF, T_STATIC, T_PARENT, T_VARIABLE, T_DOLLAR, ...TokenHelper::NAME_TOKEN_CODES], true, )) { $contentEndPointer = IdentificatorHelper::findEndPointer($phpcsFile, $notBooleanNotOperatorPointer); if ( $contentEndPointer === null && in_array($tokens[$notBooleanNotOperatorPointer]['code'], TokenHelper::NAME_TOKEN_CODES, true) ) { $nextPointer = TokenHelper::findNextEffective($phpcsFile, $contentStartPointer + 1); if ($tokens[$nextPointer]['code'] === T_OPEN_PARENTHESIS) { $contentEndPointer = $contentStartPointer; } } do { $nextPointer = TokenHelper::findNextEffective($phpcsFile, $contentEndPointer + 1); if ($tokens[$nextPointer]['code'] !== T_OPEN_PARENTHESIS) { break; } $contentEndPointer = $tokens[$nextPointer]['parenthesis_closer']; } while (true); } else { $nextPointer = TokenHelper::findNext($phpcsFile, T_OPEN_PARENTHESIS, $notBooleanNotOperatorPointer + 1); if ($nextPointer === null || !isset($tokens[$nextPointer]['parenthesis_closer'])) { return; } $contentEndPointer = $tokens[$nextPointer]['parenthesis_closer']; } $pointerAfterContent = TokenHelper::findNextEffective($phpcsFile, $contentEndPointer + 1); if ($pointerAfterContent !== $tokens[$parenthesisOpenerPointer]['parenthesis_closer']) { return; } $fix = $phpcsFile->addFixableError('Useless parentheses.', $parenthesisOpenerPointer, self::CODE_USELESS_PARENTHESES); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $parenthesisOpenerPointer, $contentStartPointer - 1); FixerHelper::removeBetweenIncluding($phpcsFile, $contentEndPointer + 1, $tokens[$parenthesisOpenerPointer]['parenthesis_closer']); $phpcsFile->fixer->endChangeset(); } private function checkParenthesesAroundString(File $phpcsFile, int $parenthesisOpenerPointer): void { $tokens = $phpcsFile->getTokens(); /** @var int $stringPointer */ $stringPointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisOpenerPointer + 1); if ($tokens[$stringPointer]['code'] !== T_CONSTANT_ENCAPSED_STRING) { return; } $pointerAfterString = TokenHelper::findNextEffective($phpcsFile, $stringPointer + 1); if ($pointerAfterString !== $tokens[$parenthesisOpenerPointer]['parenthesis_closer']) { return; } $fix = $phpcsFile->addFixableError('Useless parentheses.', $parenthesisOpenerPointer, self::CODE_USELESS_PARENTHESES); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $parenthesisOpenerPointer, $stringPointer - 1); FixerHelper::removeBetweenIncluding($phpcsFile, $stringPointer + 1, $tokens[$parenthesisOpenerPointer]['parenthesis_closer']); $phpcsFile->fixer->endChangeset(); } private function checkParenthesesAroundOperators(File $phpcsFile, int $parenthesisOpenerPointer): void { $tokens = $phpcsFile->getTokens(); $newPointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisOpenerPointer + 1); if ($tokens[$newPointer]['code'] === T_NEW) { // Check in other method return; } $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1); $pointerAfterParenthesisCloser = TokenHelper::findNextEffective( $phpcsFile, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'] + 1, ); if ($tokens[$pointerBeforeParenthesisOpener]['code'] === T_MINUS) { $pointerBeforeMinus = TokenHelper::findPreviousEffective($phpcsFile, $pointerBeforeParenthesisOpener - 1); if (!in_array($tokens[$pointerBeforeMinus]['code'], [T_DNUMBER, T_LNUMBER], true)) { return; } } if ( in_array($tokens[$pointerBeforeParenthesisOpener]['code'], Tokens::$booleanOperators, true) || in_array($tokens[$pointerAfterParenthesisCloser]['code'], Tokens::$booleanOperators, true) || $tokens[$pointerBeforeParenthesisOpener]['code'] === T_BOOLEAN_NOT ) { return; } $complicatedOperators = [T_INLINE_THEN, T_COALESCE, T_BITWISE_AND, T_BITWISE_OR, T_BITWISE_XOR, T_SL, T_SR]; $operatorsPointers = []; $actualStartPointer = $parenthesisOpenerPointer + 1; while (true) { $pointer = TokenHelper::findNext( $phpcsFile, array_merge( [ ...self::OPERATORS, T_OPEN_PARENTHESIS, ...$complicatedOperators, ], Tokens::$comparisonTokens, ), $actualStartPointer, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'], ); if ($pointer === null) { break; } if (in_array($tokens[$pointer]['code'], $complicatedOperators, true)) { return; } if (in_array($tokens[$pointer]['code'], Tokens::$comparisonTokens, true)) { return; } if ($tokens[$pointer]['code'] === T_OPEN_PARENTHESIS) { $actualStartPointer = $tokens[$pointer]['parenthesis_closer'] + 1; continue; } $operatorsPointers[] = $pointer; $actualStartPointer = $pointer + 1; } if (count($operatorsPointers) === 0) { return; } if ( $tokens[$pointerBeforeParenthesisOpener]['code'] !== T_EQUAL || $tokens[$pointerAfterParenthesisCloser]['code'] !== T_SEMICOLON ) { $operatorsGroups = array_map( static fn (int $operatorPointer): int => self::OPERATOR_GROUPS[$tokens[$operatorPointer]['code']], $operatorsPointers, ); if (count($operatorsGroups) > 1) { return; } } $firstOperatorPointer = $operatorsPointers[0]; if (in_array($tokens[$pointerBeforeParenthesisOpener]['code'], self::OPERATORS, true)) { if (self::OPERATOR_GROUPS[$tokens[$firstOperatorPointer]['code']] !== self::OPERATOR_GROUPS[$tokens[$pointerBeforeParenthesisOpener]['code']]) { return; } if ( $tokens[$pointerBeforeParenthesisOpener]['code'] === T_MINUS && in_array($tokens[$firstOperatorPointer]['code'], [T_PLUS, T_MINUS], true) ) { return; } if ( $tokens[$pointerBeforeParenthesisOpener]['code'] === T_DIVIDE && in_array($tokens[$firstOperatorPointer]['code'], [T_DIVIDE, T_MULTIPLY], true) ) { return; } if ( $tokens[$pointerBeforeParenthesisOpener]['code'] === T_MODULUS && $tokens[$firstOperatorPointer]['code'] === T_MODULUS ) { return; } } $lastOperatorPointer = $operatorsPointers[count($operatorsPointers) - 1]; if ( in_array($tokens[$pointerAfterParenthesisCloser]['code'], self::OPERATORS, true) && self::OPERATOR_GROUPS[$tokens[$lastOperatorPointer]['code']] !== self::OPERATOR_GROUPS[$tokens[$pointerAfterParenthesisCloser]['code']] ) { return; } $fix = $phpcsFile->addFixableError('Useless parentheses.', $parenthesisOpenerPointer, self::CODE_USELESS_PARENTHESES); if (!$fix) { return; } $contentStartPointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisOpenerPointer + 1); $contentEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'] - 1); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $parenthesisOpenerPointer, $contentStartPointer - 1); FixerHelper::removeBetweenIncluding($phpcsFile, $contentEndPointer + 1, $tokens[$parenthesisOpenerPointer]['parenthesis_closer']); $phpcsFile->fixer->endChangeset(); } private function checkParenthesesAroundNew(File $phpcsFile, int $parenthesisOpenerPointer): void { $tokens = $phpcsFile->getTokens(); $newPointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisOpenerPointer + 1); if ($tokens[$newPointer]['code'] !== T_NEW) { return; } $pointerAfterParenthesisCloser = TokenHelper::findNextEffective( $phpcsFile, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'] + 1, ); if (!in_array($tokens[$pointerAfterParenthesisCloser]['code'], [T_COMMA, T_SEMICOLON, T_CLOSE_SHORT_ARRAY], true)) { return; } $fix = $phpcsFile->addFixableError('Useless parentheses.', $parenthesisOpenerPointer, self::CODE_USELESS_PARENTHESES); if (!$fix) { return; } $contentStartPointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisOpenerPointer + 1); $contentEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'] - 1); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $parenthesisOpenerPointer, $contentStartPointer - 1); FixerHelper::removeBetweenIncluding($phpcsFile, $contentEndPointer + 1, $tokens[$parenthesisOpenerPointer]['parenthesis_closer']); $phpcsFile->fixer->endChangeset(); } } PK41]]%X X Ccoding-standard/SlevomatCodingStandard/Sniffs/PHP/TypeCastSniff.phpnu[ null, 'boolean' => 'bool', 'double' => 'float', 'integer' => 'int', 'real' => 'float', 'unset' => null, ]; /** * @return array */ public function register(): array { return [ T_STRING_CAST, T_BOOL_CAST, T_DOUBLE_CAST, T_INT_CAST, T_UNSET_CAST, T_BINARY_CAST, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { $tokens = $phpcsFile->getTokens(); $cast = $tokens[$pointer]['content']; preg_match('~^\(\s*(\S+)\s*\)\z~i', $cast, $matches); if (!array_key_exists(1, $matches)) { return; } $castName = $matches[1]; $castNameLower = strtolower($castName); if (!array_key_exists($castNameLower, self::INVALID_CASTS)) { return; } if ($castNameLower === 'unset') { $phpcsFile->addError( sprintf('Cast "%s" is forbidden, use "unset(...)" or assign "null" instead.', $cast), $pointer, self::CODE_FORBIDDEN_CAST_USED, ); return; } if ($castNameLower === 'binary') { $fix = $phpcsFile->addFixableError( sprintf('"Cast "%s" is forbidden and has no effect.', $cast), $pointer, self::CODE_FORBIDDEN_CAST_USED, ); if (!$fix) { return; } $end = TokenHelper::findNextEffective($phpcsFile, $pointer + 1); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $pointer, $end - 1); $phpcsFile->fixer->endChangeset(); return; } $fix = $phpcsFile->addFixableError( sprintf('Cast "%s" is forbidden, use "(%s)" instead.', $cast, self::INVALID_CASTS[$castNameLower]), $pointer, self::CODE_INVALID_CAST_USED, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $pointer, '(' . self::INVALID_CASTS[$castNameLower] . ')'); $phpcsFile->fixer->endChangeset(); } } PK41]%n.j j Lcoding-standard/SlevomatCodingStandard/Sniffs/PHP/DisallowReferenceSniff.phpnu[ */ public function register(): array { return [ T_BITWISE_AND, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $referencePointer */ public function process(File $phpcsFile, $referencePointer): void { $tokens = $phpcsFile->getTokens(); $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $referencePointer - 1); if (in_array($tokens[$previousPointer]['code'], TokenHelper::FUNCTION_TOKEN_CODES, true)) { $phpcsFile->addError('Returning reference is disallowed.', $referencePointer, self::CODE_DISALLOWED_RETURNING_REFERENCE); return; } $previousParenthesisOpenerPointer = TokenHelper::findPrevious($phpcsFile, T_OPEN_PARENTHESIS, $referencePointer - 1); if ( $previousParenthesisOpenerPointer !== null && $tokens[$previousParenthesisOpenerPointer]['parenthesis_closer'] > $referencePointer ) { if (array_key_exists('parenthesis_owner', $tokens[$previousParenthesisOpenerPointer])) { $parenthesisOwnerPointer = $tokens[$previousParenthesisOpenerPointer]['parenthesis_owner']; if (in_array($tokens[$parenthesisOwnerPointer]['code'], TokenHelper::FUNCTION_TOKEN_CODES, true)) { $phpcsFile->addError( 'Passing by reference is disallowed.', $referencePointer, self::CODE_DISALLOWED_PASSING_BY_REFERENCE, ); return; } } $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $previousParenthesisOpenerPointer - 1); if ( $pointerBeforeParenthesisOpener !== null && $tokens[$pointerBeforeParenthesisOpener]['code'] === T_USE ) { $phpcsFile->addError( 'Inheriting variable by reference is disallowed.', $referencePointer, self::CODE_DISALLOWED_INHERITING_VARIABLE_BY_REFERENCE, ); return; } } /** @var int $variableStartPointer */ $variableStartPointer = TokenHelper::findNextEffective($phpcsFile, $referencePointer + 1); $variableEndPointer = IdentificatorHelper::findEndPointer($phpcsFile, $variableStartPointer); if ($variableEndPointer === null) { return; } $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $referencePointer - 1); if (!in_array($tokens[$previousPointer]['code'], [T_EQUAL, T_DOUBLE_ARROW, T_OPEN_SHORT_ARRAY, T_COMMA, T_AS], true)) { return; } $phpcsFile->addError('Assigning by reference is disallowed.', $referencePointer, self::CODE_DISALLOWED_ASSIGNING_BY_REFERENCE); } } PK41]6/Kcoding-standard/SlevomatCodingStandard/Sniffs/PHP/UselessSemicolonSniff.phpnu[ */ public function register(): array { return [ T_SEMICOLON, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $semicolonPointer */ public function process(File $phpcsFile, $semicolonPointer): void { $this->checkMultipleSemicolons($phpcsFile, $semicolonPointer); $this->checkSemicolonAtTheBeginningOfScope($phpcsFile, $semicolonPointer); $this->checkSemicolonAfterScope($phpcsFile, $semicolonPointer); } private function checkMultipleSemicolons(File $phpcsFile, int $semicolonPointer): void { $tokens = $phpcsFile->getTokens(); $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $semicolonPointer - 1); if ($tokens[$previousPointer]['code'] !== T_SEMICOLON) { return; } $possibleEndScopePointer = TokenHelper::findNextLocal($phpcsFile, T_CLOSE_PARENTHESIS, $semicolonPointer + 1); if ( $possibleEndScopePointer !== null && $tokens[$possibleEndScopePointer]['parenthesis_opener'] < $semicolonPointer && array_key_exists('parenthesis_owner', $tokens[$possibleEndScopePointer]) && $tokens[$tokens[$possibleEndScopePointer]['parenthesis_owner']]['code'] === T_FOR ) { return; } $fix = $phpcsFile->addFixableError('Useless semicolon.', $semicolonPointer, self::CODE_USELESS_SEMICOLON); if (!$fix) { return; } $this->removeUselessSemicolon($phpcsFile, $semicolonPointer); } private function checkSemicolonAtTheBeginningOfScope(File $phpcsFile, int $semicolonPointer): void { $tokens = $phpcsFile->getTokens(); $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $semicolonPointer - 1); if (!in_array($tokens[$previousPointer]['code'], [T_OPEN_TAG, T_OPEN_CURLY_BRACKET], true)) { return; } $fix = $phpcsFile->addFixableError('Useless semicolon.', $semicolonPointer, self::CODE_USELESS_SEMICOLON); if (!$fix) { return; } $this->removeUselessSemicolon($phpcsFile, $semicolonPointer); } private function checkSemicolonAfterScope(File $phpcsFile, int $semicolonPointer): void { $tokens = $phpcsFile->getTokens(); $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $semicolonPointer - 1); if ($tokens[$previousPointer]['code'] !== T_CLOSE_CURLY_BRACKET) { return; } if (!array_key_exists('scope_condition', $tokens[$previousPointer])) { return; } $scopeOpenerPointer = $tokens[$previousPointer]['scope_condition']; if (in_array($tokens[$scopeOpenerPointer]['code'], [T_CLOSURE, T_FN, T_ANON_CLASS, T_MATCH], true)) { return; } $fix = $phpcsFile->addFixableError('Useless semicolon.', $semicolonPointer, self::CODE_USELESS_SEMICOLON); if (!$fix) { return; } $this->removeUselessSemicolon($phpcsFile, $semicolonPointer); } private function removeUselessSemicolon(File $phpcsFile, int $semicolonPointer): void { $tokens = $phpcsFile->getTokens(); $fixStartPointer = $semicolonPointer; do { if ($tokens[$fixStartPointer - 1]['code'] !== T_WHITESPACE) { break; } $fixStartPointer--; if ($tokens[$fixStartPointer]['content'] === $phpcsFile->eolChar) { break; } } while (true); $fixEndPointer = $semicolonPointer; while ($fixEndPointer < count($tokens) - 1) { if ($tokens[$fixEndPointer + 1]['code'] !== T_WHITESPACE) { break; } if ($tokens[$fixEndPointer + 1]['content'] === $phpcsFile->eolChar) { break; } $fixEndPointer++; } $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $fixStartPointer, $fixEndPointer); $phpcsFile->fixer->endChangeset(); } } PK41]ZFHcoding-standard/SlevomatCodingStandard/Sniffs/PHP/RequireNowdocSniff.phpnu[ */ public function register(): array { return [ T_START_HEREDOC, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $heredocStartPointer */ public function process(File $phpcsFile, $heredocStartPointer): void { $tokens = $phpcsFile->getTokens(); $heredocEndPointer = TokenHelper::findNext($phpcsFile, T_END_HEREDOC, $heredocStartPointer + 1); $heredocContentPointers = []; for ($i = $heredocStartPointer + 1; $i < $heredocEndPointer; $i++) { if ($tokens[$i]['code'] === T_HEREDOC) { if (preg_match('~^([^\\\\$]|\\\\[^nrtvef0-7xu])*$~', $tokens[$i]['content']) === 0) { return; } $heredocContentPointers[] = $i; } } $fix = $phpcsFile->addFixableError('Use nowdoc syntax instead of heredoc.', $heredocStartPointer, self::CODE_REQUIRED_NOWDOC); if (!$fix) { return; } $nowdocStart = preg_replace('~^<<<"?(\w+)"?~', '<<<\'$1\'', $tokens[$heredocStartPointer]['content']); $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $heredocStartPointer, $nowdocStart); foreach ($heredocContentPointers as $heredocContentPointer) { $heredocContent = $tokens[$heredocContentPointer]['content']; $nowdocContent = preg_replace( '~\\\\(\\\\[nrtvef]|\$|\\\\|\\\\[0-7]{1,3}|\\\\x[0-9A-Fa-f]{1,2}|\\\\u\{[0-9A-Fa-f]+\})~', '$1', $heredocContent, ); FixerHelper::replace($phpcsFile, $heredocContentPointer, $nowdocContent); } $phpcsFile->fixer->endChangeset(); } } PK41]~؄Xcoding-standard/SlevomatCodingStandard/Sniffs/PHP/DisallowDirectMagicInvokeCallSniff.phpnu[ */ public function register(): array { return [ T_STRING, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $stringPointer */ public function process(File $phpcsFile, $stringPointer): void { $tokens = $phpcsFile->getTokens(); $parenthesisOpenerPointer = TokenHelper::findNextEffective($phpcsFile, $stringPointer + 1); if ($tokens[$parenthesisOpenerPointer]['code'] !== T_OPEN_PARENTHESIS) { return; } if (strtolower($tokens[$stringPointer]['content']) !== '__invoke') { return; } $objectOperator = TokenHelper::findPreviousEffective($phpcsFile, $stringPointer - 1); if ($tokens[$objectOperator]['code'] !== T_OBJECT_OPERATOR) { return; } $fix = $phpcsFile->addFixableError( 'Direct call of __invoke() is disallowed.', $stringPointer, self::CODE_DISALLOWED_DIRECT_MAGIC_INVOKE_CALL, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $objectOperator, $parenthesisOpenerPointer - 1); $phpcsFile->fixer->endChangeset(); } } PK41][E//Dcoding-standard/SlevomatCodingStandard/Sniffs/PHP/ShortListSniff.phpnu[ */ public function register(): array { return [T_LIST]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { $fix = $phpcsFile->addFixableError('list(...) is forbidden, use [...] instead.', $pointer, self::CODE_LONG_LIST_USED); if (!$fix) { return; } $tokens = $phpcsFile->getTokens(); /** @var int $startPointer */ $startPointer = TokenHelper::findNext($phpcsFile, [T_OPEN_PARENTHESIS], $pointer + 1); $endPointer = $tokens[$startPointer]['parenthesis_closer']; $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $pointer, $startPointer - 1); FixerHelper::replace($phpcsFile, $startPointer, '['); FixerHelper::replace($phpcsFile, $endPointer, ']'); $phpcsFile->fixer->endChangeset(); } } PK41]ywV]coding-standard/SlevomatCodingStandard/Sniffs/PHP/OptimizedFunctionsWithoutUnpackingSniff.phpnu[ */ public function register(): array { return TokenHelper::ONLY_NAME_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { $previousTokenPointer = TokenHelper::findPreviousEffective($phpcsFile, $pointer - 1); $openBracketPointer = TokenHelper::findNextEffective($phpcsFile, $pointer + 1); $tokens = $phpcsFile->getTokens(); if ($openBracketPointer === null || $tokens[$openBracketPointer]['code'] !== T_OPEN_PARENTHESIS) { return; } if (in_array($tokens[$previousTokenPointer]['code'], [T_FUNCTION, T_NEW, T_OBJECT_OPERATOR], true)) { return; } /** @var int $tokenBeforeInvocationPointer */ $tokenBeforeInvocationPointer = TokenHelper::findPreviousExcluding($phpcsFile, TokenHelper::NAME_TOKEN_CODES, $pointer); $invokedName = TokenHelper::getContent($phpcsFile, $tokenBeforeInvocationPointer + 1, $pointer); $useName = sprintf('function %s', $invokedName); $uses = UseStatementHelper::getUseStatementsForPointer($phpcsFile, $pointer); if ($invokedName[0] === '\\') { $invokedName = substr($invokedName, 1); } elseif (array_key_exists($useName, $uses) && $uses[$useName]->isFunction()) { $invokedName = $uses[$useName]->getFullyQualifiedTypeName(); } elseif (NamespaceHelper::findCurrentNamespaceName($phpcsFile, $pointer) !== null) { return; } if (!in_array($invokedName, FunctionHelper::SPECIAL_FUNCTIONS, true)) { return; } $closeBracketPointer = $tokens[$openBracketPointer]['parenthesis_closer']; if (TokenHelper::findNextEffective($phpcsFile, $openBracketPointer + 1, $closeBracketPointer + 1) === $closeBracketPointer) { return; } $pointerBeforeCloseBracket = TokenHelper::findPreviousEffective($phpcsFile, $closeBracketPointer - 1); $startPointer = $tokens[$pointerBeforeCloseBracket]['code'] === T_COMMA ? $pointerBeforeCloseBracket : $closeBracketPointer; do { $lastArgumentSeparatorPointer = TokenHelper::findPrevious($phpcsFile, [T_COMMA], $startPointer - 1, $openBracketPointer); $startPointer = $lastArgumentSeparatorPointer; } while ( $lastArgumentSeparatorPointer !== null && $tokens[$lastArgumentSeparatorPointer]['level'] !== $tokens[$openBracketPointer]['level'] ); $lastArgumentSeparatorPointer ??= $openBracketPointer; /** @var int $nextTokenAfterSeparatorPointer */ $nextTokenAfterSeparatorPointer = TokenHelper::findNextEffective( $phpcsFile, $lastArgumentSeparatorPointer + 1, $closeBracketPointer, ); if ($tokens[$nextTokenAfterSeparatorPointer]['code'] !== T_ELLIPSIS) { return; } if (TokenHelper::findNextEffective($phpcsFile, $nextTokenAfterSeparatorPointer + 1) === $closeBracketPointer) { // First class callables return; } $phpcsFile->addError( sprintf('Function %s is specialized by PHP and should not use argument unpacking.', $invokedName), $nextTokenAfterSeparatorPointer, self::CODE_UNPACKING_USED, ); } } PK41]Kcoding-standard/SlevomatCodingStandard/Sniffs/PHP/ReferenceSpacingSniff.phpnu[ */ public function register(): array { return [ T_BITWISE_AND, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $referencePointer */ public function process(File $phpcsFile, $referencePointer): void { $this->spacesCountAfterReference = SniffSettingsHelper::normalizeInteger($this->spacesCountAfterReference); if (!$this->isReference($phpcsFile, $referencePointer)) { return; } $pointerAfterWhitespace = TokenHelper::findNextNonWhitespace($phpcsFile, $referencePointer + 1); $whitespace = TokenHelper::getContent($phpcsFile, $referencePointer + 1, $pointerAfterWhitespace - 1); $actualSpacesCount = strlen($whitespace); if ($this->spacesCountAfterReference === $actualSpacesCount) { return; } $errorMessage = $this->spacesCountAfterReference === 0 ? 'There must be no whitespace after reference.' : sprintf( 'There must be exactly %d whitespace%s after reference.', $this->spacesCountAfterReference, $this->spacesCountAfterReference !== 1 ? 's' : '', ); $fix = $phpcsFile->addFixableError($errorMessage, $referencePointer, self::CODE_INCORRECT_SPACES_AFTER_REFERENCE); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::add($phpcsFile, $referencePointer, str_repeat(' ', $this->spacesCountAfterReference)); FixerHelper::removeBetween($phpcsFile, $referencePointer, $pointerAfterWhitespace); $phpcsFile->fixer->endChangeset(); } private function isReference(File $phpcsFile, int $referencePointer): bool { $tokens = $phpcsFile->getTokens(); $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $referencePointer - 1); if (in_array($tokens[$previousPointer]['code'], TokenHelper::FUNCTION_TOKEN_CODES, true)) { return true; } $previousParenthesisOpenerPointer = TokenHelper::findPrevious($phpcsFile, T_OPEN_PARENTHESIS, $referencePointer - 1); if ( $previousParenthesisOpenerPointer !== null && $tokens[$previousParenthesisOpenerPointer]['parenthesis_closer'] > $referencePointer ) { if (array_key_exists('parenthesis_owner', $tokens[$previousParenthesisOpenerPointer])) { $parenthesisOwnerPointer = $tokens[$previousParenthesisOpenerPointer]['parenthesis_owner']; if (in_array($tokens[$parenthesisOwnerPointer]['code'], TokenHelper::FUNCTION_TOKEN_CODES, true)) { return true; } } $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $previousParenthesisOpenerPointer - 1); if ( $pointerBeforeParenthesisOpener !== null && $tokens[$pointerBeforeParenthesisOpener]['code'] === T_USE ) { return true; } } /** @var int $variableStartPointer */ $variableStartPointer = TokenHelper::findNextEffective($phpcsFile, $referencePointer + 1); $variableEndPointer = IdentificatorHelper::findEndPointer($phpcsFile, $variableStartPointer); if ($variableEndPointer === null) { return false; } $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $referencePointer - 1); return in_array($tokens[$previousPointer]['code'], [T_EQUAL, T_DOUBLE_ARROW, T_OPEN_SHORT_ARRAY, T_COMMA, T_AS], true); } } PK41]#ӕ88Scoding-standard/SlevomatCodingStandard/Sniffs/PHP/RequireExplicitAssertionSniff.phpnu[ */ public function register(): array { return [ T_DOC_COMMENT_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $docCommentOpenPointer */ public function process(File $phpcsFile, $docCommentOpenPointer): void { $tokens = $phpcsFile->getTokens(); $tokenCodes = [T_VARIABLE, T_FOREACH, T_WHILE, T_LIST, T_OPEN_SHORT_ARRAY]; $commentClosePointer = $tokens[$docCommentOpenPointer]['comment_closer']; $codePointer = TokenHelper::findFirstNonWhitespaceOnNextLine($phpcsFile, $commentClosePointer); if ($codePointer === null || !in_array($tokens[$codePointer]['code'], $tokenCodes, true)) { $firstPointerOnPreviousLine = TokenHelper::findFirstNonWhitespaceOnPreviousLine($phpcsFile, $docCommentOpenPointer); if ( $firstPointerOnPreviousLine === null || !in_array($tokens[$firstPointerOnPreviousLine]['code'], $tokenCodes, true) ) { return; } $codePointer = $firstPointerOnPreviousLine; } /** @var list> $variableAnnotations */ $variableAnnotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenPointer, '@var'); if (count($variableAnnotations) === 0) { return; } foreach (array_reverse($variableAnnotations) as $variableAnnotation) { if ($variableAnnotation->isInvalid()) { continue; } $variableName = $variableAnnotation->getValue()->variableName; if ($variableName === '') { continue; } $variableAnnotationType = $variableAnnotation->getValue()->type; if ( $variableAnnotationType instanceof UnionTypeNode || $variableAnnotationType instanceof IntersectionTypeNode ) { foreach ($variableAnnotationType->types as $typeNode) { if (!$this->isValidTypeNode($typeNode)) { continue 2; } } } elseif (!$this->isValidTypeNode($variableAnnotationType)) { continue; } /** @var IdentifierTypeNode|ThisTypeNode|UnionTypeNode|GenericTypeNode $variableAnnotationType */ $variableAnnotationType = $variableAnnotationType; $assertion = $this->createAssert($variableName, $variableAnnotationType); if ($assertion === null) { continue; } if ($tokens[$codePointer]['code'] === T_VARIABLE) { $pointerAfterVariable = TokenHelper::findNextEffective($phpcsFile, $codePointer + 1); if ($tokens[$pointerAfterVariable]['code'] !== T_EQUAL) { continue; } if ($variableName !== $tokens[$codePointer]['content']) { continue; } $pointerToAddAssertion = $this->getNextSemicolonInSameScope($phpcsFile, $codePointer, $codePointer + 1); $indentation = IndentationHelper::getIndentation($phpcsFile, $docCommentOpenPointer); } elseif ($tokens[$codePointer]['code'] === T_LIST) { $listParenthesisOpener = TokenHelper::findNextEffective($phpcsFile, $codePointer + 1); $variablePointerInList = TokenHelper::findNextContent( $phpcsFile, T_VARIABLE, $variableName, $listParenthesisOpener + 1, $tokens[$listParenthesisOpener]['parenthesis_closer'], ); if ($variablePointerInList === null) { continue; } $pointerToAddAssertion = $this->getNextSemicolonInSameScope($phpcsFile, $codePointer, $codePointer + 1); $indentation = IndentationHelper::getIndentation($phpcsFile, $docCommentOpenPointer); } elseif ($tokens[$codePointer]['code'] === T_OPEN_SHORT_ARRAY) { $pointerAfterList = TokenHelper::findNextEffective($phpcsFile, $tokens[$codePointer]['bracket_closer'] + 1); if ($tokens[$pointerAfterList]['code'] !== T_EQUAL) { continue; } $variablePointerInList = TokenHelper::findNextContent( $phpcsFile, T_VARIABLE, $variableName, $codePointer + 1, $tokens[$codePointer]['bracket_closer'], ); if ($variablePointerInList === null) { continue; } $pointerToAddAssertion = $this->getNextSemicolonInSameScope( $phpcsFile, $codePointer, $tokens[$codePointer]['bracket_closer'] + 1, ); $indentation = IndentationHelper::getIndentation($phpcsFile, $docCommentOpenPointer); } else { if ($tokens[$codePointer]['code'] === T_WHILE) { $variablePointerInWhile = TokenHelper::findNextContent( $phpcsFile, T_VARIABLE, $variableName, $tokens[$codePointer]['parenthesis_opener'] + 1, $tokens[$codePointer]['parenthesis_closer'], ); if ($variablePointerInWhile === null) { continue; } $pointerAfterVariableInWhile = TokenHelper::findNextEffective($phpcsFile, $variablePointerInWhile + 1); if ($tokens[$pointerAfterVariableInWhile]['code'] !== T_EQUAL) { continue; } } else { $asPointer = TokenHelper::findNext( $phpcsFile, T_AS, $tokens[$codePointer]['parenthesis_opener'] + 1, $tokens[$codePointer]['parenthesis_closer'], ); $variablePointerInForeach = TokenHelper::findNextContent( $phpcsFile, T_VARIABLE, $variableName, $asPointer + 1, $tokens[$codePointer]['parenthesis_closer'], ); if ($variablePointerInForeach === null) { continue; } } $pointerToAddAssertion = $tokens[$codePointer]['scope_opener']; $indentation = IndentationHelper::addIndentation($phpcsFile, IndentationHelper::getIndentation($phpcsFile, $codePointer)); } $fix = $phpcsFile->addFixableError( 'Use assertion instead of inline documentation comment.', $variableAnnotation->getStartPointer(), self::CODE_REQUIRED_EXPLICIT_ASSERTION, ); if (!$fix) { continue; } $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $variableAnnotation->getStartPointer(), $variableAnnotation->getEndPointer()); $docCommentUseful = false; $docCommentClosePointer = $tokens[$docCommentOpenPointer]['comment_closer']; for ($i = $docCommentOpenPointer + 1; $i < $docCommentClosePointer; $i++) { $tokenContent = trim($phpcsFile->fixer->getTokenContent($i)); if ($tokenContent === '' || $tokenContent === '*') { continue; } $docCommentUseful = true; break; } $pointerBeforeDocComment = TokenHelper::findPreviousContent( $phpcsFile, T_WHITESPACE, $phpcsFile->eolChar, $docCommentOpenPointer - 1, ); $pointerAfterDocComment = TokenHelper::findNextContent( $phpcsFile, T_WHITESPACE, $phpcsFile->eolChar, $docCommentClosePointer + 1, ); if (!$docCommentUseful) { FixerHelper::removeBetweenIncluding($phpcsFile, $pointerBeforeDocComment + 1, $pointerAfterDocComment); } if ( $pointerToAddAssertion < $docCommentClosePointer && array_key_exists($pointerAfterDocComment + 1, $tokens) ) { FixerHelper::addBefore($phpcsFile, $pointerAfterDocComment + 1, $indentation . $assertion . $phpcsFile->eolChar); } else { FixerHelper::add($phpcsFile, $pointerToAddAssertion, $phpcsFile->eolChar . $indentation . $assertion); } $phpcsFile->fixer->endChangeset(); } } private function isValidTypeNode(TypeNode $typeNode): bool { if ($typeNode instanceof ThisTypeNode) { return true; } if ($typeNode instanceof IdentifierTypeNode) { return true; } if ( $this->enableIntegerRanges && $typeNode instanceof GenericTypeNode && $typeNode->type->name === 'int' && count($typeNode->genericTypes) === 2 ) { foreach ($typeNode->genericTypes as $genericType) { $isValid = ($genericType instanceof IdentifierTypeNode && in_array($genericType->name, ['min', 'max'], true)) || ($genericType instanceof ConstTypeNode && $genericType->constExpr instanceof ConstExprIntegerNode); if (!$isValid) { return false; } } return true; } return false; } private function getNextSemicolonInSameScope(File $phpcsFile, int $scopePointer, int $searchAt): int { $semicolonPointer = null; do { $semicolonPointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $searchAt); if (ScopeHelper::isInSameScope($phpcsFile, $scopePointer, $semicolonPointer)) { break; } $searchAt = $semicolonPointer + 1; } while (true); return $semicolonPointer; } /** * @param IdentifierTypeNode|ThisTypeNode|UnionTypeNode|IntersectionTypeNode|GenericTypeNode $typeNode */ private function createAssert(string $variableName, TypeNode $typeNode): ?string { $conditions = []; if ( $typeNode instanceof IdentifierTypeNode || $typeNode instanceof ThisTypeNode || $typeNode instanceof GenericTypeNode ) { $conditions = $this->createConditions($variableName, $typeNode); return $conditions !== [] ? sprintf('\assert(%s);', implode(' || ', $conditions)) : null; } /** @var IdentifierTypeNode|ThisTypeNode|GenericTypeNode $innerTypeNode */ foreach ($typeNode->types as $innerTypeNode) { $innerTypeConditions = $this->createConditions($variableName, $innerTypeNode); if ($innerTypeConditions === []) { return null; } $conditions = array_merge($conditions, $innerTypeConditions); } $operator = $typeNode instanceof IntersectionTypeNode ? '&&' : '||'; $formattedConditions = []; foreach (array_unique($conditions) as $condition) { $formattedConditions[] = $operator === '||' && strpos($condition, '&&') !== false ? sprintf('(%s)', $condition) : $condition; } return sprintf('\assert(%s);', implode(sprintf(' %s ', $operator), $formattedConditions)); } /** * @param IdentifierTypeNode|ThisTypeNode|GenericTypeNode $typeNode * @return list */ private function createConditions(string $variableName, TypeNode $typeNode): array { if ($typeNode instanceof GenericTypeNode) { $conditions = [sprintf('\is_int(%s)', $variableName)]; if ($typeNode->genericTypes[0] instanceof ConstTypeNode) { $conditions[] = sprintf('%s >= %s', $variableName, (string) $typeNode->genericTypes[0]); } if ($typeNode->genericTypes[1] instanceof ConstTypeNode) { $conditions[] = sprintf('%s <= %s', $variableName, (string) $typeNode->genericTypes[1]); } return [implode(' && ', $conditions)]; } if ($typeNode instanceof ThisTypeNode) { return [sprintf('%s instanceof $this', $variableName)]; } if ($typeNode->name === 'self') { return [sprintf('%s instanceof %s', $variableName, $typeNode->name)]; } if ($typeNode->name === 'static') { return [sprintf('%s instanceof static', $variableName)]; } if (in_array($typeNode->name, ['true', 'false', 'null'], true)) { return [sprintf('%s === %s', $variableName, $typeNode->name)]; } if ( $typeNode->name === 'mixed' || TypeHintHelper::isVoidTypeHint($typeNode->name) || TypeHintHelper::isNeverTypeHint($typeNode->name) ) { return []; } if (TypeHintHelper::isSimpleTypeHint($typeNode->name)) { return [sprintf('\is_%s(%s)', TypeHintHelper::convertLongSimpleTypeHintToShort($typeNode->name), $variableName)]; } if (in_array($typeNode->name, ['resource', 'object'], true)) { return [sprintf('\is_%s(%s)', $typeNode->name, $variableName)]; } if ($typeNode->name === 'numeric') { return [ sprintf('\is_numeric(%s)', $variableName), ]; } if ($typeNode->name === 'scalar') { return [ sprintf('\is_int(%s)', $variableName), sprintf('\is_float(%s)', $variableName), sprintf('\is_bool(%s)', $variableName), sprintf('\is_string(%s)', $variableName), ]; } if ($this->enableIntegerRanges) { if ($typeNode->name === 'positive-int' || $typeNode->name === 'non-negative-int') { return [sprintf('\is_int(%1$s) && %1$s > 0', $variableName)]; } if ($typeNode->name === 'negative-int' || $typeNode->name === 'non-positive-int') { return [sprintf('\is_int(%1$s) && %1$s < 0', $variableName)]; } if ($typeNode->name === 'literal-int') { return [sprintf('\is_int(%1$s)', $variableName)]; } } if ( $this->enableAdvancedStringTypes && preg_match('~-string$~', $typeNode->name) === 1 && preg_match('~^(?:class|trait|enum)-string$~', $typeNode->name) !== 1 ) { $conditions = [sprintf('\is_string(%s)', $variableName)]; if ($typeNode->name === 'callable-string') { $conditions[] = sprintf('\is_callable(%s)', $variableName); } elseif ($typeNode->name === 'numeric-string') { $conditions[] = sprintf('\is_numeric(%s)', $variableName); } elseif (preg_match('~^non-empty-~i', $typeNode->name) === 1) { $conditions[] = sprintf("%s !== ''", $variableName); } elseif (preg_match('~^non-falsy-~i', $typeNode->name) === 1) { $conditions[] = sprintf('(bool) %s === true', $variableName); } return [implode(' && ', $conditions)]; } if (TypeHintHelper::isSimpleUnofficialTypeHints($typeNode->name)) { return []; } return [sprintf('%s instanceof %s', $variableName, $typeNode->name)]; } } PK41]dzieeVcoding-standard/SlevomatCodingStandard/Sniffs/Strings/DisallowVariableParsingSniff.phpnu[ */ public function register(): array { return [ T_DOUBLE_QUOTED_STRING, T_HEREDOC, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $stringPointer */ public function process(File $phpcsFile, $stringPointer): void { if (!$this->disallowDollarCurlySyntax && !$this->disallowCurlyDollarSyntax && !$this->disallowSimpleSyntax) { throw new UnexpectedValueException('No option is set.'); } $tokens = $phpcsFile->getTokens(); $tokenContent = $tokens[$stringPointer]['content']; if (strpos($tokenContent, '$') === false) { return; } $stringTokens = $tokens[$stringPointer]['code'] === T_HEREDOC ? token_get_all('disallowDollarCurlySyntax && $this->getTokenContent($stringToken) === '${') { $usedVariable = $stringToken[1]; for ($j = $i + 1; $j < count($stringTokens); $j++) { $usedVariable .= $this->getTokenContent($stringTokens[$j]); if ($this->getTokenContent($stringTokens[$j]) === '}') { $phpcsFile->addError( sprintf( 'Using variable syntax "${...}" inside string is disallowed as syntax "${...}" is deprecated as of PHP 8.2, found "%s".', $usedVariable, ), $stringPointer, self::CODE_DISALLOWED_DOLLAR_CURLY_SYNTAX, ); break; } } } elseif ($stringToken[0] === T_VARIABLE) { if ($this->disallowCurlyDollarSyntax && $this->getTokenContent($stringTokens[$i - 1]) === '{') { $usedVariable = $stringToken[1]; for ($j = $i + 1; $j < count($stringTokens); $j++) { $stringTokenContent = $this->getTokenContent($stringTokens[$j]); if ($stringTokenContent === '}') { break; } $usedVariable .= $stringTokenContent; } $phpcsFile->addError( sprintf( 'Using variable syntax "{$...}" inside string is disallowed, found "{%s}".', $usedVariable, ), $stringPointer, self::CODE_DISALLOWED_CURLY_DOLLAR_SYNTAX, ); } elseif ($this->disallowSimpleSyntax) { $error = true; for ($j = $i - 1; $j >= 0; $j--) { $stringTokenContent = $this->getTokenContent($stringTokens[$j]); if (in_array($stringTokenContent, ['{', '${'], true)) { $error = false; break; } if ($stringTokenContent === '}') { break; } } if ($error) { $phpcsFile->addError( sprintf( 'Using variable syntax "$..." inside string is disallowed, found "%s".', $this->getTokenContent($stringToken), ), $stringPointer, self::CODE_DISALLOWED_SIMPLE_SYNTAX, ); } } } } } /** * @param array{0: int, 1: string}|string $token */ private function getTokenContent($token): string { return is_array($token) ? $token[1] : $token; } } PK41]u4VVRcoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/ParameterTypeHintSniff.phpnu[ */ public array $traversableTypeHints = []; /** @var list|null */ private ?array $normalizedTraversableTypeHints = null; /** * @return array */ public function register(): array { return [ T_FUNCTION, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $functionPointer */ public function process(File $phpcsFile, $functionPointer): void { $this->enableObjectTypeHint = SniffSettingsHelper::isEnabledByPhpVersion($this->enableObjectTypeHint, 70200); $this->enableMixedTypeHint = SniffSettingsHelper::isEnabledByPhpVersion($this->enableMixedTypeHint, 80000); $this->enableUnionTypeHint = SniffSettingsHelper::isEnabledByPhpVersion($this->enableUnionTypeHint, 80000); $this->enableIntersectionTypeHint = SniffSettingsHelper::isEnabledByPhpVersion($this->enableIntersectionTypeHint, 80100); $this->enableStandaloneNullTrueFalseTypeHints = SniffSettingsHelper::isEnabledByPhpVersion( $this->enableStandaloneNullTrueFalseTypeHints, 80200, ); if (SuppressHelper::isSniffSuppressed($phpcsFile, $functionPointer, self::NAME)) { return; } if (DocCommentHelper::hasInheritdocAnnotation($phpcsFile, $functionPointer)) { return; } $parametersTypeHints = FunctionHelper::getParametersTypeHints($phpcsFile, $functionPointer); $parametersAnnotations = FunctionHelper::getValidParametersAnnotations($phpcsFile, $functionPointer); $prefixedParametersAnnotations = FunctionHelper::getValidPrefixedParametersAnnotations($phpcsFile, $functionPointer); $this->checkTypeHints($phpcsFile, $functionPointer, $parametersTypeHints, $parametersAnnotations, $prefixedParametersAnnotations); $this->checkTraversableTypeHintSpecification( $phpcsFile, $functionPointer, $parametersTypeHints, $parametersAnnotations, $prefixedParametersAnnotations, ); $this->checkUselessAnnotations($phpcsFile, $functionPointer, $parametersTypeHints, $parametersAnnotations); } /** * @param array $parametersTypeHints * @param array|Annotation|Annotation> $parametersAnnotations * @param array|Annotation> $prefixedParametersAnnotations */ private function checkTypeHints( File $phpcsFile, int $functionPointer, array $parametersTypeHints, array $parametersAnnotations, array $prefixedParametersAnnotations ): void { $suppressNameAnyTypeHint = self::getSniffName(self::CODE_MISSING_ANY_TYPE_HINT); $isSuppressedAnyTypeHint = SuppressHelper::isSniffSuppressed($phpcsFile, $functionPointer, $suppressNameAnyTypeHint); $suppressNameNativeTypeHint = $this->getSniffName(self::CODE_MISSING_NATIVE_TYPE_HINT); $isSuppressedNativeTypeHint = SuppressHelper::isSniffSuppressed($phpcsFile, $functionPointer, $suppressNameNativeTypeHint); $suppressedErrors = 0; $parametersWithoutTypeHint = array_keys( array_filter($parametersTypeHints, static fn (?TypeHint $parameterTypeHint = null): bool => $parameterTypeHint === null), ); $tokens = $phpcsFile->getTokens(); $isConstructor = FunctionHelper::isMethod($phpcsFile, $functionPointer) && strtolower(FunctionHelper::getName($phpcsFile, $functionPointer)) === '__construct'; foreach ($parametersWithoutTypeHint as $parameterName) { $isPropertyPromotion = false; if ($isConstructor) { $parameterPointer = TokenHelper::findNextContent( $phpcsFile, T_VARIABLE, $parameterName, $tokens[$functionPointer]['parenthesis_opener'], $tokens[$functionPointer]['parenthesis_closer'], ); $pointerBeforeParameter = TokenHelper::findPrevious($phpcsFile, [T_COMMA, T_OPEN_PARENTHESIS], $parameterPointer - 1); $visibilityPointer = TokenHelper::findNextEffective($phpcsFile, $pointerBeforeParameter + 1); $isPropertyPromotion = in_array($tokens[$visibilityPointer]['code'], Tokens::$scopeModifiers, true); } if ( !array_key_exists($parameterName, $parametersAnnotations) || $parametersAnnotations[$parameterName]->getValue() instanceof TypelessParamTagValueNode ) { if (array_key_exists($parameterName, $prefixedParametersAnnotations)) { continue; } if ($isSuppressedAnyTypeHint) { $suppressedErrors++; continue; } $phpcsFile->addError( sprintf( '%s %s() does not have parameter type hint nor @param annotation for its parameter %s.', FunctionHelper::getTypeLabel($phpcsFile, $functionPointer), FunctionHelper::getFullyQualifiedName($phpcsFile, $functionPointer), $parameterName, ), $functionPointer, self::CODE_MISSING_ANY_TYPE_HINT, ); continue; } if (AttributeHelper::hasAttribute($phpcsFile, $functionPointer, '\Override')) { continue; } $parameterTypeNode = $parametersAnnotations[$parameterName]->getValue()->type; if ( $parameterTypeNode instanceof IdentifierTypeNode && strtolower($parameterTypeNode->name) === 'null' && !$this->enableStandaloneNullTrueFalseTypeHints ) { continue; } $originalParameterTypeNode = $parameterTypeNode; if ($parameterTypeNode instanceof NullableTypeNode) { $parameterTypeNode = $parameterTypeNode->type; } $canTryUnionTypeHint = $this->enableUnionTypeHint && $parameterTypeNode instanceof UnionTypeNode; $typeHints = []; $traversableTypeHints = []; $nullableParameterTypeHint = false; if (AnnotationTypeHelper::containsOneType($parameterTypeNode)) { /** @var ArrayTypeNode|ArrayShapeNode|ObjectShapeNode|IdentifierTypeNode|ThisTypeNode|GenericTypeNode|CallableTypeNode|ConstTypeNode $parameterTypeNode */ $parameterTypeNode = $parameterTypeNode; $typeHints[] = AnnotationTypeHelper::getTypeHintFromOneType( $parameterTypeNode, false, $this->enableStandaloneNullTrueFalseTypeHints, ); } elseif ( $parameterTypeNode instanceof UnionTypeNode || $parameterTypeNode instanceof IntersectionTypeNode ) { $traversableTypeHints = []; foreach ($parameterTypeNode->types as $typeNode) { if (!AnnotationTypeHelper::containsOneType($typeNode)) { continue 2; } /** @var ArrayTypeNode|ArrayShapeNode|ObjectShapeNode|IdentifierTypeNode|ThisTypeNode|GenericTypeNode|CallableTypeNode|ConstTypeNode $typeNode */ $typeNode = $typeNode; $typeHint = AnnotationTypeHelper::getTypeHintFromOneType($typeNode, $canTryUnionTypeHint); if (strtolower($typeHint) === 'null') { $nullableParameterTypeHint = true; continue; } $isTraversable = TypeHintHelper::isTraversableType( TypeHintHelper::getFullyQualifiedTypeHint($phpcsFile, $functionPointer, $typeHint), $this->getTraversableTypeHints(), ); if ( !$typeNode instanceof ArrayTypeNode && !$typeNode instanceof ArrayShapeNode && $isTraversable ) { $traversableTypeHints[] = $typeHint; } $typeHints[] = $typeHint; } $traversableTypeHints = array_values(array_unique($traversableTypeHints)); if (count($traversableTypeHints) > 1 && !$canTryUnionTypeHint) { continue; } } $typeHints = array_values(array_unique($typeHints)); if (count($traversableTypeHints) > 0) { /** @var UnionTypeNode|IntersectionTypeNode $parameterTypeNode */ $parameterTypeNode = $parameterTypeNode; $itemsSpecificationTypeHint = AnnotationTypeHelper::getItemsSpecificationTypeFromType($parameterTypeNode); if ($itemsSpecificationTypeHint !== null) { $typeHints = AnnotationTypeHelper::getTraversableTypeHintsFromType( $parameterTypeNode, $phpcsFile, $functionPointer, $this->getTraversableTypeHints(), $canTryUnionTypeHint, ); } } if (count($typeHints) === 0) { continue; } $typeHintsWithConvertedUnion = []; foreach ($typeHints as $typeHint) { if ($this->enableUnionTypeHint && TypeHintHelper::isUnofficialUnionTypeHint($typeHint)) { $canTryUnionTypeHint = true; array_push( $typeHintsWithConvertedUnion, ...TypeHintHelper::convertUnofficialUnionTypeHintToOfficialTypeHints($typeHint), ); } else { $typeHintsWithConvertedUnion[] = $typeHint; } } $typeHintsWithConvertedUnion = array_unique($typeHintsWithConvertedUnion); if ( count($typeHintsWithConvertedUnion) > 1 && ( ($parameterTypeNode instanceof UnionTypeNode && !$canTryUnionTypeHint) || ($parameterTypeNode instanceof IntersectionTypeNode && !$this->enableIntersectionTypeHint) ) ) { continue; } foreach ($typeHintsWithConvertedUnion as $typeHintNo => $typeHint) { if ($canTryUnionTypeHint && $typeHint === 'false') { continue; } if ($isPropertyPromotion && $typeHint === 'callable') { continue 2; } if (!TypeHintHelper::isValidTypeHint( $typeHint, $this->enableObjectTypeHint, false, $this->enableMixedTypeHint, $this->enableStandaloneNullTrueFalseTypeHints, )) { continue 2; } if (TypeHintHelper::isTypeDefinedInAnnotation($phpcsFile, $functionPointer, $typeHint)) { continue 2; } $typeHintsWithConvertedUnion[$typeHintNo] = TypeHintHelper::convertLongSimpleTypeHintToShort($typeHint); } if ($originalParameterTypeNode instanceof NullableTypeNode) { $nullableParameterTypeHint = true; } if ($isSuppressedNativeTypeHint) { $suppressedErrors++; continue; } $fix = $phpcsFile->addFixableError( sprintf( '%s %s() does not have native type hint for its parameter %s but it should be possible to add it based on @param annotation "%s".', FunctionHelper::getTypeLabel($phpcsFile, $functionPointer), FunctionHelper::getFullyQualifiedName($phpcsFile, $functionPointer), $parameterName, AnnotationTypeHelper::print($parameterTypeNode), ), $functionPointer, self::CODE_MISSING_NATIVE_TYPE_HINT, ); if (!$fix) { continue; } if (in_array('mixed', $typeHintsWithConvertedUnion, true)) { $parameterTypeHint = 'mixed'; } elseif ($originalParameterTypeNode instanceof IntersectionTypeNode) { $parameterTypeHint = implode('&', $typeHintsWithConvertedUnion); } else { $parameterTypeHint = implode('|', $typeHintsWithConvertedUnion); if ($nullableParameterTypeHint) { if (count($typeHintsWithConvertedUnion) > 1) { $parameterTypeHint .= '|null'; } else { $parameterTypeHint = '?' . $parameterTypeHint; } } } $tokens = $phpcsFile->getTokens(); /** @var int $parameterPointer */ $parameterPointer = TokenHelper::findNextContent( $phpcsFile, T_VARIABLE, $parameterName, $tokens[$functionPointer]['parenthesis_opener'], $tokens[$functionPointer]['parenthesis_closer'], ); $beforeParameterPointer = $parameterPointer; do { $previousPointer = TokenHelper::findPreviousEffective( $phpcsFile, $beforeParameterPointer - 1, $tokens[$functionPointer]['parenthesis_opener'] + 1, ); if ( $previousPointer === null || !in_array($tokens[$previousPointer]['code'], [T_BITWISE_AND, T_ELLIPSIS], true) ) { break; } /** @var int $beforeParameterPointer */ $beforeParameterPointer = $previousPointer; } while (true); $phpcsFile->fixer->beginChangeset(); FixerHelper::addBefore( $phpcsFile, $beforeParameterPointer, sprintf('%s ', $parameterTypeHint), ); $phpcsFile->fixer->endChangeset(); } if ($suppressedErrors > 0) { return; } if ($isSuppressedAnyTypeHint) { $this->reportUselessSuppress($phpcsFile, $functionPointer, $suppressNameAnyTypeHint); } if ($isSuppressedNativeTypeHint) { $this->reportUselessSuppress($phpcsFile, $functionPointer, $suppressNameNativeTypeHint); } } /** * @param array $parametersTypeHints * @param array|Annotation|Annotation> $parametersAnnotations * @param array|Annotation> $prefixedParametersAnnotations */ private function checkTraversableTypeHintSpecification( File $phpcsFile, int $functionPointer, array $parametersTypeHints, array $parametersAnnotations, array $prefixedParametersAnnotations ): void { $suppressName = self::getSniffName(self::CODE_MISSING_TRAVERSABLE_TYPE_HINT_SPECIFICATION); $isSniffSuppressed = SuppressHelper::isSniffSuppressed($phpcsFile, $functionPointer, $suppressName); $suppressUseless = true; foreach ($parametersTypeHints as $parameterName => $parameterTypeHint) { if (array_key_exists($parameterName, $prefixedParametersAnnotations)) { continue; } $hasTraversableTypeHint = false; if ( $parameterTypeHint !== null && TypeHintHelper::isTraversableType( TypeHintHelper::getFullyQualifiedTypeHint($phpcsFile, $functionPointer, $parameterTypeHint->getTypeHint()), $this->getTraversableTypeHints(), ) ) { $hasTraversableTypeHint = true; } elseif ( array_key_exists($parameterName, $parametersAnnotations) && !$parametersAnnotations[$parameterName]->getValue() instanceof TypelessParamTagValueNode && AnnotationTypeHelper::containsTraversableType( $parametersAnnotations[$parameterName]->getValue()->type, $phpcsFile, $functionPointer, $this->getTraversableTypeHints(), ) ) { $hasTraversableTypeHint = true; } if ($hasTraversableTypeHint && !array_key_exists($parameterName, $parametersAnnotations)) { $suppressUseless = false; if (!$isSniffSuppressed) { $phpcsFile->addError( sprintf( '%s %s() does not have @param annotation for its traversable parameter %s.', FunctionHelper::getTypeLabel($phpcsFile, $functionPointer), FunctionHelper::getFullyQualifiedName($phpcsFile, $functionPointer), $parameterName, ), $functionPointer, self::CODE_MISSING_TRAVERSABLE_TYPE_HINT_SPECIFICATION, ); } continue; } if (!array_key_exists($parameterName, $parametersAnnotations)) { continue; } if ($parametersAnnotations[$parameterName]->getValue() instanceof TypelessParamTagValueNode) { continue; } $parameterTypeNode = $parametersAnnotations[$parameterName]->getValue()->type; if ( ( !$hasTraversableTypeHint && !AnnotationTypeHelper::containsTraversableType( $parameterTypeNode, $phpcsFile, $functionPointer, $this->getTraversableTypeHints(), ) ) || AnnotationTypeHelper::containsItemsSpecificationForTraversable( $parameterTypeNode, $phpcsFile, $functionPointer, $this->getTraversableTypeHints(), ) ) { continue; } $suppressUseless = false; if ($isSniffSuppressed) { continue; } $phpcsFile->addError( sprintf( '@param annotation of %s %s() does not specify type hint for items of its traversable parameter %s.', lcfirst(FunctionHelper::getTypeLabel($phpcsFile, $functionPointer)), FunctionHelper::getFullyQualifiedName($phpcsFile, $functionPointer), $parameterName, ), $parametersAnnotations[$parameterName]->getStartPointer(), self::CODE_MISSING_TRAVERSABLE_TYPE_HINT_SPECIFICATION, ); } if ($isSniffSuppressed && $suppressUseless) { $this->reportUselessSuppress($phpcsFile, $functionPointer, $suppressName); } } /** * @param array $parametersTypeHints * @param array $parametersAnnotations */ private function checkUselessAnnotations( File $phpcsFile, int $functionPointer, array $parametersTypeHints, array $parametersAnnotations ): void { $suppressName = self::getSniffName(self::CODE_USELESS_ANNOTATION); $isSniffSuppressed = SuppressHelper::isSniffSuppressed($phpcsFile, $functionPointer, $suppressName); $suppressUseless = true; foreach ($parametersTypeHints as $parameterName => $parameterTypeHint) { if (!array_key_exists($parameterName, $parametersAnnotations)) { continue; } $parameterAnnotation = $parametersAnnotations[$parameterName]; if ($parameterAnnotation->getValue() instanceof TypelessParamTagValueNode) { continue; } if (!AnnotationHelper::isAnnotationUseless( $phpcsFile, $functionPointer, $parameterTypeHint, $parameterAnnotation, $this->getTraversableTypeHints(), $this->enableUnionTypeHint, $this->enableIntersectionTypeHint, $this->enableStandaloneNullTrueFalseTypeHints, )) { continue; } $suppressUseless = false; if ($isSniffSuppressed) { continue; } $fix = $phpcsFile->addFixableError( sprintf( '%s %s() has useless @param annotation for parameter %s.', FunctionHelper::getTypeLabel($phpcsFile, $functionPointer), FunctionHelper::getFullyQualifiedName($phpcsFile, $functionPointer), $parameterName, ), $parameterAnnotation->getStartPointer(), self::CODE_USELESS_ANNOTATION, ); if (!$fix) { continue; } $docCommentOpenPointer = $parameterAnnotation->getValue() instanceof VarTagValueNode ? TokenHelper::findPrevious($phpcsFile, T_DOC_COMMENT_OPEN_TAG, $parameterAnnotation->getStartPointer() - 1) : DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $functionPointer); $starPointer = TokenHelper::findPrevious( $phpcsFile, T_DOC_COMMENT_STAR, $parameterAnnotation->getStartPointer() - 1, $docCommentOpenPointer, ); $changeStart = $starPointer ?? $parameterAnnotation->getStartPointer(); /** @var int $changeEnd */ $changeEnd = TokenHelper::findNext( $phpcsFile, [T_DOC_COMMENT_CLOSE_TAG, T_DOC_COMMENT_STAR], $parameterAnnotation->getEndPointer(), ) - 1; $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $changeStart, $changeEnd); $phpcsFile->fixer->endChangeset(); } if ($isSniffSuppressed && $suppressUseless) { $this->reportUselessSuppress($phpcsFile, $functionPointer, $suppressName); } } private function reportUselessSuppress(File $phpcsFile, int $pointer, string $suppressName): void { $fix = $phpcsFile->addFixableError( sprintf('Useless %s %s', SuppressHelper::ANNOTATION, $suppressName), $pointer, self::CODE_USELESS_SUPPRESS, ); if ($fix) { SuppressHelper::removeSuppressAnnotation($phpcsFile, $pointer, $suppressName); } } private function getSniffName(string $sniffName): string { return sprintf('%s.%s', self::NAME, $sniffName); } /** * @return list */ private function getTraversableTypeHints(): array { $this->normalizedTraversableTypeHints ??= array_map( static fn (string $typeHint): string => NamespaceHelper::isFullyQualifiedName($typeHint) ? $typeHint : sprintf('%s%s', NamespaceHelper::NAMESPACE_SEPARATOR, $typeHint), SniffSettingsHelper::normalizeArray($this->traversableTypeHints), ); return $this->normalizedTraversableTypeHints; } } PK41]4p!!Scoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/DeclareStrictTypesSniff.phpnu[ */ public function register(): array { return [ T_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $openTagPointer */ public function process(File $phpcsFile, $openTagPointer): void { $this->linesCountBeforeDeclare = SniffSettingsHelper::normalizeInteger($this->linesCountBeforeDeclare); $this->linesCountAfterDeclare = SniffSettingsHelper::normalizeInteger($this->linesCountAfterDeclare); $this->spacesCountAroundEqualsSign = SniffSettingsHelper::normalizeInteger($this->spacesCountAroundEqualsSign); if (TokenHelper::findPrevious($phpcsFile, T_OPEN_TAG, $openTagPointer - 1) !== null) { return; } $tokens = $phpcsFile->getTokens(); $declarePointer = TokenHelper::findNextEffective($phpcsFile, $openTagPointer + 1); if ($declarePointer === null || $tokens[$declarePointer]['code'] !== T_DECLARE) { $fix = $phpcsFile->addFixableError( sprintf('Missing declare(%s).', $this->getStrictTypeDeclaration()), $openTagPointer, self::CODE_DECLARE_STRICT_TYPES_MISSING, ); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::change( $phpcsFile, $openTagPointer, substr($tokens[$openTagPointer]['content'], -1) === $phpcsFile->eolChar ? $openTagPointer : $openTagPointer + 1, sprintf('getStrictTypeDeclaration(), $phpcsFile->eolChar), ); $phpcsFile->fixer->endChangeset(); } return; } $strictTypesPointer = null; for ($i = $tokens[$declarePointer]['parenthesis_opener'] + 1; $i < $tokens[$declarePointer]['parenthesis_closer']; $i++) { if ($tokens[$i]['code'] !== T_STRING || $tokens[$i]['content'] !== 'strict_types') { continue; } $strictTypesPointer = $i; break; } if ($strictTypesPointer === null) { $fix = $phpcsFile->addFixableError( sprintf('Missing declare(%s).', $this->getStrictTypeDeclaration()), $declarePointer, self::CODE_DECLARE_STRICT_TYPES_MISSING, ); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::addBefore( $phpcsFile, $tokens[$declarePointer]['parenthesis_closer'], ', ' . $this->getStrictTypeDeclaration(), ); $phpcsFile->fixer->endChangeset(); } return; } /** @var int $numberPointer */ $numberPointer = TokenHelper::findNext($phpcsFile, T_LNUMBER, $strictTypesPointer + 1); if ($tokens[$numberPointer]['content'] !== '1') { $fix = $phpcsFile->addFixableError( sprintf( 'Expected %s, found %s.', $this->getStrictTypeDeclaration(), TokenHelper::getContent($phpcsFile, $strictTypesPointer, $numberPointer), ), $declarePointer, self::CODE_DECLARE_STRICT_TYPES_MISSING, ); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $numberPointer, '1'); $phpcsFile->fixer->endChangeset(); } return; } $strictTypesContent = TokenHelper::getContent($phpcsFile, $strictTypesPointer, $numberPointer); $format = sprintf('strict_types%1$s=%1$s1', str_repeat(' ', $this->spacesCountAroundEqualsSign)); if ($strictTypesContent !== $format) { $fix = $phpcsFile->addFixableError( sprintf( 'Expected %s, found %s.', $format, $strictTypesContent, ), $strictTypesPointer, self::CODE_INCORRECT_STRICT_TYPES_FORMAT, ); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $strictTypesPointer, $numberPointer, $format); $phpcsFile->fixer->endChangeset(); } } $pointerBeforeDeclare = TokenHelper::findPreviousNonWhitespace($phpcsFile, $declarePointer - 1); $whitespaceBefore = ''; if ($pointerBeforeDeclare === $openTagPointer) { $whitespaceBefore .= substr($tokens[$openTagPointer]['content'], strlen('declareOnFirstLine) { if ($whitespaceBefore !== ' ') { $fix = $phpcsFile->addFixableError( 'There must be a single space between the PHP open tag and declare statement.', $declarePointer, self::CODE_INCORRECT_WHITESPACE_BEFORE_DECLARE, ); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $openTagPointer, $declarePointer - 1, 'fixer->endChangeset(); } } } else { $declareOnFirstLine = $tokens[$declarePointer]['line'] === $tokens[$openTagPointer]['line']; $whitespaceLinesBeforeDeclare = $this->linesCountBeforeDeclare; $linesCountBefore = 0; if (!$declareOnFirstLine) { $linesCountBefore = substr_count($whitespaceBefore, $phpcsFile->eolChar); if ( $tokens[$pointerBeforeDeclare]['code'] === T_COMMENT && CommentHelper::isLineComment($phpcsFile, $pointerBeforeDeclare) ) { $whitespaceLinesBeforeDeclare--; } else { $linesCountBefore--; } } if ($declareOnFirstLine || $linesCountBefore !== $this->linesCountBeforeDeclare) { $fix = $phpcsFile->addFixableError( sprintf( 'Expected %d line%s before declare statement, found %d.', $this->linesCountBeforeDeclare, $this->linesCountBeforeDeclare === 1 ? '' : 's', $linesCountBefore, ), $declarePointer, self::CODE_INCORRECT_WHITESPACE_BEFORE_DECLARE, ); if ($fix) { $phpcsFile->fixer->beginChangeset(); if ($pointerBeforeDeclare === $openTagPointer) { FixerHelper::replace($phpcsFile, $openTagPointer, 'fixer->addNewline($pointerBeforeDeclare); } $phpcsFile->fixer->endChangeset(); } } } /** @var int $declareSemicolonPointer */ $declareSemicolonPointer = TokenHelper::findNextEffective($phpcsFile, $tokens[$declarePointer]['parenthesis_closer'] + 1); $pointerAfterWhitespaceEnd = TokenHelper::findNextNonWhitespace($phpcsFile, $declareSemicolonPointer + 1); if ($pointerAfterWhitespaceEnd === null) { return; } $whitespaceAfter = TokenHelper::getContent($phpcsFile, $declareSemicolonPointer + 1, $pointerAfterWhitespaceEnd - 1); $newLinesAfter = substr_count($whitespaceAfter, $phpcsFile->eolChar); $linesCountAfter = $newLinesAfter > 0 ? $newLinesAfter - 1 : 0; if ($linesCountAfter === $this->linesCountAfterDeclare) { return; } $fix = $phpcsFile->addFixableError( sprintf( 'Expected %d line%s after declare statement, found %d.', $this->linesCountAfterDeclare, $this->linesCountAfterDeclare === 1 ? '' : 's', $linesCountAfter, ), $declarePointer, self::CODE_INCORRECT_WHITESPACE_AFTER_DECLARE, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetween($phpcsFile, $declareSemicolonPointer, $pointerAfterWhitespaceEnd); for ($i = 0; $i <= $this->linesCountAfterDeclare; $i++) { $phpcsFile->fixer->addNewline($declareSemicolonPointer); } $phpcsFile->fixer->endChangeset(); } protected function getStrictTypeDeclaration(): string { return sprintf( 'strict_types%s=%s1', str_repeat(' ', $this->spacesCountAroundEqualsSign), str_repeat(' ', $this->spacesCountAroundEqualsSign), ); } } PK41]2t}}Vcoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/ClassConstantTypeHintSniff.phpnu[ */ private static array $tokenToTypeHintMapping = [ T_FALSE => 'false', T_TRUE => 'true', T_DNUMBER => 'float', T_LNUMBER => 'int', T_NULL => 'null', T_OPEN_SHORT_ARRAY => 'array', T_CONSTANT_ENCAPSED_STRING => 'string', T_START_NOWDOC => 'string', T_START_HEREDOC => 'string', ]; /** * @return array */ public function register(): array { return [ T_CONST, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $constantPointer */ public function process(File $phpcsFile, $constantPointer): void { if (ClassHelper::getClassPointer($phpcsFile, $constantPointer) === null) { // Constant in namespace return; } $this->checkNativeTypeHint($phpcsFile, $constantPointer); $this->checkDocComment($phpcsFile, $constantPointer); } private function checkNativeTypeHint(File $phpcsFile, int $constantPointer): void { $this->enableNativeTypeHint = SniffSettingsHelper::isEnabledByPhpVersion($this->enableNativeTypeHint, 80300); if (!$this->enableNativeTypeHint) { return; } $namePointer = $this->getConstantNamePointer($phpcsFile, $constantPointer); $typeHintPointer = TokenHelper::findPreviousEffective($phpcsFile, $namePointer - 1); if ($typeHintPointer !== $constantPointer) { // Has type hint return; } $tokens = $phpcsFile->getTokens(); $namePointer = $this->getConstantNamePointer($phpcsFile, $constantPointer); $equalPointer = TokenHelper::findNext($phpcsFile, T_EQUAL, $constantPointer + 1); $valuePointer = TokenHelper::findNextEffective($phpcsFile, $equalPointer + 1); if ($tokens[$valuePointer]['code'] === T_MINUS) { $valuePointer = TokenHelper::findNextEffective($phpcsFile, $valuePointer + 1); } $constantName = $tokens[$namePointer]['content']; $typeHint = null; if (array_key_exists($tokens[$valuePointer]['code'], self::$tokenToTypeHintMapping)) { $typeHint = self::$tokenToTypeHintMapping[$tokens[$valuePointer]['code']]; } $errorParameters = [ sprintf('Constant %s does not have native type hint.', $constantName), $constantPointer, self::CODE_MISSING_NATIVE_TYPE_HINT, ]; if ( $typeHint === null || $this->fixableNativeTypeHint === self::NO || ( $this->fixableNativeTypeHint === self::PRIVATE && !$this->isConstantPrivate($phpcsFile, $constantPointer) ) ) { $phpcsFile->addError(...$errorParameters); return; } $fix = $phpcsFile->addFixableError(...$errorParameters); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::add($phpcsFile, $constantPointer, ' ' . $typeHint); $phpcsFile->fixer->endChangeset(); } private function checkDocComment(File $phpcsFile, int $constantPointer): void { $docCommentOpenPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $constantPointer); if ($docCommentOpenPointer === null) { return; } $annotations = AnnotationHelper::getAnnotations($phpcsFile, $constantPointer, '@var'); if ($annotations === []) { return; } $tokens = $phpcsFile->getTokens(); $namePointer = $this->getConstantNamePointer($phpcsFile, $constantPointer); $constantName = $tokens[$namePointer]['content']; $uselessDocComment = !DocCommentHelper::hasDocCommentDescription($phpcsFile, $constantPointer) && count($annotations) === 1; if ($uselessDocComment) { $fix = $phpcsFile->addFixableError( sprintf('Useless documentation comment for constant %s.', $constantName), $docCommentOpenPointer, self::CODE_USELESS_DOC_COMMENT, ); /** @var int $fixerStart */ $fixerStart = TokenHelper::findLastTokenOnPreviousLine($phpcsFile, $docCommentOpenPointer); $fixerEnd = $tokens[$docCommentOpenPointer]['comment_closer']; } else { $annotation = $annotations[0]; $fix = $phpcsFile->addFixableError( sprintf('Useless @var annotation for constant %s.', $constantName), $annotation->getStartPointer(), self::CODE_USELESS_VAR_ANNOTATION, ); /** @var int $fixerStart */ $fixerStart = TokenHelper::findPreviousContent( $phpcsFile, T_DOC_COMMENT_WHITESPACE, $phpcsFile->eolChar, $annotation->getStartPointer() - 1, ); $fixerEnd = $annotation->getEndPointer(); } if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $fixerStart, $fixerEnd); $phpcsFile->fixer->endChangeset(); } private function getConstantNamePointer(File $phpcsFile, int $constantPointer): int { $equalPointer = TokenHelper::findNext($phpcsFile, T_EQUAL, $constantPointer + 1); return TokenHelper::findPreviousEffective($phpcsFile, $equalPointer - 1); } private function isConstantPrivate(File $phpcsFile, int $constantPointer): bool { $tokens = $phpcsFile->getTokens(); $previousPointer = TokenHelper::findPrevious( $phpcsFile, [T_PRIVATE, T_OPEN_CURLY_BRACKET, T_CLOSE_CURLY_BRACKET, T_SEMICOLON], $constantPointer - 1, ); return $previousPointer !== null && $tokens[$previousPointer]['code'] === T_PRIVATE; } } PK41]6Є++Rcoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/DNFTypeHintFormatSniff.phpnu[ */ public function register(): array { return [ T_VARIABLE, ...TokenHelper::FUNCTION_TOKEN_CODES, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { $this->enable = SniffSettingsHelper::isEnabledByPhpVersion($this->enable, 80000); if (!$this->enable) { return; } $tokens = $phpcsFile->getTokens(); if ($tokens[$pointer]['code'] === T_VARIABLE) { if (!PropertyHelper::isProperty($phpcsFile, $pointer)) { return; } $propertyTypeHint = PropertyHelper::findTypeHint($phpcsFile, $pointer); if ($propertyTypeHint !== null) { $this->checkTypeHint($phpcsFile, $propertyTypeHint); } return; } $returnTypeHint = FunctionHelper::findReturnTypeHint($phpcsFile, $pointer); if ($returnTypeHint !== null) { $this->checkTypeHint($phpcsFile, $returnTypeHint); } foreach (FunctionHelper::getParametersTypeHints($phpcsFile, $pointer) as $parameterTypeHint) { if ($parameterTypeHint !== null) { $this->checkTypeHint($phpcsFile, $parameterTypeHint); } } } private function checkTypeHint(File $phpcsFile, TypeHint $typeHint): void { $tokens = $phpcsFile->getTokens(); $typeHintsCount = substr_count($typeHint->getTypeHint(), '|') + substr_count($typeHint->getTypeHint(), '&') + 1; if ($typeHintsCount > 1) { if ($this->withSpacesAroundOperators === self::NO) { $error = false; foreach (TokenHelper::findNextAll( $phpcsFile, T_WHITESPACE, $typeHint->getStartPointer(), $typeHint->getEndPointer(), ) as $whitespacePointer) { if (in_array($tokens[$whitespacePointer - 1]['code'], [T_TYPE_UNION, T_TYPE_INTERSECTION], true)) { $error = true; break; } if (in_array($tokens[$whitespacePointer + 1]['code'], [T_TYPE_UNION, T_TYPE_INTERSECTION], true)) { $error = true; break; } } if ($error) { $originalTypeHint = TokenHelper::getContent($phpcsFile, $typeHint->getStartPointer(), $typeHint->getEndPointer()); $fix = $phpcsFile->addFixableError( sprintf('Spaces around "|" or "&" in type hint "%s" are disallowed.', $originalTypeHint), $typeHint->getStartPointer(), self::CODE_DISALLOWED_WHITESPACE_AROUND_OPERATOR, ); if ($fix) { $fixedTypeHint = preg_replace('~\s*([|&])\s*~', '\1', $originalTypeHint); $this->fixTypeHint($phpcsFile, $typeHint, $fixedTypeHint); } } } elseif ($this->withSpacesAroundOperators === self::YES) { $error = false; foreach (TokenHelper::findNextAll( $phpcsFile, [T_TYPE_UNION, T_TYPE_INTERSECTION], $typeHint->getStartPointer(), $typeHint->getEndPointer(), ) as $operatorPointer) { if ($tokens[$operatorPointer - 1]['content'] !== ' ') { $error = true; break; } if ($tokens[$operatorPointer + 1]['content'] !== ' ') { $error = true; break; } } if ($error) { $originalTypeHint = TokenHelper::getContent($phpcsFile, $typeHint->getStartPointer(), $typeHint->getEndPointer()); $fix = $phpcsFile->addFixableError( sprintf('One space required around each "|" or "&" in type hint "%s".', $originalTypeHint), $typeHint->getStartPointer(), self::CODE_REQUIRED_WHITESPACE_AROUND_OPERATOR, ); if ($fix) { $fixedTypeHint = preg_replace('~\s*([|&])\s*~', ' \1 ', $originalTypeHint); $this->fixTypeHint($phpcsFile, $typeHint, $fixedTypeHint); } } } if ($this->withSpacesInsideParentheses === self::NO) { $error = false; foreach (TokenHelper::findNextAll( $phpcsFile, T_WHITESPACE, $typeHint->getStartPointer(), $typeHint->getEndPointer(), ) as $whitespacePointer) { if ($tokens[$whitespacePointer - 1]['code'] === T_TYPE_OPEN_PARENTHESIS) { $error = true; break; } if ($tokens[$whitespacePointer + 1]['code'] === T_TYPE_CLOSE_PARENTHESIS) { $error = true; break; } } if ($error) { $originalTypeHint = TokenHelper::getContent($phpcsFile, $typeHint->getStartPointer(), $typeHint->getEndPointer()); $fix = $phpcsFile->addFixableError( sprintf('Spaces inside parentheses in type hint "%s" are disallowed.', $originalTypeHint), $typeHint->getStartPointer(), self::CODE_DISALLOWED_WHITESPACE_INSIDE_PARENTHESES, ); if ($fix) { $fixedTypeHint = preg_replace('~\s+\)~', ')', preg_replace('~\(\s+~', '(', $originalTypeHint)); $this->fixTypeHint($phpcsFile, $typeHint, $fixedTypeHint); } } } elseif ($this->withSpacesInsideParentheses === self::YES) { $error = false; foreach (TokenHelper::findNextAll( $phpcsFile, [T_TYPE_OPEN_PARENTHESIS, T_TYPE_CLOSE_PARENTHESIS], $typeHint->getStartPointer(), $typeHint->getEndPointer() + 1, ) as $parenthesisPointer) { if ( $tokens[$parenthesisPointer]['code'] === T_TYPE_OPEN_PARENTHESIS && $tokens[$parenthesisPointer + 1]['content'] !== ' ' ) { $error = true; break; } if ( $tokens[$parenthesisPointer]['code'] === T_TYPE_CLOSE_PARENTHESIS && $tokens[$parenthesisPointer - 1]['content'] !== ' ' ) { $error = true; break; } } if ($error) { $originalTypeHint = TokenHelper::getContent($phpcsFile, $typeHint->getStartPointer(), $typeHint->getEndPointer()); $fix = $phpcsFile->addFixableError( sprintf('One space required around expression inside parentheses in type hint "%s".', $originalTypeHint), $typeHint->getStartPointer(), self::CODE_REQUIRED_WHITESPACE_INSIDE_PARENTHESES, ); if ($fix) { $fixedTypeHint = preg_replace('~\s*\)~', ' )', preg_replace('~\(\s*~', '( ', $originalTypeHint)); $this->fixTypeHint($phpcsFile, $typeHint, $fixedTypeHint); } } } } if (substr_count($typeHint->getTypeHint(), '&') > 0) { return; } if (!$typeHint->isNullable()) { return; } $hasShortNullable = strpos($typeHint->getTypeHint(), '?') === 0; if ($this->shortNullable === self::YES && $typeHintsCount === 2 && !$hasShortNullable) { $fix = $phpcsFile->addFixableError( sprintf('Short nullable type hint in "%s" is required.', $typeHint->getTypeHint()), $typeHint->getStartPointer(), self::CODE_REQUIRED_SHORT_NULLABLE, ); if ($fix) { $typeHintWithoutNull = self::getTypeHintContentWithoutNull($phpcsFile, $typeHint); $this->fixTypeHint($phpcsFile, $typeHint, '?' . $typeHintWithoutNull); } } elseif ($this->shortNullable === self::NO && $hasShortNullable) { $fix = $phpcsFile->addFixableError( sprintf('Usage of short nullable type hint in "%s" is disallowed.', $typeHint->getTypeHint()), $typeHint->getStartPointer(), self::CODE_DISALLOWED_SHORT_NULLABLE, ); if ($fix) { $this->fixTypeHint($phpcsFile, $typeHint, substr($typeHint->getTypeHint(), 1) . '|null'); } } if ($hasShortNullable || ($this->shortNullable === self::YES && $typeHintsCount === 2)) { return; } if ($this->nullPosition === self::FIRST && strtolower($tokens[$typeHint->getStartPointer()]['content']) !== 'null') { $fix = $phpcsFile->addFixableError( sprintf('Null type hint should be on first position in "%s".', $typeHint->getTypeHint()), $typeHint->getStartPointer(), self::CODE_NULL_TYPE_HINT_NOT_ON_FIRST_POSITION, ); if ($fix) { $this->fixTypeHint($phpcsFile, $typeHint, 'null|' . self::getTypeHintContentWithoutNull($phpcsFile, $typeHint)); } } elseif ($this->nullPosition === self::LAST && strtolower($tokens[$typeHint->getEndPointer()]['content']) !== 'null') { $fix = $phpcsFile->addFixableError( sprintf('Null type hint should be on last position in "%s".', $typeHint->getTypeHint()), $typeHint->getStartPointer(), self::CODE_NULL_TYPE_HINT_NOT_ON_LAST_POSITION, ); if ($fix) { $this->fixTypeHint($phpcsFile, $typeHint, self::getTypeHintContentWithoutNull($phpcsFile, $typeHint) . '|null'); } } } private function getTypeHintContentWithoutNull(File $phpcsFile, TypeHint $typeHint): string { $tokens = $phpcsFile->getTokens(); if (strtolower($tokens[$typeHint->getEndPointer()]['content']) === 'null') { $previousTypeHintPointer = TokenHelper::findPrevious( $phpcsFile, TokenHelper::ONLY_TYPE_HINT_TOKEN_CODES, $typeHint->getEndPointer() - 1, ); return TokenHelper::getContent($phpcsFile, $typeHint->getStartPointer(), $previousTypeHintPointer); } $content = ''; for ($i = $typeHint->getStartPointer(); $i <= $typeHint->getEndPointer(); $i++) { if (strtolower($tokens[$i]['content']) === 'null') { $i = TokenHelper::findNext($phpcsFile, TokenHelper::ONLY_TYPE_HINT_TOKEN_CODES, $i + 1); } $content .= $tokens[$i]['content']; } return $content; } private function fixTypeHint(File $phpcsFile, TypeHint $typeHint, string $fixedTypeHint): void { $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $typeHint->getStartPointer(), $typeHint->getEndPointer(), $fixedTypeHint); $phpcsFile->fixer->endChangeset(); } } PK41]LYcoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/ParameterTypeHintSpacingSniff.phpnu[ */ public function register(): array { return TokenHelper::FUNCTION_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $functionPointer */ public function process(File $phpcsFile, $functionPointer): void { $tokens = $phpcsFile->getTokens(); $parametersStartPointer = $tokens[$functionPointer]['parenthesis_opener'] + 1; $parametersEndPointer = $tokens[$functionPointer]['parenthesis_closer'] - 1; for ($i = $parametersStartPointer; $i <= $parametersEndPointer; $i++) { if ($tokens[$i]['code'] !== T_VARIABLE) { continue; } $parameterPointer = $i; $parameterName = $tokens[$parameterPointer]['content']; $parameterStartPointer = TokenHelper::findPrevious($phpcsFile, T_COMMA, $parameterPointer - 1, $parametersStartPointer); $parameterStartPointer ??= $parametersStartPointer; $parameterEndPointer = TokenHelper::findNext($phpcsFile, T_COMMA, $parameterPointer + 1, $parametersEndPointer + 1); $parameterEndPointer ??= $parametersEndPointer; $attributeCloserPointer = TokenHelper::findPrevious($phpcsFile, T_ATTRIBUTE_END, $parameterPointer - 1, $parameterStartPointer); $typeHintEndPointer = TokenHelper::findPrevious( $phpcsFile, TokenHelper::TYPE_HINT_TOKEN_CODES, $parameterPointer - 1, $attributeCloserPointer ?? $parameterStartPointer, ); if ($typeHintEndPointer === null) { continue; } $typeHintStartPointer = TypeHintHelper::getStartPointer($phpcsFile, $typeHintEndPointer); $nextTokenNames = [ T_VARIABLE => sprintf('parameter %s', $parameterName), T_BITWISE_AND => sprintf('reference sign of parameter %s', $parameterName), T_ELLIPSIS => sprintf('varadic parameter %s', $parameterName), ]; $nextTokenPointer = TokenHelper::findNext( $phpcsFile, array_keys($nextTokenNames), $typeHintEndPointer + 1, $parameterEndPointer + 1, ); if ($tokens[$typeHintEndPointer + 1]['code'] !== T_WHITESPACE) { $fix = $phpcsFile->addFixableError( sprintf( 'There must be exactly one space between parameter type hint and %s.', $nextTokenNames[$tokens[$nextTokenPointer]['code']], ), $typeHintEndPointer, self::CODE_NO_SPACE_BETWEEN_TYPE_HINT_AND_PARAMETER, ); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::add($phpcsFile, $typeHintEndPointer, ' '); $phpcsFile->fixer->endChangeset(); } } elseif ($tokens[$typeHintEndPointer + 1]['content'] !== ' ') { $fix = $phpcsFile->addFixableError( sprintf( 'There must be exactly one space between parameter type hint and %s.', $nextTokenNames[$tokens[$nextTokenPointer]['code']], ), $typeHintEndPointer, self::CODE_MULTIPLE_SPACES_BETWEEN_TYPE_HINT_AND_PARAMETER, ); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $typeHintEndPointer + 1, ' '); $phpcsFile->fixer->endChangeset(); } } $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $typeHintStartPointer - 1, $parameterStartPointer); $nullabilitySymbolPointer = $previousPointer !== null && $tokens[$previousPointer]['code'] === T_NULLABLE ? $previousPointer : null; if ($nullabilitySymbolPointer === null) { continue; } if ($nullabilitySymbolPointer + 1 === $typeHintStartPointer) { continue; } $fix = $phpcsFile->addFixableError( sprintf( 'There must be no whitespace between parameter type hint nullability symbol and parameter type hint of parameter %s.', $parameterName, ), $typeHintStartPointer, self::CODE_WHITESPACE_AFTER_NULLABILITY_SYMBOL, ); if (!$fix) { continue; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $nullabilitySymbolPointer + 1, ''); $phpcsFile->fixer->endChangeset(); } } } PK41]~! ! Ncoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/LongTypeHintsSniff.phpnu[ */ public function register(): array { return [ T_DOC_COMMENT_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $docCommentOpenPointer */ public function process(File $phpcsFile, $docCommentOpenPointer): void { $annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenPointer); foreach ($annotations as $annotation) { $identifierTypeNodes = AnnotationHelper::getAnnotationNodesByType($annotation->getNode(), IdentifierTypeNode::class); foreach ($identifierTypeNodes as $typeHintNode) { $typeHint = $typeHintNode->name; $lowercasedTypeHint = strtolower($typeHint); $shortTypeHint = null; if ($lowercasedTypeHint === 'integer') { $shortTypeHint = 'int'; } elseif ($lowercasedTypeHint === 'boolean') { $shortTypeHint = 'bool'; } if ($shortTypeHint === null) { continue; } $fix = $phpcsFile->addFixableError(sprintf( 'Expected "%s" but found "%s" in %s annotation.', $shortTypeHint, $typeHint, $annotation->getName(), ), $annotation->getStartPointer(), self::CODE_USED_LONG_TYPE_HINT); if (!$fix) { continue; } $parsedDocComment = DocCommentHelper::parseDocComment($phpcsFile, $docCommentOpenPointer); $fixedDocComment = AnnotationHelper::fixAnnotation( $parsedDocComment, $annotation, $typeHintNode, new IdentifierTypeNode($shortTypeHint), ); $phpcsFile->fixer->beginChangeset(); FixerHelper::change( $phpcsFile, $parsedDocComment->getOpenPointer(), $parsedDocComment->getClosePointer(), $fixedDocComment, ); $phpcsFile->fixer->endChangeset(); } } } } PK41] [coding-standard/SlevomatCodingStandard/Sniffs/TypeHints/NullTypeHintOnLastPositionSniff.phpnu[ */ public function register(): array { return [ T_DOC_COMMENT_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $docCommentOpenPointer */ public function process(File $phpcsFile, $docCommentOpenPointer): void { $annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenPointer); foreach ($annotations as $annotation) { $unionTypeNodes = AnnotationHelper::getAnnotationNodesByType($annotation->getNode(), UnionTypeNode::class); foreach ($unionTypeNodes as $unionTypeNode) { $nullTypeNode = null; $nullPosition = 0; $position = 0; foreach ($unionTypeNode->types as $typeNode) { if ($typeNode instanceof IdentifierTypeNode && strtolower($typeNode->name) === 'null') { $nullTypeNode = $typeNode; $nullPosition = $position; break; } $position++; } if ($nullTypeNode === null) { continue; } if ($nullPosition === count($unionTypeNode->types) - 1) { continue; } $fix = $phpcsFile->addFixableError( sprintf('Null type hint should be on last position in "%s".', AnnotationTypeHelper::print($unionTypeNode)), $annotation->getStartPointer(), self::CODE_NULL_TYPE_HINT_NOT_ON_LAST_POSITION, ); if (!$fix) { continue; } $fixedTypeNodes = []; foreach ($unionTypeNode->types as $typeNode) { if ($typeNode === $nullTypeNode) { continue; } $fixedTypeNodes[] = $typeNode; } $fixedTypeNodes[] = $nullTypeNode; $fixedUnionTypeNode = PhpDocParserHelper::cloneNode($unionTypeNode); $fixedUnionTypeNode->types = $fixedTypeNodes; $phpcsFile->fixer->beginChangeset(); $parsedDocComment = DocCommentHelper::parseDocComment($phpcsFile, $docCommentOpenPointer); $fixedDocComment = AnnotationHelper::fixAnnotation($parsedDocComment, $annotation, $unionTypeNode, $fixedUnionTypeNode); FixerHelper::change( $phpcsFile, $parsedDocComment->getOpenPointer(), $parsedDocComment->getClosePointer(), $fixedDocComment, ); $phpcsFile->fixer->endChangeset(); } } } } PK41]R m!!\coding-standard/SlevomatCodingStandard/Sniffs/TypeHints/DisallowArrayTypeHintSyntaxSniff.phpnu[ */ public array $traversableTypeHints = []; /** @var array|null */ private ?array $normalizedTraversableTypeHints = null; /** * @return array */ public function register(): array { return [ T_DOC_COMMENT_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $docCommentOpenPointer */ public function process(File $phpcsFile, $docCommentOpenPointer): void { $annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenPointer); foreach ($annotations as $annotation) { $arrayTypeNodes = $this->getArrayTypeNodes($annotation->getValue()); foreach ($arrayTypeNodes as $arrayTypeNode) { $fix = $phpcsFile->addFixableError( sprintf( 'Usage of array type hint syntax in "%s" is disallowed, use generic type hint syntax instead.', AnnotationTypeHelper::print($arrayTypeNode), ), $annotation->getStartPointer(), self::CODE_DISALLOWED_ARRAY_TYPE_HINT_SYNTAX, ); if (!$fix) { continue; } $parsedDocComment = DocCommentHelper::parseDocComment($phpcsFile, $docCommentOpenPointer); $unionTypeNodes = AnnotationHelper::getAnnotationNodesByType($annotation->getNode(), UnionTypeNode::class); $unionTypeNode = $this->findUnionTypeThatContainsArrayType($arrayTypeNode, $unionTypeNodes); if ($unionTypeNode !== null) { $genericIdentifier = $this->findGenericIdentifier( $phpcsFile, $docCommentOpenPointer, $unionTypeNode, $annotation->getValue(), ); if ($genericIdentifier !== null) { $genericTypeNode = new GenericTypeNode( new IdentifierTypeNode($genericIdentifier), [$this->fixArrayNode($arrayTypeNode->type)], ); $fixedDocComment = AnnotationHelper::fixAnnotation( $parsedDocComment, $annotation, $unionTypeNode, $genericTypeNode, ); } else { $genericTypeNode = new GenericTypeNode( new IdentifierTypeNode('array'), [$this->fixArrayNode($arrayTypeNode->type)], ); $fixedDocComment = AnnotationHelper::fixAnnotation( $parsedDocComment, $annotation, $arrayTypeNode, $genericTypeNode, ); } } else { $genericIdentifier = $this->findGenericIdentifier( $phpcsFile, $docCommentOpenPointer, $arrayTypeNode, $annotation->getValue(), ) ?? 'array'; $genericTypeNode = new GenericTypeNode( new IdentifierTypeNode($genericIdentifier), [$this->fixArrayNode($arrayTypeNode->type)], ); $fixedDocComment = AnnotationHelper::fixAnnotation($parsedDocComment, $annotation, $arrayTypeNode, $genericTypeNode); } $phpcsFile->fixer->beginChangeset(); FixerHelper::change( $phpcsFile, $parsedDocComment->getOpenPointer(), $parsedDocComment->getClosePointer(), $fixedDocComment, ); $phpcsFile->fixer->endChangeset(); } } } /** * @return list */ public function getArrayTypeNodes(Node $node): array { static $visitor; static $traverser; $visitor ??= new class extends AbstractNodeVisitor { /** @var list */ private array $nodes = []; /** * @return NodeTraverser::DONT_TRAVERSE_CHILDREN|null */ public function enterNode(Node $node) { if ($node instanceof ArrayTypeNode) { $this->nodes[] = $node; if ($node->type instanceof ArrayTypeNode) { return NodeTraverser::DONT_TRAVERSE_CHILDREN; } } return null; } public function cleanNodes(): void { $this->nodes = []; } /** * @return list */ public function getNodes(): array { return $this->nodes; } }; $traverser ??= new NodeTraverser([$visitor]); $visitor->cleanNodes(); $traverser->traverse([$node]); return $visitor->getNodes(); } private function fixArrayNode(TypeNode $node): TypeNode { if (!$node instanceof ArrayTypeNode) { return $node; } return new GenericTypeNode(new IdentifierTypeNode('array'), [$this->fixArrayNode($node->type)]); } /** * @param list $unionTypeNodes */ private function findUnionTypeThatContainsArrayType(ArrayTypeNode $arrayTypeNode, array $unionTypeNodes): ?UnionTypeNode { foreach ($unionTypeNodes as $unionTypeNode) { if (in_array($arrayTypeNode, $unionTypeNode->types, true)) { return $unionTypeNode; } } return null; } private function findGenericIdentifier( File $phpcsFile, int $docCommentOpenPointer, TypeNode $typeNode, PhpDocTagValueNode $annotationValue ): ?string { if (!$typeNode instanceof UnionTypeNode) { if (!$annotationValue instanceof ParamTagValueNode && !$annotationValue instanceof ReturnTagValueNode) { return null; } $functionPointer = TokenHelper::findNext($phpcsFile, TokenHelper::FUNCTION_TOKEN_CODES, $docCommentOpenPointer + 1); if ($functionPointer === null || $phpcsFile->getTokens()[$functionPointer]['code'] !== T_FUNCTION) { return null; } if ($annotationValue instanceof ParamTagValueNode) { $parameterTypeHints = FunctionHelper::getParametersTypeHints($phpcsFile, $functionPointer); return array_key_exists( $annotationValue->parameterName, $parameterTypeHints, ) && $parameterTypeHints[$annotationValue->parameterName] !== null ? $parameterTypeHints[$annotationValue->parameterName]->getTypeHint() : null; } $returnType = FunctionHelper::findReturnTypeHint($phpcsFile, $functionPointer); return $returnType !== null ? $returnType->getTypeHint() : null; } if (count($typeNode->types) !== 2) { return null; } if ( $typeNode->types[0] instanceof ArrayTypeNode && $typeNode->types[1] instanceof IdentifierTypeNode && $this->isTraversableType( TypeHintHelper::getFullyQualifiedTypeHint($phpcsFile, $docCommentOpenPointer, $typeNode->types[1]->name), ) ) { return $typeNode->types[1]->name; } if ( $typeNode->types[1] instanceof ArrayTypeNode && $typeNode->types[0] instanceof IdentifierTypeNode && $this->isTraversableType( TypeHintHelper::getFullyQualifiedTypeHint($phpcsFile, $docCommentOpenPointer, $typeNode->types[0]->name), ) ) { return $typeNode->types[0]->name; } return null; } private function isTraversableType(string $type): bool { return TypeHintHelper::isSimpleIterableTypeHint($type) || array_key_exists($type, $this->getNormalizedTraversableTypeHints()); } /** * @return array */ private function getNormalizedTraversableTypeHints(): array { $this->normalizedTraversableTypeHints ??= array_flip( array_map(static fn (string $typeHint): string => NamespaceHelper::isFullyQualifiedName($typeHint) ? $typeHint : sprintf('%s%s', NamespaceHelper::NAMESPACE_SEPARATOR, $typeHint), SniffSettingsHelper::normalizeArray( $this->traversableTypeHints, )), ); return $this->normalizedTraversableTypeHints; } } PK41]'TTQcoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/PropertyTypeHintSniff.phpnu[ */ public array $traversableTypeHints = []; /** @var list|null */ private ?array $normalizedTraversableTypeHints = null; /** * @return array */ public function register(): array { // Other modifiers cannot be used without type hint return [ T_VAR, T_PUBLIC, T_PROTECTED, T_PRIVATE, T_STATIC, T_FINAL, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { $this->enableNativeTypeHint = SniffSettingsHelper::isEnabledByPhpVersion($this->enableNativeTypeHint, 70400); $this->enableMixedTypeHint = $this->enableNativeTypeHint ? SniffSettingsHelper::isEnabledByPhpVersion($this->enableMixedTypeHint, 80000) : false; $this->enableUnionTypeHint = $this->enableNativeTypeHint ? SniffSettingsHelper::isEnabledByPhpVersion($this->enableUnionTypeHint, 80000) : false; $this->enableIntersectionTypeHint = $this->enableNativeTypeHint ? SniffSettingsHelper::isEnabledByPhpVersion($this->enableIntersectionTypeHint, 80100) : false; $this->enableStandaloneNullTrueFalseTypeHints = $this->enableNativeTypeHint ? SniffSettingsHelper::isEnabledByPhpVersion($this->enableStandaloneNullTrueFalseTypeHints, 80200) : false; $tokens = $phpcsFile->getTokens(); $asPointer = TokenHelper::findPreviousEffective($phpcsFile, $pointer - 1); if ($tokens[$asPointer]['code'] === T_AS) { return; } $nextPointer = TokenHelper::findNextEffective($phpcsFile, $pointer + 1); if (in_array($tokens[$nextPointer]['code'], TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES, true)) { // We don't want to report the same property multiple times return; } $propertyPointer = TokenHelper::findNext($phpcsFile, [T_FUNCTION, T_CONST, T_VARIABLE], $pointer + 1); if ($propertyPointer === null || $tokens[$propertyPointer]['code'] !== T_VARIABLE) { return; } if (!PropertyHelper::isProperty($phpcsFile, $propertyPointer)) { return; } if (SuppressHelper::isSniffSuppressed($phpcsFile, $propertyPointer, self::NAME)) { return; } $docCommentOpenPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $propertyPointer); if ($docCommentOpenPointer !== null) { if (DocCommentHelper::hasInheritdocAnnotation($phpcsFile, $docCommentOpenPointer)) { return; } $varAnnotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenPointer, '@var'); $prefixedPropertyAnnotations = $this->getValidPrefixedAnnotations($phpcsFile, $docCommentOpenPointer); $propertyAnnotation = count($varAnnotations) > 0 ? current($varAnnotations) : null; } else { $propertyAnnotation = null; $prefixedPropertyAnnotations = []; } $propertyTypeHint = PropertyHelper::findTypeHint($phpcsFile, $propertyPointer); $this->checkTypeHint($phpcsFile, $propertyPointer, $propertyTypeHint, $propertyAnnotation, $prefixedPropertyAnnotations); $this->checkTraversableTypeHintSpecification( $phpcsFile, $propertyPointer, $propertyTypeHint, $propertyAnnotation, $prefixedPropertyAnnotations, ); $this->checkUselessAnnotation($phpcsFile, $propertyPointer, $propertyTypeHint, $propertyAnnotation); } /** * @param Annotation|null $propertyAnnotation * @param list> $prefixedPropertyAnnotations */ private function checkTypeHint( File $phpcsFile, int $propertyPointer, ?TypeHint $propertyTypeHint, ?Annotation $propertyAnnotation, array $prefixedPropertyAnnotations ): void { $suppressNameAnyTypeHint = $this->getSniffName(self::CODE_MISSING_ANY_TYPE_HINT); $isSuppressedAnyTypeHint = SuppressHelper::isSniffSuppressed($phpcsFile, $propertyPointer, $suppressNameAnyTypeHint); $suppressNameNativeTypeHint = $this->getSniffName(self::CODE_MISSING_NATIVE_TYPE_HINT); $isSuppressedNativeTypeHint = SuppressHelper::isSniffSuppressed($phpcsFile, $propertyPointer, $suppressNameNativeTypeHint); if ($propertyTypeHint !== null) { $this->reportUselessSuppress($phpcsFile, $propertyPointer, $isSuppressedAnyTypeHint, $suppressNameAnyTypeHint); $this->reportUselessSuppress($phpcsFile, $propertyPointer, $isSuppressedNativeTypeHint, $suppressNameNativeTypeHint); return; } if (!$this->hasAnnotation($propertyAnnotation)) { if (count($prefixedPropertyAnnotations) !== 0) { $this->reportUselessSuppress($phpcsFile, $propertyPointer, $isSuppressedAnyTypeHint, $suppressNameAnyTypeHint); return; } if (!$isSuppressedAnyTypeHint) { $phpcsFile->addError( sprintf( $this->enableNativeTypeHint ? 'Property %s does not have native type hint nor @var annotation for its value.' : 'Property %s does not have @var annotation for its value.', PropertyHelper::getFullyQualifiedName($phpcsFile, $propertyPointer), ), $propertyPointer, self::CODE_MISSING_ANY_TYPE_HINT, ); } return; } if (!$this->enableNativeTypeHint) { return; } $typeNode = $propertyAnnotation->getValue()->type; $originalTypeNode = $typeNode; if ($typeNode instanceof NullableTypeNode) { $typeNode = $typeNode->type; } $canTryUnionTypeHint = $this->enableUnionTypeHint && $typeNode instanceof UnionTypeNode; $typeHints = []; $traversableTypeHints = []; $nullableTypeHint = false; if (AnnotationTypeHelper::containsOneType($typeNode)) { /** @var ArrayTypeNode|ArrayShapeNode|ObjectShapeNode|IdentifierTypeNode|ThisTypeNode|GenericTypeNode|CallableTypeNode $typeNode */ $typeNode = $typeNode; $typeHints[] = AnnotationTypeHelper::getTypeHintFromOneType($typeNode, false, $this->enableStandaloneNullTrueFalseTypeHints); } elseif ($typeNode instanceof UnionTypeNode || $typeNode instanceof IntersectionTypeNode) { $traversableTypeHints = []; foreach ($typeNode->types as $innerTypeNode) { if (!AnnotationTypeHelper::containsOneType($innerTypeNode)) { $this->reportUselessSuppress($phpcsFile, $propertyPointer, $isSuppressedNativeTypeHint, $suppressNameNativeTypeHint); return; } /** @var ArrayTypeNode|ArrayShapeNode|ObjectShapeNode|IdentifierTypeNode|ThisTypeNode|GenericTypeNode|CallableTypeNode $innerTypeNode */ $innerTypeNode = $innerTypeNode; $typeHint = AnnotationTypeHelper::getTypeHintFromOneType($innerTypeNode, $canTryUnionTypeHint); if (strtolower($typeHint) === 'null') { $nullableTypeHint = true; continue; } $isTraversable = TypeHintHelper::isTraversableType( TypeHintHelper::getFullyQualifiedTypeHint($phpcsFile, $propertyPointer, $typeHint), $this->getTraversableTypeHints(), ); if ( !$innerTypeNode instanceof ArrayTypeNode && !$innerTypeNode instanceof ArrayShapeNode && $isTraversable ) { $traversableTypeHints[] = $typeHint; } $typeHints[] = $typeHint; } $traversableTypeHints = array_values(array_unique($traversableTypeHints)); if (count($traversableTypeHints) > 1 && !$canTryUnionTypeHint) { $this->reportUselessSuppress($phpcsFile, $propertyPointer, $isSuppressedNativeTypeHint, $suppressNameNativeTypeHint); return; } } $typeHints = array_values(array_unique($typeHints)); if (count($traversableTypeHints) > 0) { /** @var UnionTypeNode|IntersectionTypeNode $typeNode */ $typeNode = $typeNode; $itemsSpecificationTypeHint = AnnotationTypeHelper::getItemsSpecificationTypeFromType($typeNode); if ($itemsSpecificationTypeHint !== null) { $typeHints = AnnotationTypeHelper::getTraversableTypeHintsFromType( $typeNode, $phpcsFile, $propertyPointer, $this->getTraversableTypeHints(), $this->enableUnionTypeHint, ); } } if (count($typeHints) === 0) { $this->reportUselessSuppress($phpcsFile, $propertyPointer, $isSuppressedNativeTypeHint, $suppressNameNativeTypeHint); return; } $typeHintsWithConvertedUnion = []; foreach ($typeHints as $typeHint) { if ($this->enableUnionTypeHint && TypeHintHelper::isUnofficialUnionTypeHint($typeHint)) { $canTryUnionTypeHint = true; array_push($typeHintsWithConvertedUnion, ...TypeHintHelper::convertUnofficialUnionTypeHintToOfficialTypeHints($typeHint)); } else { $typeHintsWithConvertedUnion[] = $typeHint; } } $typeHintsWithConvertedUnion = array_unique($typeHintsWithConvertedUnion); if ( count($typeHintsWithConvertedUnion) > 1 && ( ($typeNode instanceof UnionTypeNode && !$canTryUnionTypeHint) || ($typeNode instanceof IntersectionTypeNode && !$this->enableIntersectionTypeHint) ) ) { $this->reportUselessSuppress($phpcsFile, $propertyPointer, $isSuppressedNativeTypeHint, $suppressNameNativeTypeHint); return; } foreach ($typeHintsWithConvertedUnion as $typeHintNo => $typeHint) { if ($typeHint === 'callable') { $this->reportUselessSuppress($phpcsFile, $propertyPointer, $isSuppressedNativeTypeHint, $suppressNameNativeTypeHint); return; } if ($canTryUnionTypeHint && $typeHint === 'false') { continue; } if (!TypeHintHelper::isValidTypeHint( $typeHint, true, false, $this->enableMixedTypeHint, $this->enableStandaloneNullTrueFalseTypeHints, )) { $this->reportUselessSuppress($phpcsFile, $propertyPointer, $isSuppressedNativeTypeHint, $suppressNameNativeTypeHint); return; } if (TypeHintHelper::isTypeDefinedInAnnotation($phpcsFile, $propertyPointer, $typeHint)) { $this->reportUselessSuppress($phpcsFile, $propertyPointer, $isSuppressedNativeTypeHint, $suppressNameNativeTypeHint); return; } $typeHintsWithConvertedUnion[$typeHintNo] = TypeHintHelper::convertLongSimpleTypeHintToShort($typeHint); } if ($originalTypeNode instanceof NullableTypeNode) { $nullableTypeHint = true; } if ($isSuppressedNativeTypeHint) { return; } $fix = $phpcsFile->addFixableError( sprintf( 'Property %s does not have native type hint for its value but it should be possible to add it based on @var annotation "%s".', PropertyHelper::getFullyQualifiedName($phpcsFile, $propertyPointer), AnnotationTypeHelper::print($typeNode), ), $propertyPointer, self::CODE_MISSING_NATIVE_TYPE_HINT, ); if (!$fix) { return; } if (in_array('mixed', $typeHintsWithConvertedUnion, true)) { $propertyTypeHint = 'mixed'; } elseif ($originalTypeNode instanceof IntersectionTypeNode) { $propertyTypeHint = implode('&', $typeHintsWithConvertedUnion); } else { $propertyTypeHint = implode('|', $typeHintsWithConvertedUnion); if ($nullableTypeHint) { if (count($typeHintsWithConvertedUnion) > 1) { $propertyTypeHint .= '|null'; } else { $propertyTypeHint = '?' . $propertyTypeHint; } } } $tokens = $phpcsFile->getTokens(); $pointerAfterProperty = null; if ($nullableTypeHint) { $pointerAfterProperty = TokenHelper::findNextEffective($phpcsFile, $propertyPointer + 1); } $phpcsFile->fixer->beginChangeset(); FixerHelper::addBefore($phpcsFile, $propertyPointer, sprintf('%s ', $propertyTypeHint)); if ( $pointerAfterProperty !== null && in_array($tokens[$pointerAfterProperty]['code'], [T_SEMICOLON, T_COMMA], true) ) { FixerHelper::add($phpcsFile, $propertyPointer, ' = null'); } $phpcsFile->fixer->endChangeset(); } /** * @param Annotation|null $propertyAnnotation * @param list> $prefixedPropertyAnnotations */ private function checkTraversableTypeHintSpecification( File $phpcsFile, int $propertyPointer, ?TypeHint $propertyTypeHint, ?Annotation $propertyAnnotation, array $prefixedPropertyAnnotations ): void { $suppressName = $this->getSniffName(self::CODE_MISSING_TRAVERSABLE_TYPE_HINT_SPECIFICATION); $isSuppressed = SuppressHelper::isSniffSuppressed($phpcsFile, $propertyPointer, $suppressName); $hasTraversableTypeHint = $this->hasTraversableTypeHint($phpcsFile, $propertyPointer, $propertyTypeHint, $propertyAnnotation); $hasAnnotation = $this->hasAnnotation($propertyAnnotation); if (!$hasAnnotation) { if ($hasTraversableTypeHint) { if (count($prefixedPropertyAnnotations) !== 0) { $this->reportUselessSuppress($phpcsFile, $propertyPointer, $isSuppressed, $suppressName); return; } if (!$isSuppressed) { $phpcsFile->addError( sprintf( '@var annotation of property %s does not specify type hint for its items.', PropertyHelper::getFullyQualifiedName($phpcsFile, $propertyPointer), ), $propertyPointer, self::CODE_MISSING_TRAVERSABLE_TYPE_HINT_SPECIFICATION, ); } } return; } $typeNode = $propertyAnnotation->getValue()->type; if ( !$hasTraversableTypeHint && !AnnotationTypeHelper::containsTraversableType($typeNode, $phpcsFile, $propertyPointer, $this->getTraversableTypeHints()) ) { $this->reportUselessSuppress($phpcsFile, $propertyPointer, $isSuppressed, $suppressName); return; } if (AnnotationTypeHelper::containsItemsSpecificationForTraversable( $typeNode, $phpcsFile, $propertyPointer, $this->getTraversableTypeHints(), )) { $this->reportUselessSuppress($phpcsFile, $propertyPointer, $isSuppressed, $suppressName); return; } if ($isSuppressed) { return; } $phpcsFile->addError( sprintf( '@var annotation of property %s does not specify type hint for its items.', PropertyHelper::getFullyQualifiedName($phpcsFile, $propertyPointer), ), $propertyAnnotation->getStartPointer(), self::CODE_MISSING_TRAVERSABLE_TYPE_HINT_SPECIFICATION, ); } private function checkUselessAnnotation( File $phpcsFile, int $propertyPointer, ?TypeHint $propertyTypeHint, ?Annotation $propertyAnnotation ): void { if ($propertyAnnotation === null) { return; } $suppressName = self::getSniffName(self::CODE_USELESS_ANNOTATION); $isSuppressed = SuppressHelper::isSniffSuppressed($phpcsFile, $propertyPointer, $suppressName); if (!AnnotationHelper::isAnnotationUseless( $phpcsFile, $propertyPointer, $propertyTypeHint, $propertyAnnotation, $this->getTraversableTypeHints(), $this->enableUnionTypeHint, $this->enableIntersectionTypeHint, $this->enableStandaloneNullTrueFalseTypeHints, )) { $this->reportUselessSuppress($phpcsFile, $propertyPointer, $isSuppressed, $suppressName); return; } if ($isSuppressed) { return; } $fix = $phpcsFile->addFixableError( sprintf( 'Property %s has useless @var annotation.', PropertyHelper::getFullyQualifiedName($phpcsFile, $propertyPointer), ), $propertyAnnotation->getStartPointer(), self::CODE_USELESS_ANNOTATION, ); if (!$fix) { return; } if ($this->isDocCommentUseless($phpcsFile, $propertyPointer)) { $docCommentOpenPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $propertyPointer); $docCommentClosePointer = $phpcsFile->getTokens()[$docCommentOpenPointer]['comment_closer']; $changeStart = $docCommentOpenPointer; /** @var int $changeEnd */ $changeEnd = TokenHelper::findNextEffective($phpcsFile, $docCommentClosePointer + 1) - 1; $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $changeStart, $changeEnd); $phpcsFile->fixer->endChangeset(); return; } /** @var int $changeStart */ $changeStart = TokenHelper::findPrevious($phpcsFile, T_DOC_COMMENT_STAR, $propertyAnnotation->getStartPointer() - 1); /** @var int $changeEnd */ $changeEnd = TokenHelper::findNext( $phpcsFile, [T_DOC_COMMENT_CLOSE_TAG, T_DOC_COMMENT_STAR], $propertyAnnotation->getEndPointer() + 1, ) - 1; $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $changeStart, $changeEnd); $phpcsFile->fixer->endChangeset(); } private function isDocCommentUseless(File $phpcsFile, int $propertyPointer): bool { if (DocCommentHelper::hasDocCommentDescription($phpcsFile, $propertyPointer)) { return false; } foreach (AnnotationHelper::getAnnotations($phpcsFile, $propertyPointer) as $annotation) { if ($annotation->getName() !== '@var') { return false; } } return true; } private function reportUselessSuppress(File $phpcsFile, int $pointer, bool $isSuppressed, string $suppressName): void { if (!$isSuppressed) { return; } $fix = $phpcsFile->addFixableError( sprintf('Useless %s %s', SuppressHelper::ANNOTATION, $suppressName), $pointer, self::CODE_USELESS_SUPPRESS, ); if ($fix) { SuppressHelper::removeSuppressAnnotation($phpcsFile, $pointer, $suppressName); } } private function getSniffName(string $sniffName): string { return sprintf('%s.%s', self::NAME, $sniffName); } /** * @return list */ private function getTraversableTypeHints(): array { $this->normalizedTraversableTypeHints ??= array_map( static fn (string $typeHint): string => NamespaceHelper::isFullyQualifiedName($typeHint) ? $typeHint : sprintf('%s%s', NamespaceHelper::NAMESPACE_SEPARATOR, $typeHint), SniffSettingsHelper::normalizeArray($this->traversableTypeHints), ); return $this->normalizedTraversableTypeHints; } private function hasAnnotation(?Annotation $propertyAnnotation): bool { return $propertyAnnotation !== null && $propertyAnnotation->getValue() instanceof VarTagValueNode; } /** * @param Annotation|null $propertyAnnotation */ private function hasTraversableTypeHint( File $phpcsFile, int $propertyPointer, ?TypeHint $propertyTypeHint, ?Annotation $propertyAnnotation ): bool { if ( $propertyTypeHint !== null && TypeHintHelper::isTraversableType( TypeHintHelper::getFullyQualifiedTypeHint( $phpcsFile, $propertyPointer, $propertyTypeHint->getTypeHintWithoutNullabilitySymbol(), ), $this->getTraversableTypeHints(), ) ) { return true; } return $this->hasAnnotation($propertyAnnotation) && AnnotationTypeHelper::containsTraversableType( $propertyAnnotation->getValue()->type, $phpcsFile, $propertyPointer, $this->getTraversableTypeHints(), ); } /** * @return list> */ private function getValidPrefixedAnnotations(File $phpcsFile, int $docCommentOpenPointer): array { $varAnnotations = []; $annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenPointer); foreach (AnnotationHelper::STATIC_ANALYSIS_PREFIXES as $prefix) { foreach ($annotations as $annotation) { if ($annotation->isInvalid()) { continue; } if ($annotation->getName() === sprintf('@%s-var', $prefix)) { $varAnnotations[] = $annotation; } } } return $varAnnotations; } } PK41]^ӎVcoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/DisallowMixedTypeHintSniff.phpnu[ */ public function register(): array { return [ T_DOC_COMMENT_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $docCommentOpenPointer */ public function process(File $phpcsFile, $docCommentOpenPointer): void { if (SuppressHelper::isSniffSuppressed( $phpcsFile, $docCommentOpenPointer, $this->getSniffName(self::CODE_DISALLOWED_MIXED_TYPE_HINT), )) { return; } $docCommentOwnerPointer = DocCommentHelper::findDocCommentOwnerPointer($phpcsFile, $docCommentOpenPointer); if ( $docCommentOwnerPointer !== null && AttributeHelper::hasAttribute($phpcsFile, $docCommentOwnerPointer, '\Override') ) { return; } $annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenPointer); foreach ($annotations as $annotation) { $identifierTypeNodes = AnnotationHelper::getAnnotationNodesByType($annotation->getNode(), IdentifierTypeNode::class); foreach ($identifierTypeNodes as $typeHintNode) { $typeHint = $typeHintNode->name; if (strtolower($typeHint) !== 'mixed') { continue; } $phpcsFile->addError( 'Usage of "mixed" type hint is disallowed.', $annotation->getStartPointer(), self::CODE_DISALLOWED_MIXED_TYPE_HINT, ); } } } private function getSniffName(string $sniffName): string { return sprintf('%s.%s', self::NAME, $sniffName); } } PK41]^+Vcoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/ReturnTypeHintSpacingSniff.phpnu[ */ public function register(): array { return TokenHelper::FUNCTION_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $functionPointer */ public function process(File $phpcsFile, $functionPointer): void { $this->spacesCountBeforeColon = SniffSettingsHelper::normalizeInteger($this->spacesCountBeforeColon); $typeHint = FunctionHelper::findReturnTypeHint($phpcsFile, $functionPointer); if ($typeHint === null) { return; } $tokens = $phpcsFile->getTokens(); $typeHintStartPointer = $typeHint->getStartPointer(); /** @var int $colonPointer */ $colonPointer = TokenHelper::findPreviousEffective($phpcsFile, $typeHintStartPointer - 1); if ($tokens[$typeHintStartPointer]['code'] !== T_NULLABLE) { if ($tokens[$colonPointer + 1]['code'] !== T_WHITESPACE) { $fix = $phpcsFile->addFixableError( 'There must be exactly one space between return type hint colon and return type hint.', $typeHintStartPointer, self::CODE_NO_SPACE_BETWEEN_COLON_AND_TYPE_HINT, ); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::add($phpcsFile, $colonPointer, ' '); $phpcsFile->fixer->endChangeset(); } } elseif ($tokens[$colonPointer + 1]['content'] !== ' ') { $fix = $phpcsFile->addFixableError( 'There must be exactly one space between return type hint colon and return type hint.', $typeHintStartPointer, self::CODE_MULTIPLE_SPACES_BETWEEN_COLON_AND_TYPE_HINT, ); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $colonPointer + 1, ' '); $phpcsFile->fixer->endChangeset(); } } } else { if ($tokens[$colonPointer + 1]['code'] !== T_WHITESPACE) { $fix = $phpcsFile->addFixableError( 'There must be exactly one space between return type hint colon and return type hint nullability symbol.', $typeHintStartPointer, self::CODE_NO_SPACE_BETWEEN_COLON_AND_NULLABILITY_SYMBOL, ); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::add($phpcsFile, $colonPointer, ' '); $phpcsFile->fixer->endChangeset(); } } elseif ($tokens[$colonPointer + 1]['content'] !== ' ') { $fix = $phpcsFile->addFixableError( 'There must be exactly one space between return type hint colon and return type hint nullability symbol.', $typeHintStartPointer, self::CODE_MULTIPLE_SPACES_BETWEEN_COLON_AND_NULLABILITY_SYMBOL, ); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $colonPointer + 1, ' '); $phpcsFile->fixer->endChangeset(); } } if ($tokens[$typeHintStartPointer + 1]['code'] === T_WHITESPACE) { $fix = $phpcsFile->addFixableError( 'There must be no whitespace between return type hint nullability symbol and return type hint.', $typeHintStartPointer, self::CODE_WHITESPACE_AFTER_NULLABILITY_SYMBOL, ); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $typeHintStartPointer + 1, ''); $phpcsFile->fixer->endChangeset(); } } } $expectedSpaces = str_repeat(' ', $this->spacesCountBeforeColon); if ( $tokens[$colonPointer - 1]['code'] !== T_CLOSE_PARENTHESIS && $tokens[$colonPointer - 1]['content'] !== $expectedSpaces ) { $fix = $this->spacesCountBeforeColon === 0 ? $phpcsFile->addFixableError( 'There must be no whitespace between closing parenthesis and return type colon.', $typeHintStartPointer, self::CODE_WHITESPACE_BEFORE_COLON, ) : $phpcsFile->addFixableError( sprintf( 'There must be exactly %d whitespace%s between closing parenthesis and return type colon.', $this->spacesCountBeforeColon, $this->spacesCountBeforeColon !== 1 ? 's' : '', ), $typeHintStartPointer, self::CODE_INCORRECT_SPACES_BEFORE_COLON, ); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $colonPointer - 1, $expectedSpaces); $phpcsFile->fixer->endChangeset(); } } elseif ($tokens[$colonPointer - 1]['code'] === T_CLOSE_PARENTHESIS && $this->spacesCountBeforeColon !== 0) { $fix = $phpcsFile->addFixableError( sprintf( 'There must be exactly %d whitespace%s between closing parenthesis and return type colon.', $this->spacesCountBeforeColon, $this->spacesCountBeforeColon !== 1 ? 's' : '', ), $typeHintStartPointer, self::CODE_INCORRECT_SPACES_BEFORE_COLON, ); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::add($phpcsFile, $colonPointer - 1, $expectedSpaces); $phpcsFile->fixer->endChangeset(); } } } } PK41]`4 4 Tcoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/UnionTypeHintFormatSniff.phpnu[ */ public function register(): array { return [ T_VARIABLE, ...TokenHelper::FUNCTION_TOKEN_CODES, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { $this->enable = SniffSettingsHelper::isEnabledByPhpVersion($this->enable, 80000); if (!$this->enable) { return; } $tokens = $phpcsFile->getTokens(); if ($tokens[$pointer]['code'] === T_VARIABLE) { if (!PropertyHelper::isProperty($phpcsFile, $pointer)) { return; } $propertyTypeHint = PropertyHelper::findTypeHint($phpcsFile, $pointer); if ($propertyTypeHint !== null) { $this->checkTypeHint($phpcsFile, $propertyTypeHint); } return; } $returnTypeHint = FunctionHelper::findReturnTypeHint($phpcsFile, $pointer); if ($returnTypeHint !== null) { $this->checkTypeHint($phpcsFile, $returnTypeHint); } foreach (FunctionHelper::getParametersTypeHints($phpcsFile, $pointer) as $parameterTypeHint) { if ($parameterTypeHint !== null) { $this->checkTypeHint($phpcsFile, $parameterTypeHint); } } } private function checkTypeHint(File $phpcsFile, TypeHint $typeHint): void { $tokens = $phpcsFile->getTokens(); $typeHintsCount = substr_count($typeHint->getTypeHint(), '|') + 1; if ($typeHintsCount > 1) { if ($this->withSpaces === self::NO) { $whitespacePointer = TokenHelper::findNext( $phpcsFile, T_WHITESPACE, $typeHint->getStartPointer() + 1, $typeHint->getEndPointer(), ); if ($whitespacePointer !== null) { $originalTypeHint = TokenHelper::getContent($phpcsFile, $typeHint->getStartPointer(), $typeHint->getEndPointer()); $fix = $phpcsFile->addFixableError( sprintf('Spaces in type hint "%s" are disallowed.', $originalTypeHint), $typeHint->getStartPointer(), self::CODE_DISALLOWED_WHITESPACE, ); if ($fix) { $this->fixTypeHint($phpcsFile, $typeHint, $typeHint->getTypeHint()); } } } elseif ($this->withSpaces === self::YES) { $error = false; foreach (TokenHelper::findNextAll( $phpcsFile, [T_TYPE_UNION], $typeHint->getStartPointer(), $typeHint->getEndPointer(), ) as $unionSeparator) { if ($tokens[$unionSeparator - 1]['content'] !== ' ') { $error = true; break; } if ($tokens[$unionSeparator + 1]['content'] !== ' ') { $error = true; break; } } if ($error) { $originalTypeHint = TokenHelper::getContent($phpcsFile, $typeHint->getStartPointer(), $typeHint->getEndPointer()); $fix = $phpcsFile->addFixableError( sprintf('One space required before and after each "|" in type hint "%s".', $originalTypeHint), $typeHint->getStartPointer(), self::CODE_REQUIRED_WHITESPACE, ); if ($fix) { $fixedTypeHint = implode(' | ', explode('|', $typeHint->getTypeHint())); $this->fixTypeHint($phpcsFile, $typeHint, $fixedTypeHint); } } } } if (!$typeHint->isNullable()) { return; } $hasShortNullable = strpos($typeHint->getTypeHint(), '?') === 0; if ($this->shortNullable === self::YES && $typeHintsCount === 2 && !$hasShortNullable) { $fix = $phpcsFile->addFixableError( sprintf('Short nullable type hint in "%s" is required.', $typeHint->getTypeHint()), $typeHint->getStartPointer(), self::CODE_REQUIRED_SHORT_NULLABLE, ); if ($fix) { $typeHintWithoutNull = self::getTypeHintContentWithoutNull($phpcsFile, $typeHint); $this->fixTypeHint($phpcsFile, $typeHint, '?' . $typeHintWithoutNull); } } elseif ($this->shortNullable === self::NO && $hasShortNullable) { $fix = $phpcsFile->addFixableError( sprintf('Usage of short nullable type hint in "%s" is disallowed.', $typeHint->getTypeHint()), $typeHint->getStartPointer(), self::CODE_DISALLOWED_SHORT_NULLABLE, ); if ($fix) { $this->fixTypeHint($phpcsFile, $typeHint, substr($typeHint->getTypeHint(), 1) . '|null'); } } if ($hasShortNullable || ($this->shortNullable === self::YES && $typeHintsCount === 2)) { return; } if ($this->nullPosition === self::FIRST && strtolower($tokens[$typeHint->getStartPointer()]['content']) !== 'null') { $fix = $phpcsFile->addFixableError( sprintf('Null type hint should be on first position in "%s".', $typeHint->getTypeHint()), $typeHint->getStartPointer(), self::CODE_NULL_TYPE_HINT_NOT_ON_FIRST_POSITION, ); if ($fix) { $this->fixTypeHint($phpcsFile, $typeHint, 'null|' . self::getTypeHintContentWithoutNull($phpcsFile, $typeHint)); } } elseif ($this->nullPosition === self::LAST && strtolower($tokens[$typeHint->getEndPointer()]['content']) !== 'null') { $fix = $phpcsFile->addFixableError( sprintf('Null type hint should be on last position in "%s".', $typeHint->getTypeHint()), $typeHint->getStartPointer(), self::CODE_NULL_TYPE_HINT_NOT_ON_LAST_POSITION, ); if ($fix) { $this->fixTypeHint($phpcsFile, $typeHint, self::getTypeHintContentWithoutNull($phpcsFile, $typeHint) . '|null'); } } } private function getTypeHintContentWithoutNull(File $phpcsFile, TypeHint $typeHint): string { $tokens = $phpcsFile->getTokens(); if (strtolower($tokens[$typeHint->getEndPointer()]['content']) === 'null') { $previousTypeHintPointer = TokenHelper::findPrevious( $phpcsFile, TokenHelper::ONLY_TYPE_HINT_TOKEN_CODES, $typeHint->getEndPointer() - 1, ); return TokenHelper::getContent($phpcsFile, $typeHint->getStartPointer(), $previousTypeHintPointer); } $content = ''; for ($i = $typeHint->getStartPointer(); $i <= $typeHint->getEndPointer(); $i++) { if (strtolower($tokens[$i]['content']) === 'null') { $i = TokenHelper::findNext($phpcsFile, TokenHelper::ONLY_TYPE_HINT_TOKEN_CODES, $i + 1); } $content .= $tokens[$i]['content']; } return $content; } private function fixTypeHint(File $phpcsFile, TypeHint $typeHint, string $fixedTypeHint): void { $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $typeHint->getStartPointer(), $typeHint->getEndPointer(), $fixedTypeHint); $phpcsFile->fixer->endChangeset(); } } PK41] `coding-standard/SlevomatCodingStandard/Sniffs/TypeHints/NullableTypeForNullDefaultValueSniff.phpnu[ */ public function register(): array { return TokenHelper::FUNCTION_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $functionPointer */ public function process(File $phpcsFile, $functionPointer): void { if (SuppressHelper::isSniffSuppressed($phpcsFile, $functionPointer, self::NAME)) { return; } $tokens = $phpcsFile->getTokens(); $startPointer = $tokens[$functionPointer]['parenthesis_opener'] + 1; $endPointer = $tokens[$functionPointer]['parenthesis_closer']; for ($i = $startPointer; $i < $endPointer; $i++) { if ($tokens[$i]['code'] !== T_VARIABLE) { continue; } $parameterName = $tokens[$i]['content']; $afterVariablePointer = TokenHelper::findNextEffective($phpcsFile, $i + 1); if ($tokens[$afterVariablePointer]['code'] !== T_EQUAL) { continue; } $afterEqualsPointer = TokenHelper::findNextEffective($phpcsFile, $afterVariablePointer + 1); if ($tokens[$afterEqualsPointer]['code'] !== T_NULL) { continue; } $ignoreTokensToFindTypeHint = [...TokenHelper::INEFFECTIVE_TOKEN_CODES, T_BITWISE_AND, T_ELLIPSIS]; $typeHintEndPointer = TokenHelper::findPreviousExcluding($phpcsFile, $ignoreTokensToFindTypeHint, $i - 1, $startPointer); if ( $typeHintEndPointer === null || !in_array($tokens[$typeHintEndPointer]['code'], TokenHelper::ONLY_TYPE_HINT_TOKEN_CODES, true) ) { continue; } $typeHintStartPointer = TypeHintHelper::getStartPointer($phpcsFile, $typeHintEndPointer); $typeHint = TokenHelper::getContent($phpcsFile, $typeHintStartPointer, $typeHintEndPointer); if (strtolower($typeHint) === 'mixed') { continue; } $nullableSymbolPointer = TokenHelper::findPreviousEffective( $phpcsFile, $typeHintStartPointer - 1, $tokens[$functionPointer]['parenthesis_opener'], ); if ($nullableSymbolPointer !== null && $tokens[$nullableSymbolPointer]['code'] === T_NULLABLE) { continue; } if (preg_match('~(?:^|(?:\|\s*))null(?:(?:\s*\|)|$)~i', $typeHint) === 1) { continue; } $fix = $phpcsFile->addFixableError( sprintf('Parameter %s has null default value, but is not marked as nullable.', $parameterName), $i, self::CODE_NULLABILITY_TYPE_MISSING, ); if (!$fix) { continue; } $phpcsFile->fixer->beginChangeset(); if (substr_count($typeHint, '|') > 0) { FixerHelper::add($phpcsFile, $typeHintEndPointer, '|null'); } else { FixerHelper::addBefore($phpcsFile, $typeHintStartPointer, '?'); } $phpcsFile->fixer->endChangeset(); } } } PK41]-q1]]Ocoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/ReturnTypeHintSniff.phpnu[ */ public array $traversableTypeHints = []; /** @var list|null */ private ?array $normalizedTraversableTypeHints = null; /** * @return array */ public function register(): array { return [ T_FUNCTION, T_CLOSURE, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { $this->enableObjectTypeHint = SniffSettingsHelper::isEnabledByPhpVersion($this->enableObjectTypeHint, 70200); $this->enableStaticTypeHint = SniffSettingsHelper::isEnabledByPhpVersion($this->enableStaticTypeHint, 80000); $this->enableMixedTypeHint = SniffSettingsHelper::isEnabledByPhpVersion($this->enableMixedTypeHint, 80000); $this->enableUnionTypeHint = SniffSettingsHelper::isEnabledByPhpVersion($this->enableUnionTypeHint, 80000); $this->enableIntersectionTypeHint = SniffSettingsHelper::isEnabledByPhpVersion($this->enableIntersectionTypeHint, 80100); $this->enableNeverTypeHint = SniffSettingsHelper::isEnabledByPhpVersion($this->enableNeverTypeHint, 80100); $this->enableStandaloneNullTrueFalseTypeHints = SniffSettingsHelper::isEnabledByPhpVersion( $this->enableStandaloneNullTrueFalseTypeHints, 80200, ); if (SuppressHelper::isSniffSuppressed($phpcsFile, $pointer, self::NAME)) { return; } if (DocCommentHelper::hasInheritdocAnnotation($phpcsFile, $pointer)) { return; } $token = $phpcsFile->getTokens()[$pointer]; if ($token['code'] === T_FUNCTION) { $returnTypeHint = FunctionHelper::findReturnTypeHint($phpcsFile, $pointer); $returnAnnotation = FunctionHelper::findReturnAnnotation($phpcsFile, $pointer); $prefixedReturnAnnotations = FunctionHelper::getValidPrefixedReturnAnnotations($phpcsFile, $pointer); $this->checkFunctionTypeHint($phpcsFile, $pointer, $returnTypeHint, $returnAnnotation, $prefixedReturnAnnotations); $this->checkFunctionTraversableTypeHintSpecification( $phpcsFile, $pointer, $returnTypeHint, $returnAnnotation, $prefixedReturnAnnotations, ); $this->checkFunctionUselessAnnotation($phpcsFile, $pointer, $returnTypeHint, $returnAnnotation); } elseif ($token['code'] === T_CLOSURE) { $this->checkClosureTypeHint($phpcsFile, $pointer); } } /** * @param list $prefixedReturnAnnotations */ private function checkFunctionTypeHint( File $phpcsFile, int $functionPointer, ?TypeHint $returnTypeHint, ?Annotation $returnAnnotation, array $prefixedReturnAnnotations ): void { $suppressNameAnyTypeHint = $this->getSniffName(self::CODE_MISSING_ANY_TYPE_HINT); $isSuppressedAnyTypeHint = SuppressHelper::isSniffSuppressed($phpcsFile, $functionPointer, $suppressNameAnyTypeHint); $suppressNameNativeTypeHint = $this->getSniffName(self::CODE_MISSING_NATIVE_TYPE_HINT); $isSuppressedNativeTypeHint = SuppressHelper::isSniffSuppressed($phpcsFile, $functionPointer, $suppressNameNativeTypeHint); $hasReturnAnnotation = $this->hasReturnAnnotation($returnAnnotation); $returnTypeNode = $this->getReturnTypeNode($returnAnnotation); $isAnnotationReturnTypeNever = $returnTypeNode instanceof IdentifierTypeNode && TypeHintHelper::isNeverTypeHint(strtolower($returnTypeNode->name)); if ($returnTypeHint !== null) { $this->reportUselessSuppress($phpcsFile, $functionPointer, $isSuppressedAnyTypeHint, $suppressNameAnyTypeHint); $this->reportUselessSuppress($phpcsFile, $functionPointer, $isSuppressedNativeTypeHint, $suppressNameNativeTypeHint); if ($this->enableNeverTypeHint && $returnTypeHint->getTypeHint() === 'void' && $isAnnotationReturnTypeNever) { $fix = $phpcsFile->addFixableError( sprintf( '%s %s() has return type hint "void" but it should be possible to add "never" based on @return annotation "%s".', FunctionHelper::getTypeLabel($phpcsFile, $functionPointer), FunctionHelper::getFullyQualifiedName($phpcsFile, $functionPointer), AnnotationTypeHelper::print($returnTypeNode), ), $functionPointer, self::CODE_LESS_SPECIFIC_NATIVE_TYPE_HINT, ); if ($fix) { $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $returnTypeHint->getStartPointer(), 'never'); $phpcsFile->fixer->endChangeset(); } } return; } $methodsWithoutVoidSupport = ['__construct' => true, '__destruct' => true, '__clone' => true]; if (array_key_exists(FunctionHelper::getName($phpcsFile, $functionPointer), $methodsWithoutVoidSupport)) { $this->reportUselessSuppress($phpcsFile, $functionPointer, $isSuppressedAnyTypeHint, $suppressNameAnyTypeHint); $this->reportUselessSuppress($phpcsFile, $functionPointer, $isSuppressedNativeTypeHint, $suppressNameNativeTypeHint); return; } $isAnnotationReturnTypeVoidOrNever = $returnTypeNode instanceof IdentifierTypeNode && ( TypeHintHelper::isVoidTypeHint(strtolower($returnTypeNode->name)) || $isAnnotationReturnTypeNever ); $isAbstract = FunctionHelper::isAbstract($phpcsFile, $functionPointer); $returnsValue = $isAbstract ? ($hasReturnAnnotation && !$isAnnotationReturnTypeVoidOrNever) : FunctionHelper::returnsValue($phpcsFile, $functionPointer); if (($returnsValue || $isAbstract) && !$hasReturnAnnotation) { if (count($prefixedReturnAnnotations) !== 0) { $this->reportUselessSuppress($phpcsFile, $functionPointer, $isSuppressedAnyTypeHint, $suppressNameAnyTypeHint); return; } if (!$isSuppressedAnyTypeHint) { $phpcsFile->addError( sprintf( '%s %s() does not have return type hint nor @return annotation for its return value.', FunctionHelper::getTypeLabel($phpcsFile, $functionPointer), FunctionHelper::getFullyQualifiedName($phpcsFile, $functionPointer), ), $functionPointer, self::CODE_MISSING_ANY_TYPE_HINT, ); } return; } if ( !$returnsValue && ( !$hasReturnAnnotation || $isAnnotationReturnTypeVoidOrNever ) ) { if (!$isSuppressedNativeTypeHint) { $message = !$hasReturnAnnotation ? sprintf( '%s %s() does not have void return type hint.', FunctionHelper::getTypeLabel($phpcsFile, $functionPointer), FunctionHelper::getFullyQualifiedName($phpcsFile, $functionPointer), ) : sprintf( '%s %s() does not have native return type hint for its return value but it should be possible to add it based on @return annotation "%s".', FunctionHelper::getTypeLabel($phpcsFile, $functionPointer), FunctionHelper::getFullyQualifiedName($phpcsFile, $functionPointer), AnnotationTypeHelper::print($returnTypeNode), ); $fix = $phpcsFile->addFixableError($message, $functionPointer, self::getSniffName(self::CODE_MISSING_NATIVE_TYPE_HINT)); if ($fix) { $fixedReturnType = $this->enableNeverTypeHint && $isAnnotationReturnTypeNever ? 'never' : 'void'; $phpcsFile->fixer->beginChangeset(); FixerHelper::add( $phpcsFile, $phpcsFile->getTokens()[$functionPointer]['parenthesis_closer'], sprintf(': %s', $fixedReturnType), ); $phpcsFile->fixer->endChangeset(); } } return; } if (!$isSuppressedNativeTypeHint && $returnsValue && $isAnnotationReturnTypeVoidOrNever) { $message = sprintf( '%s %s() does not have native return type hint for its return value but it should be possible to add it based on @return annotation "%s".', FunctionHelper::getTypeLabel($phpcsFile, $functionPointer), FunctionHelper::getFullyQualifiedName($phpcsFile, $functionPointer), AnnotationTypeHelper::print($returnTypeNode), ); $phpcsFile->addError($message, $functionPointer, self::getSniffName(self::CODE_MISSING_NATIVE_TYPE_HINT)); return; } $canTryUnionTypeHint = $this->enableUnionTypeHint && $returnTypeNode instanceof UnionTypeNode; $typeHints = []; $traversableTypeHints = []; $nullableReturnTypeHint = false; $originalReturnTypeNode = $returnTypeNode; if ($returnTypeNode instanceof NullableTypeNode) { $returnTypeNode = $returnTypeNode->type; } if (AnnotationTypeHelper::containsOneType($returnTypeNode)) { /** @var ArrayTypeNode|ArrayShapeNode|ObjectShapeNode|IdentifierTypeNode|ThisTypeNode|GenericTypeNode|CallableTypeNode $returnTypeNode */ $returnTypeNode = $returnTypeNode; $typeHints[] = AnnotationTypeHelper::getTypeHintFromOneType( $returnTypeNode, false, $this->enableStandaloneNullTrueFalseTypeHints, ); } elseif ($returnTypeNode instanceof UnionTypeNode || $returnTypeNode instanceof IntersectionTypeNode) { $traversableTypeHints = []; foreach ($returnTypeNode->types as $typeNode) { if (!AnnotationTypeHelper::containsOneType($typeNode)) { $this->reportUselessSuppress($phpcsFile, $functionPointer, $isSuppressedNativeTypeHint, $suppressNameNativeTypeHint); return; } /** @var ArrayTypeNode|ArrayShapeNode|ObjectShapeNode|IdentifierTypeNode|ThisTypeNode|GenericTypeNode|CallableTypeNode $typeNode */ $typeNode = $typeNode; $typeHint = AnnotationTypeHelper::getTypeHintFromOneType($typeNode, $canTryUnionTypeHint); if (strtolower($typeHint) === 'null') { $nullableReturnTypeHint = true; continue; } $isTraversable = TypeHintHelper::isTraversableType( TypeHintHelper::getFullyQualifiedTypeHint($phpcsFile, $functionPointer, $typeHint), $this->getTraversableTypeHints(), ); if ( !$typeNode instanceof ArrayTypeNode && !$typeNode instanceof ArrayShapeNode && $isTraversable ) { $traversableTypeHints[] = $typeHint; } $typeHints[] = $typeHint; } $traversableTypeHints = array_values(array_unique($traversableTypeHints)); if (count($traversableTypeHints) > 1 && !$canTryUnionTypeHint) { $this->reportUselessSuppress($phpcsFile, $functionPointer, $isSuppressedNativeTypeHint, $suppressNameNativeTypeHint); return; } } $typeHints = array_values(array_unique($typeHints)); if (count($traversableTypeHints) > 0) { /** @var UnionTypeNode|IntersectionTypeNode $returnTypeNode */ $returnTypeNode = $returnTypeNode; $itemsSpecificationTypeHint = AnnotationTypeHelper::getItemsSpecificationTypeFromType($returnTypeNode); if ($itemsSpecificationTypeHint !== null) { $typeHints = AnnotationTypeHelper::getTraversableTypeHintsFromType( $returnTypeNode, $phpcsFile, $functionPointer, $this->getTraversableTypeHints(), $canTryUnionTypeHint, ); } } if (count($typeHints) === 0) { $this->reportUselessSuppress($phpcsFile, $functionPointer, $isSuppressedNativeTypeHint, $suppressNameNativeTypeHint); return; } $typeHintsWithConvertedUnion = []; foreach ($typeHints as $typeHint) { if ($this->enableUnionTypeHint && TypeHintHelper::isUnofficialUnionTypeHint($typeHint)) { $canTryUnionTypeHint = true; array_push($typeHintsWithConvertedUnion, ...TypeHintHelper::convertUnofficialUnionTypeHintToOfficialTypeHints($typeHint)); } else { $typeHintsWithConvertedUnion[] = $typeHint; } } $typeHintsWithConvertedUnion = array_unique($typeHintsWithConvertedUnion); if ( count($typeHintsWithConvertedUnion) > 1 && ( ($returnTypeNode instanceof UnionTypeNode && !$canTryUnionTypeHint) || ($returnTypeNode instanceof IntersectionTypeNode && !$this->enableIntersectionTypeHint) ) ) { $this->reportUselessSuppress($phpcsFile, $functionPointer, $isSuppressedNativeTypeHint, $suppressNameNativeTypeHint); return; } foreach ($typeHintsWithConvertedUnion as $typeHintNo => $typeHint) { if ($canTryUnionTypeHint && $typeHint === 'false') { continue; } if (!TypeHintHelper::isValidTypeHint( $typeHint, $this->enableObjectTypeHint, $this->enableStaticTypeHint, $this->enableMixedTypeHint, $this->enableStandaloneNullTrueFalseTypeHints, )) { $this->reportUselessSuppress($phpcsFile, $functionPointer, $isSuppressedNativeTypeHint, $suppressNameNativeTypeHint); return; } if (TypeHintHelper::isTypeDefinedInAnnotation($phpcsFile, $functionPointer, $typeHint)) { $this->reportUselessSuppress($phpcsFile, $functionPointer, $isSuppressedNativeTypeHint, $suppressNameNativeTypeHint); return; } if (TypeHintHelper::isVoidTypeHint($typeHint) || TypeHintHelper::isNeverTypeHint($typeHint)) { $this->reportUselessSuppress($phpcsFile, $functionPointer, $isSuppressedNativeTypeHint, $suppressNameNativeTypeHint); return; } $typeHintsWithConvertedUnion[$typeHintNo] = TypeHintHelper::convertLongSimpleTypeHintToShort($typeHint); } if ($originalReturnTypeNode instanceof NullableTypeNode) { $nullableReturnTypeHint = true; } if ($isSuppressedNativeTypeHint) { return; } $fix = $phpcsFile->addFixableError( sprintf( '%s %s() does not have native return type hint for its return value but it should be possible to add it based on @return annotation "%s".', FunctionHelper::getTypeLabel($phpcsFile, $functionPointer), FunctionHelper::getFullyQualifiedName($phpcsFile, $functionPointer), AnnotationTypeHelper::print($returnTypeNode), ), $functionPointer, self::CODE_MISSING_NATIVE_TYPE_HINT, ); if (!$fix) { return; } if (in_array('mixed', $typeHintsWithConvertedUnion, true)) { $returnTypeHint = 'mixed'; } elseif ($originalReturnTypeNode instanceof IntersectionTypeNode) { $returnTypeHint = implode('&', $typeHintsWithConvertedUnion); } else { $returnTypeHint = implode('|', $typeHintsWithConvertedUnion); if ($nullableReturnTypeHint) { if (count($typeHintsWithConvertedUnion) > 1) { $returnTypeHint .= '|null'; } else { $returnTypeHint = '?' . $returnTypeHint; } } } $phpcsFile->fixer->beginChangeset(); FixerHelper::add( $phpcsFile, $phpcsFile->getTokens()[$functionPointer]['parenthesis_closer'], sprintf(': %s', $returnTypeHint), ); $phpcsFile->fixer->endChangeset(); } /** * @param list $prefixedReturnAnnotations */ private function checkFunctionTraversableTypeHintSpecification( File $phpcsFile, int $functionPointer, ?TypeHint $returnTypeHint, ?Annotation $returnAnnotation, array $prefixedReturnAnnotations ): void { $suppressName = $this->getSniffName(self::CODE_MISSING_TRAVERSABLE_TYPE_HINT_SPECIFICATION); $isSuppressed = SuppressHelper::isSniffSuppressed($phpcsFile, $functionPointer, $suppressName); $hasTraversableTypeHint = $this->hasTraversableTypeHint($phpcsFile, $functionPointer, $returnTypeHint, $returnAnnotation); $hasReturnAnnotation = $this->hasReturnAnnotation($returnAnnotation); if (!$hasReturnAnnotation) { if ($hasTraversableTypeHint) { if (count($prefixedReturnAnnotations) !== 0) { $this->reportUselessSuppress($phpcsFile, $functionPointer, $isSuppressed, $suppressName); return; } if (!$isSuppressed) { $phpcsFile->addError( sprintf( '%s %s() does not have @return annotation for its traversable return value.', FunctionHelper::getTypeLabel($phpcsFile, $functionPointer), FunctionHelper::getFullyQualifiedName($phpcsFile, $functionPointer), ), $functionPointer, self::CODE_MISSING_TRAVERSABLE_TYPE_HINT_SPECIFICATION, ); } } return; } $returnTypeNode = $this->getReturnTypeNode($returnAnnotation); if ( !$hasTraversableTypeHint && !AnnotationTypeHelper::containsTraversableType( $returnTypeNode, $phpcsFile, $functionPointer, $this->getTraversableTypeHints(), ) ) { $this->reportUselessSuppress($phpcsFile, $functionPointer, $isSuppressed, $suppressName); return; } if (AnnotationTypeHelper::containsItemsSpecificationForTraversable( $returnTypeNode, $phpcsFile, $functionPointer, $this->getTraversableTypeHints(), )) { $this->reportUselessSuppress($phpcsFile, $functionPointer, $isSuppressed, $suppressName); return; } if ($isSuppressed) { return; } /** @var Annotation $returnAnnotation */ $returnAnnotation = $returnAnnotation; $phpcsFile->addError( sprintf( '@return annotation of %s %s() does not specify type hint for items of its traversable return value.', lcfirst(FunctionHelper::getTypeLabel($phpcsFile, $functionPointer)), FunctionHelper::getFullyQualifiedName($phpcsFile, $functionPointer), ), $returnAnnotation->getStartPointer(), self::CODE_MISSING_TRAVERSABLE_TYPE_HINT_SPECIFICATION, ); } private function checkFunctionUselessAnnotation( File $phpcsFile, int $functionPointer, ?TypeHint $returnTypeHint, ?Annotation $returnAnnotation ): void { if ($returnAnnotation === null) { return; } $suppressName = self::getSniffName(self::CODE_USELESS_ANNOTATION); $isSuppressed = SuppressHelper::isSniffSuppressed($phpcsFile, $functionPointer, $suppressName); if (!AnnotationHelper::isAnnotationUseless( $phpcsFile, $functionPointer, $returnTypeHint, $returnAnnotation, $this->getTraversableTypeHints(), $this->enableUnionTypeHint, $this->enableIntersectionTypeHint, $this->enableStandaloneNullTrueFalseTypeHints, )) { $this->reportUselessSuppress($phpcsFile, $functionPointer, $isSuppressed, $suppressName); return; } if ($isSuppressed) { return; } $fix = $phpcsFile->addFixableError( sprintf( '%s %s() has useless @return annotation.', FunctionHelper::getTypeLabel($phpcsFile, $functionPointer), FunctionHelper::getFullyQualifiedName($phpcsFile, $functionPointer), ), $returnAnnotation->getStartPointer(), self::CODE_USELESS_ANNOTATION, ); if (!$fix) { return; } $docCommentOpenPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $functionPointer); $starPointer = TokenHelper::findPrevious( $phpcsFile, T_DOC_COMMENT_STAR, $returnAnnotation->getStartPointer() - 1, $docCommentOpenPointer, ); $changeStart = $starPointer ?? $returnAnnotation->getStartPointer(); /** @var int $changeEnd */ $changeEnd = TokenHelper::findNext( $phpcsFile, [T_DOC_COMMENT_CLOSE_TAG, T_DOC_COMMENT_STAR], $returnAnnotation->getEndPointer() + 1, ) - 1; $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $changeStart, $changeEnd); $phpcsFile->fixer->endChangeset(); } private function checkClosureTypeHint(File $phpcsFile, int $closurePointer): void { $returnTypeHint = FunctionHelper::findReturnTypeHint($phpcsFile, $closurePointer); $returnsValue = FunctionHelper::returnsValue($phpcsFile, $closurePointer); if ($returnsValue || $returnTypeHint !== null) { return; } $fix = $phpcsFile->addFixableError( 'Closure does not have void return type hint.', $closurePointer, self::CODE_MISSING_NATIVE_TYPE_HINT, ); if (!$fix) { return; } $tokens = $phpcsFile->getTokens(); /** @var int $position */ $position = TokenHelper::findPreviousEffective($phpcsFile, $tokens[$closurePointer]['scope_opener'] - 1, $closurePointer); $phpcsFile->fixer->beginChangeset(); FixerHelper::add($phpcsFile, $position, ': void'); $phpcsFile->fixer->endChangeset(); } /** * @param Annotation|null $returnAnnotation */ private function getReturnTypeNode(?Annotation $returnAnnotation): ?TypeNode { if ($this->hasReturnAnnotation($returnAnnotation)) { return $returnAnnotation->getValue()->type; } return null; } private function hasTraversableTypeHint( File $phpcsFile, int $functionPointer, ?TypeHint $returnTypeHint, ?Annotation $returnAnnotation ): bool { if ( $returnTypeHint !== null && TypeHintHelper::isTraversableType( TypeHintHelper::getFullyQualifiedTypeHint( $phpcsFile, $functionPointer, $returnTypeHint->getTypeHintWithoutNullabilitySymbol(), ), $this->getTraversableTypeHints(), ) ) { return true; } return $this->hasReturnAnnotation($returnAnnotation) && AnnotationTypeHelper::containsTraversableType( $this->getReturnTypeNode($returnAnnotation), $phpcsFile, $functionPointer, $this->getTraversableTypeHints(), ); } private function hasReturnAnnotation(?Annotation $returnAnnotation): bool { return $returnAnnotation !== null && !$returnAnnotation->isInvalid(); } private function reportUselessSuppress(File $phpcsFile, int $pointer, bool $isSuppressed, string $suppressName): void { if (!$isSuppressed) { return; } $fix = $phpcsFile->addFixableError( sprintf('Useless %s %s', SuppressHelper::ANNOTATION, $suppressName), $pointer, self::CODE_USELESS_SUPPRESS, ); if ($fix) { SuppressHelper::removeSuppressAnnotation($phpcsFile, $pointer, $suppressName); } } private function getSniffName(string $sniffName): string { return sprintf('%s.%s', self::NAME, $sniffName); } /** * @return list */ private function getTraversableTypeHints(): array { $this->normalizedTraversableTypeHints ??= array_map( static fn (string $typeHint): string => NamespaceHelper::isFullyQualifiedName($typeHint) ? $typeHint : sprintf('%s%s', NamespaceHelper::NAMESPACE_SEPARATOR, $typeHint), SniffSettingsHelper::normalizeArray($this->traversableTypeHints), ); return $this->normalizedTraversableTypeHints; } } PK41]vM M Xcoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/UselessConstantTypeHintSniff.phpnu[ */ public function register(): array { return [ T_CONST, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $constantPointer */ public function process(File $phpcsFile, $constantPointer): void { $tokens = $phpcsFile->getTokens(); $docCommentOpenPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $constantPointer); if ($docCommentOpenPointer === null) { return; } $annotations = AnnotationHelper::getAnnotations($phpcsFile, $constantPointer, '@var'); if ($annotations === []) { return; } $uselessDocComment = !DocCommentHelper::hasDocCommentDescription($phpcsFile, $constantPointer) && count($annotations) === 1; if ($uselessDocComment) { $fix = $phpcsFile->addFixableError('Useless documentation comment.', $docCommentOpenPointer, self::CODE_USELESS_DOC_COMMENT); /** @var int $fixerStart */ $fixerStart = TokenHelper::findLastTokenOnPreviousLine($phpcsFile, $docCommentOpenPointer); $fixerEnd = $tokens[$docCommentOpenPointer]['comment_closer']; } else { $annotation = $annotations[0]; $fix = $phpcsFile->addFixableError( 'Useless @var annotation.', $annotation->getStartPointer(), self::CODE_USELESS_VAR_ANNOTATION, ); /** @var int $fixerStart */ $fixerStart = TokenHelper::findPreviousContent( $phpcsFile, T_DOC_COMMENT_WHITESPACE, $phpcsFile->eolChar, $annotation->getStartPointer() - 1, ); $fixerEnd = $annotation->getEndPointer(); } if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $fixerStart, $fixerEnd); $phpcsFile->fixer->endChangeset(); } } PK41]|t??[coding-standard/SlevomatCodingStandard/Sniffs/Exceptions/DisallowNonCapturingCatchSniff.phpnu[ */ public function register(): array { return [ T_CATCH, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $catchPointer */ public function process(File $phpcsFile, $catchPointer): void { $tokens = $phpcsFile->getTokens(); $variablePointer = TokenHelper::findNext( $phpcsFile, T_VARIABLE, $tokens[$catchPointer]['parenthesis_opener'], $tokens[$catchPointer]['parenthesis_closer'], ); if ($variablePointer === null) { $phpcsFile->addError('Use of non-capturing catch is disallowed.', $catchPointer, self::CODE_DISALLOWED_NON_CAPTURING_CATCH); } } } PK41]o..Zcoding-standard/SlevomatCodingStandard/Sniffs/Exceptions/RequireNonCapturingCatchSniff.phpnu[ */ public function register(): array { return [ T_CATCH, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $catchPointer */ public function process(File $phpcsFile, $catchPointer): void { $this->enable = SniffSettingsHelper::isEnabledByPhpVersion($this->enable, 80000); if (!$this->enable) { return; } $tokens = $phpcsFile->getTokens(); $variablePointer = TokenHelper::findNext( $phpcsFile, T_VARIABLE, $tokens[$catchPointer]['parenthesis_opener'], $tokens[$catchPointer]['parenthesis_closer'], ); if ($variablePointer === null) { return; } $variableName = $tokens[$variablePointer]['content']; if ($this->isVariableUsedInCodePart( $phpcsFile, $tokens[$catchPointer]['scope_opener'], $tokens[$catchPointer]['scope_closer'], $variableName, )) { return; } $tryEndPointer = CatchHelper::getTryEndPointer($phpcsFile, $catchPointer); $possibleFinallyPointer = $tokens[$tryEndPointer]['scope_condition']; if ( $tokens[$possibleFinallyPointer]['code'] === T_FINALLY && $this->isVariableUsedInCodePart( $phpcsFile, $tokens[$possibleFinallyPointer]['scope_opener'], $tokens[$possibleFinallyPointer]['scope_closer'], $variableName, ) ) { return; } $nextScopeEnd = count($tokens) - 1; foreach (array_reverse($tokens[$tryEndPointer]['conditions'], true) as $conditionPointer => $conditionCode) { if (in_array($conditionCode, TokenHelper::FUNCTION_TOKEN_CODES, true)) { $nextScopeEnd = $tokens[$conditionPointer]['scope_closer']; break; } } if ($this->isVariableUsedInCodePart($phpcsFile, $tryEndPointer, $nextScopeEnd, $variableName)) { return; } $fix = $phpcsFile->addFixableError('Non-capturing catch is required.', $catchPointer, self::CODE_NON_CAPTURING_CATCH_REQUIRED); if (!$fix) { return; } $pointerBeforeVariable = TokenHelper::findPreviousEffective($phpcsFile, $variablePointer - 1); $fixEndPointer = TokenHelper::findNextContent( $phpcsFile, T_WHITESPACE, $phpcsFile->eolChar, $variablePointer + 1, $tokens[$catchPointer]['parenthesis_closer'], ); $fixEndPointer ??= $tokens[$catchPointer]['parenthesis_closer']; $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetween($phpcsFile, $pointerBeforeVariable, $fixEndPointer); $phpcsFile->fixer->endChangeset(); } private function isVariableUsedInCodePart(File $phpcsFile, int $codeStartPointer, int $codeEndPointer, string $variableName): bool { $tokens = $phpcsFile->getTokens(); $firstPointerInCode = $codeStartPointer + 1; for ($i = $firstPointerInCode; $i <= $codeEndPointer; $i++) { if ($tokens[$i]['code'] === T_VARIABLE) { if ($tokens[$i]['content'] !== $variableName) { continue; } if (ParameterHelper::isParameter($phpcsFile, $i)) { continue; } if (!ScopeHelper::isInSameScope($phpcsFile, $firstPointerInCode, $i)) { continue; } $catchPointer = TokenHelper::findPrevious($phpcsFile, T_CATCH, $i - 1, $firstPointerInCode); if ($catchPointer === null) { return true; } if ($tokens[$catchPointer]['parenthesis_closer'] < $i) { return true; } } elseif ( in_array($tokens[$i]['code'], [T_DOUBLE_QUOTED_STRING, T_HEREDOC], true) && VariableHelper::isUsedInScopeInString($phpcsFile, $variableName, $i) ) { return true; } } return false; } } PK41]ԤMKcoding-standard/SlevomatCodingStandard/Sniffs/Exceptions/DeadCatchSniff.phpnu[ */ public function register(): array { return [ T_CATCH, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $catchPointer */ public function process(File $phpcsFile, $catchPointer): void { $tokens = $phpcsFile->getTokens(); $catchToken = $tokens[$catchPointer]; $caughtTypes = CatchHelper::findCaughtTypesInCatch($phpcsFile, $catchToken); if (!in_array('\\Throwable', $caughtTypes, true)) { return; } $nextCatchPointer = TokenHelper::findNextEffective($phpcsFile, $catchToken['scope_closer'] + 1); while ($nextCatchPointer !== null) { $nextCatchToken = $tokens[$nextCatchPointer]; if ($nextCatchToken['code'] !== T_CATCH) { break; } $phpcsFile->addError('Unreachable catch block.', $nextCatchPointer, self::CODE_CATCH_AFTER_THROWABLE_CATCH); $nextCatchPointer = TokenHelper::findNextEffective($phpcsFile, $nextCatchToken['scope_closer'] + 1); } } } PK41]~"@@Xcoding-standard/SlevomatCodingStandard/Sniffs/Exceptions/ReferenceThrowableOnlySniff.phpnu[ */ public function register(): array { return [ T_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $openTagPointer */ public function process(File $phpcsFile, $openTagPointer): void { if (TokenHelper::findPrevious($phpcsFile, T_OPEN_TAG, $openTagPointer - 1) !== null) { return; } $tokens = $phpcsFile->getTokens(); $message = sprintf('Referencing general \%s; use \%s instead.', Exception::class, Throwable::class); $referencedNames = ReferencedNameHelper::getAllReferencedNames($phpcsFile, $openTagPointer); foreach ($referencedNames as $referencedName) { $resolvedName = NamespaceHelper::resolveClassName( $phpcsFile, $referencedName->getNameAsReferencedInFile(), $referencedName->getStartPointer(), ); if ($resolvedName !== '\\Exception') { continue; } $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $referencedName->getStartPointer() - 1); if (in_array($tokens[$previousPointer]['code'], [T_EXTENDS, T_NEW, T_INSTANCEOF], true)) { // Allow \Exception in extends and instantiating it continue; } if ($tokens[$previousPointer]['code'] === T_BITWISE_OR) { $previousPointer = TokenHelper::findPreviousExcluding( $phpcsFile, [...TokenHelper::INEFFECTIVE_TOKEN_CODES, ...TokenHelper::NAME_TOKEN_CODES, T_BITWISE_OR], $previousPointer - 1, ); } if ($tokens[$previousPointer]['code'] === T_OPEN_PARENTHESIS) { /** @var int $openParenthesisOpenerPointer */ $openParenthesisOpenerPointer = TokenHelper::findPreviousEffective($phpcsFile, $previousPointer - 1); if ($tokens[$openParenthesisOpenerPointer]['code'] === T_CATCH) { if ($this->searchForThrowableInNextCatches($phpcsFile, $openParenthesisOpenerPointer)) { continue; } } elseif ( array_key_exists('parenthesis_owner', $tokens[$previousPointer]) && $tokens[$tokens[$previousPointer]['parenthesis_owner']]['code'] === T_FUNCTION && $tokens[$previousPointer]['parenthesis_closer'] > $referencedName->getStartPointer() && SuppressHelper::isSniffSuppressed( $phpcsFile, $openParenthesisOpenerPointer, sprintf('%s.%s', self::NAME, self::CODE_REFERENCED_GENERAL_EXCEPTION), ) ) { continue; } } $fix = $phpcsFile->addFixableError( $message, $referencedName->getStartPointer(), self::CODE_REFERENCED_GENERAL_EXCEPTION, ); if (!$fix) { continue; } $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $referencedName->getStartPointer(), $referencedName->getEndPointer(), '\Throwable'); $phpcsFile->fixer->endChangeset(); } } private function searchForThrowableInNextCatches(File $phpcsFile, int $catchPointer): bool { $tokens = $phpcsFile->getTokens(); $nextCatchPointer = TokenHelper::findNextEffective($phpcsFile, $tokens[$catchPointer]['scope_closer'] + 1); while ($nextCatchPointer !== null) { $nextCatchToken = $tokens[$nextCatchPointer]; if ($nextCatchToken['code'] !== T_CATCH) { break; } $caughtTypes = CatchHelper::findCaughtTypesInCatch($phpcsFile, $nextCatchToken); if (in_array('\\Throwable', $caughtTypes, true)) { return true; } $nextCatchPointer = TokenHelper::findNextEffective($phpcsFile, $nextCatchToken['scope_closer'] + 1); } return false; } } PK41]ㄧ^coding-standard/SlevomatCodingStandard/Sniffs/Numbers/DisallowNumericLiteralSeparatorSniff.phpnu[ */ public function register(): array { return [ T_LNUMBER, T_DNUMBER, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $numberPointer */ public function process(File $phpcsFile, $numberPointer): void { $tokens = $phpcsFile->getTokens(); if (strpos($tokens[$numberPointer]['content'], '_') === false) { return; } $fix = $phpcsFile->addFixableError( 'Use of numeric literal separator is disallowed.', $numberPointer, self::CODE_DISALLOWED_NUMERIC_LITERAL_SEPARATOR, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace( $phpcsFile, $numberPointer, str_replace('_', '', $tokens[$numberPointer]['content']), ); $phpcsFile->fixer->endChangeset(); } } PK41]Pytt]coding-standard/SlevomatCodingStandard/Sniffs/Numbers/RequireNumericLiteralSeparatorSniff.phpnu[ */ public function register(): array { return [ T_LNUMBER, T_DNUMBER, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $numberPointer */ public function process(File $phpcsFile, $numberPointer): void { $this->enable = SniffSettingsHelper::isEnabledByPhpVersion($this->enable, 70400); $this->minDigitsBeforeDecimalPoint = SniffSettingsHelper::normalizeInteger($this->minDigitsBeforeDecimalPoint); $this->minDigitsAfterDecimalPoint = SniffSettingsHelper::normalizeInteger($this->minDigitsAfterDecimalPoint); if (!$this->enable) { return; } $tokens = $phpcsFile->getTokens(); $number = $tokens[$numberPointer]['content']; if (strpos($tokens[$numberPointer]['content'], '_') !== false) { return; } if ( $this->ignoreOctalNumbers && preg_match('~^0[0-7]+$~', $number) === 1 ) { return; } $regexp = '~(?:^\\d{' . $this->minDigitsBeforeDecimalPoint . '}|\.\\d{' . $this->minDigitsAfterDecimalPoint . '})~'; if (preg_match($regexp, $number) === 0) { return; } $phpcsFile->addError( 'Use of numeric literal separator is required.', $numberPointer, self::CODE_REQUIRED_NUMERIC_LITERAL_SEPARATOR, ); } } PK41]w//\coding-standard/SlevomatCodingStandard/Sniffs/Variables/DisallowSuperGlobalVariableSniff.phpnu[ */ public function register(): array { return [ T_VARIABLE, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { $tokens = $phpcsFile->getTokens(); if (!in_array($tokens[$pointer]['content'], self::SUPER_GLOBALS, true)) { return; } $phpcsFile->addError('Use of super global variable is disallowed.', $pointer, self::CODE_DISALLOWED_SUPER_GLOBAL_VARIABLE); } } PK41] ~~^coding-standard/SlevomatCodingStandard/Sniffs/Variables/DuplicateAssignmentToVariableSniff.phpnu[ */ public function register(): array { return [ T_EQUAL, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $assignmentPointer */ public function process(File $phpcsFile, $assignmentPointer): void { $tokens = $phpcsFile->getTokens(); $variablePointer = TokenHelper::findPreviousEffective($phpcsFile, $assignmentPointer - 1); if ($tokens[$variablePointer]['code'] !== T_VARIABLE) { return; } $pointerBeforeVariable = TokenHelper::findPreviousEffective($phpcsFile, $variablePointer - 1); if (in_array($tokens[$pointerBeforeVariable]['code'], [T_OBJECT_OPERATOR, T_DOUBLE_COLON], true)) { return; } /** @var int $secondVariablePointer */ $secondVariablePointer = TokenHelper::findNextEffective($phpcsFile, $assignmentPointer + 1); if ($tokens[$secondVariablePointer]['code'] !== T_VARIABLE) { return; } if ($tokens[$variablePointer]['content'] !== $tokens[$secondVariablePointer]['content']) { return; } $pointerAfterSecondVariable = TokenHelper::findNextEffective($phpcsFile, $secondVariablePointer + 1); if ($tokens[$pointerAfterSecondVariable]['code'] !== T_EQUAL) { return; } $phpcsFile->addError( sprintf('Duplicate assignment to variable %s.', $tokens[$secondVariablePointer]['content']), $secondVariablePointer, self::CODE_DUPLICATE_ASSIGNMENT, ); } } PK41]On'%O%OOcoding-standard/SlevomatCodingStandard/Sniffs/Variables/UnusedVariableSniff.phpnu[ */ public function register(): array { return [ T_VARIABLE, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { if (!$this->isAssignment($phpcsFile, $pointer)) { return; } $tokens = $phpcsFile->getTokens(); $variableName = $tokens[$pointer]['content']; if (in_array($variableName, [ '$this', '$GLOBALS', '$_SERVER', '$_GET', '$_POST', '$_FILES', '$_COOKIE', '$_SESSION', '$_REQUEST', '$_ENV', ], true)) { return; } $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $pointer - 1); if (in_array($tokens[$previousPointer]['code'], [T_OBJECT_OPERATOR, T_DOUBLE_COLON], true)) { // Property return; } if (in_array($tokens[$previousPointer]['code'], Tokens::$castTokens, true)) { $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $previousPointer - 1); } if (in_array($tokens[$previousPointer]['code'], [ T_EQUAL, T_PLUS_EQUAL, T_MINUS_EQUAL, T_MUL_EQUAL, T_DIV_EQUAL, T_POW_EQUAL, T_MOD_EQUAL, T_AND_EQUAL, T_OR_EQUAL, T_XOR_EQUAL, T_SL_EQUAL, T_SR_EQUAL, T_CONCAT_EQUAL, T_YIELD, ], true)) { return; } if ($this->isUsedAsParameter($phpcsFile, $pointer)) { return; } if ($this->isUsedInForLoopCondition($phpcsFile, $pointer, $variableName)) { return; } if ($this->isDefinedInDoConditionAndUsedInLoop($phpcsFile, $pointer, $variableName)) { return; } if ($this->isUsedInLoopCycle($phpcsFile, $pointer, $variableName)) { return; } if ($this->isUsedAsKeyOrValueInArray($phpcsFile, $pointer)) { return; } if ($this->isValueInForeachAndErrorIsIgnored($phpcsFile, $pointer)) { return; } $scopeOwnerPointer = ScopeHelper::getRootPointer($phpcsFile, $pointer - 1); foreach (array_reverse($tokens[$pointer]['conditions'], true) as $conditionPointer => $conditionTokenCode) { if (in_array($conditionTokenCode, TokenHelper::FUNCTION_TOKEN_CODES, true)) { $scopeOwnerPointer = $conditionPointer; break; } } if (in_array($tokens[$scopeOwnerPointer]['code'], TokenHelper::FUNCTION_TOKEN_CODES, true)) { if ($this->isStaticOrGlobalVariable($phpcsFile, $scopeOwnerPointer, $variableName)) { return; } if ($this->isParameterPassedByReference($phpcsFile, $scopeOwnerPointer, $variableName)) { return; } if ( $tokens[$scopeOwnerPointer]['code'] === T_CLOSURE && $this->isInheritedVariablePassedByReference($phpcsFile, $scopeOwnerPointer, $variableName) ) { return; } } if ($this->isReference($phpcsFile, $scopeOwnerPointer, $pointer)) { return; } if (VariableHelper::isUsedInScopeAfterPointer($phpcsFile, $scopeOwnerPointer, $pointer, $pointer + 1)) { return; } if ($this->isPartOfStatementAndWithIncrementOrDecrementOperator($phpcsFile, $pointer)) { return; } $phpcsFile->addError( sprintf('Unused variable %s.', $variableName), $pointer, self::CODE_UNUSED_VARIABLE, ); } private function isAssignment(File $phpcsFile, int $variablePointer): bool { $tokens = $phpcsFile->getTokens(); $nextPointer = TokenHelper::findNextEffective($phpcsFile, $variablePointer + 1); if (in_array($tokens[$nextPointer]['code'], [ T_EQUAL, T_PLUS_EQUAL, T_MINUS_EQUAL, T_MUL_EQUAL, T_DIV_EQUAL, T_POW_EQUAL, T_MOD_EQUAL, T_AND_EQUAL, T_OR_EQUAL, T_XOR_EQUAL, T_SL_EQUAL, T_SR_EQUAL, T_CONCAT_EQUAL, ], true)) { if ($tokens[$nextPointer]['code'] === T_EQUAL) { if (PropertyHelper::isProperty($phpcsFile, $variablePointer)) { return false; } if (ParameterHelper::isParameter($phpcsFile, $variablePointer)) { return false; } } return true; } $actualPointer = $variablePointer; do { $parenthesisOpenerPointer = $this->findOpenerOfNestedParentheses($phpcsFile, $actualPointer); $parenthesisOwnerPointer = $this->findOwnerOfNestedParentheses($phpcsFile, $actualPointer); if ($parenthesisOpenerPointer === null) { break; } $actualPointer = $parenthesisOpenerPointer; } while ($parenthesisOwnerPointer === null && isset($tokens[$actualPointer]['nested_parenthesis'])); $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $variablePointer - 1); if ( in_array($tokens[$nextPointer]['code'], [T_INC, T_DEC], true) || in_array($tokens[$previousPointer]['code'], [T_INC, T_DEC], true) ) { if ($parenthesisOwnerPointer === null) { return true; } return !in_array($tokens[$parenthesisOwnerPointer]['code'], [T_FOR, T_WHILE, T_IF, T_ELSEIF], true); } if ($parenthesisOwnerPointer !== null && $tokens[$parenthesisOwnerPointer]['code'] === T_FOREACH) { $pointerBeforeVariable = TokenHelper::findPreviousEffective($phpcsFile, $variablePointer - 1); return in_array($tokens[$pointerBeforeVariable]['code'], [T_AS, T_DOUBLE_ARROW], true); } if ($parenthesisOpenerPointer !== null) { $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1); if ($tokens[$pointerBeforeParenthesisOpener]['code'] === T_LIST) { return true; } } $possibleShortListCloserPointer = TokenHelper::findNextExcluding( $phpcsFile, [...TokenHelper::INEFFECTIVE_TOKEN_CODES, T_VARIABLE, T_COMMA], $variablePointer + 1, ); if ($tokens[$possibleShortListCloserPointer]['code'] === T_CLOSE_SHORT_ARRAY) { return $tokens[TokenHelper::findNextEffective($phpcsFile, $possibleShortListCloserPointer + 1)]['code'] === T_EQUAL; } return false; } private function isUsedAsParameter(File $phpcsFile, int $variablePointer): bool { $parenthesisOpenerPointer = $this->findOpenerOfNestedParentheses($phpcsFile, $variablePointer); if ($parenthesisOpenerPointer === null) { return false; } if (!ScopeHelper::isInSameScope($phpcsFile, $parenthesisOpenerPointer, $variablePointer)) { return false; } return $phpcsFile->getTokens()[TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1)]['code'] === T_STRING; } private function isUsedInForLoopCondition(File $phpcsFile, int $variablePointer, string $variableName): bool { $tokens = $phpcsFile->getTokens(); $parenthesisOpenerPointer = $this->findOpenerOfNestedParentheses($phpcsFile, $variablePointer); if ($parenthesisOpenerPointer === null) { return false; } $parenthesisOwnerPointer = $this->findOwnerOfNestedParentheses($phpcsFile, $variablePointer); if ($parenthesisOwnerPointer === null) { return false; } if ($tokens[$parenthesisOwnerPointer]['code'] !== T_FOR) { return false; } for ($i = $parenthesisOpenerPointer + 1; $i < $tokens[$parenthesisOwnerPointer]['parenthesis_closer']; $i++) { if ($i === $variablePointer) { continue; } if ($tokens[$i]['code'] !== T_VARIABLE) { continue; } if ($tokens[$i]['content'] !== $variableName) { continue; } return true; } return false; } private function isDefinedInDoConditionAndUsedInLoop(File $phpcsFile, int $variablePointer, string $variableName): bool { $tokens = $phpcsFile->getTokens(); $parenthesisOpener = TokenHelper::findPrevious($phpcsFile, T_OPEN_PARENTHESIS, $variablePointer - 1); if ($parenthesisOpener === null || $tokens[$parenthesisOpener]['parenthesis_closer'] < $variablePointer) { return false; } $whilePointer = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpener - 1); if ($tokens[$whilePointer]['code'] !== T_WHILE) { return false; } $loopCloserPointer = TokenHelper::findPreviousEffective($phpcsFile, $whilePointer - 1); if ($tokens[$loopCloserPointer]['code'] !== T_CLOSE_CURLY_BRACKET) { return false; } $doPointer = TokenHelper::findPreviousEffective($phpcsFile, $tokens[$loopCloserPointer]['bracket_opener'] - 1); if ($tokens[$doPointer]['code'] !== T_DO) { return false; } return TokenHelper::findNextContent( $phpcsFile, T_VARIABLE, $variableName, $tokens[$loopCloserPointer]['bracket_opener'] + 1, $loopCloserPointer, ) !== null; } private function isUsedInLoopCycle(File $phpcsFile, int $variablePointer, string $variableName): bool { $tokens = $phpcsFile->getTokens(); $loopPointer = null; foreach (array_reverse($tokens[$variablePointer]['conditions'], true) as $conditionPointer => $conditionTokenCode) { if (in_array($conditionTokenCode, TokenHelper::FUNCTION_TOKEN_CODES, true)) { break; } if (!in_array($conditionTokenCode, [T_FOREACH, T_FOR, T_DO, T_WHILE], true)) { continue; } $loopPointer = $conditionPointer; $loopConditionPointer = $conditionTokenCode === T_DO ? TokenHelper::findNextEffective($phpcsFile, $tokens[$loopPointer]['scope_closer'] + 1) : $loopPointer; $variableUsedInLoopConditionPointer = TokenHelper::findNextContent( $phpcsFile, T_VARIABLE, $variableName, $tokens[$loopConditionPointer]['parenthesis_opener'] + 1, $tokens[$loopConditionPointer]['parenthesis_closer'], ); if ( $variableUsedInLoopConditionPointer === null || $variableUsedInLoopConditionPointer === $variablePointer ) { continue; } if ($conditionTokenCode !== T_FOREACH) { return true; } $pointerBeforeVariableUsedInLoopCondition = TokenHelper::findPreviousEffective( $phpcsFile, $variableUsedInLoopConditionPointer - 1, ); if ($tokens[$pointerBeforeVariableUsedInLoopCondition]['code'] === T_BITWISE_AND) { return true; } } if ($loopPointer === null) { return false; } for ($i = $tokens[$loopPointer]['scope_opener'] + 1; $i < $tokens[$loopPointer]['scope_closer']; $i++) { if ( in_array($tokens[$i]['code'], [T_DOUBLE_QUOTED_STRING, T_HEREDOC], true) && VariableHelper::isUsedInScopeInString($phpcsFile, $variableName, $i) ) { return true; } if ($tokens[$i]['code'] !== T_VARIABLE) { continue; } if ($tokens[$i]['content'] !== $variableName) { continue; } if (!$this->isAssignment($phpcsFile, $i)) { return true; } $nextPointer = TokenHelper::findNextEffective($phpcsFile, $i + 1); if (!in_array($tokens[$nextPointer]['code'], [ T_INC, T_DEC, T_PLUS_EQUAL, T_MINUS_EQUAL, T_MUL_EQUAL, T_DIV_EQUAL, T_POW_EQUAL, T_MOD_EQUAL, T_AND_EQUAL, T_OR_EQUAL, T_XOR_EQUAL, T_SL_EQUAL, T_SR_EQUAL, T_CONCAT_EQUAL, ], true)) { continue; } $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $i - 1); if ($tokens[$previousPointer]['code'] === T_INLINE_ELSE) { return true; } $parenthesisOwnerPointer = $this->findNestedParenthesisWithOwner($phpcsFile, $i); if ( $parenthesisOwnerPointer !== null && in_array($tokens[$parenthesisOwnerPointer]['code'], [T_IF, T_ELSEIF], true) ) { return true; } } return false; } private function isUsedAsKeyOrValueInArray(File $phpcsFile, int $variablePointer): bool { $tokens = $phpcsFile->getTokens(); $squareBracketOpenerPointer = TokenHelper::findPrevious($phpcsFile, T_OPEN_SQUARE_BRACKET, $variablePointer - 1); if ( $squareBracketOpenerPointer !== null && $tokens[$squareBracketOpenerPointer]['bracket_closer'] > $variablePointer ) { return true; } $arrayOpenerPointer = TokenHelper::findPrevious($phpcsFile, T_OPEN_SHORT_ARRAY, $variablePointer - 1); if ($arrayOpenerPointer === null) { return false; } $arrayCloserPointer = $tokens[$arrayOpenerPointer]['bracket_closer']; if ($arrayCloserPointer < $variablePointer) { return false; } $pointerAfterArrayCloser = TokenHelper::findNextEffective($phpcsFile, $arrayCloserPointer + 1); if ($tokens[$pointerAfterArrayCloser]['code'] === T_EQUAL) { return false; } $pointerBeforeVariable = TokenHelper::findPreviousEffective($phpcsFile, $variablePointer - 1); if (in_array($tokens[$pointerBeforeVariable]['code'], [T_INC, T_DEC], true)) { $pointerBeforeVariable = TokenHelper::findPreviousEffective($phpcsFile, $pointerBeforeVariable - 1); } return in_array($tokens[$pointerBeforeVariable]['code'], [T_OPEN_SHORT_ARRAY, T_COMMA, T_DOUBLE_ARROW], true); } private function isValueInForeachAndErrorIsIgnored(File $phpcsFile, int $variablePointer): bool { $tokens = $phpcsFile->getTokens(); $parenthesisOwnerPointer = $this->findNestedParenthesisWithOwner($phpcsFile, $variablePointer); $isInForeach = $parenthesisOwnerPointer !== null && $tokens[$parenthesisOwnerPointer]['code'] === T_FOREACH; if (!$isInForeach) { return false; } $pointerAfterVariable = TokenHelper::findNextEffective($phpcsFile, $variablePointer + 1); if ($pointerAfterVariable !== null && $tokens[$pointerAfterVariable]['code'] === T_DOUBLE_ARROW) { return false; } return $this->ignoreUnusedValuesWhenOnlyKeysAreUsedInForeach; } private function isStaticOrGlobalVariable(File $phpcsFile, int $functionPointer, string $variableName): bool { $tokens = $phpcsFile->getTokens(); for ($i = $tokens[$functionPointer]['scope_opener'] + 1; $i < $tokens[$functionPointer]['scope_closer']; $i++) { if ($tokens[$i]['code'] !== T_VARIABLE) { continue; } if ($tokens[$i]['content'] !== $variableName) { continue; } $pointerBeforeParameter = TokenHelper::findPreviousEffective($phpcsFile, $i - 1); if (in_array($tokens[$pointerBeforeParameter]['code'], [T_STATIC, T_GLOBAL], true)) { return true; } } return false; } private function isParameterPassedByReference(File $phpcsFile, int $functionPointer, string $variableName): bool { $tokens = $phpcsFile->getTokens(); for ($i = $tokens[$functionPointer]['parenthesis_opener'] + 1; $i < $tokens[$functionPointer]['parenthesis_closer']; $i++) { if ($tokens[$i]['code'] !== T_VARIABLE) { continue; } if ($tokens[$i]['content'] !== $variableName) { continue; } $pointerBeforeParameter = TokenHelper::findPreviousEffective($phpcsFile, $i - 1); if ($tokens[$pointerBeforeParameter]['code'] === T_BITWISE_AND) { return true; } } return false; } private function isInheritedVariablePassedByReference(File $phpcsFile, int $functionPointer, string $variableName): bool { $tokens = $phpcsFile->getTokens(); $usePointer = TokenHelper::findNextEffective($phpcsFile, $tokens[$functionPointer]['parenthesis_closer'] + 1); if ($tokens[$usePointer]['code'] !== T_USE) { return false; } $useParenthesisOpener = TokenHelper::findNextEffective($phpcsFile, $usePointer + 1); for ($i = $useParenthesisOpener + 1; $i < $tokens[$useParenthesisOpener]['parenthesis_closer']; $i++) { if ($tokens[$i]['code'] !== T_VARIABLE) { continue; } if ($tokens[$i]['content'] !== $variableName) { continue; } $pointerBeforeInheritedVariable = TokenHelper::findPreviousEffective($phpcsFile, $i - 1); if ($tokens[$pointerBeforeInheritedVariable]['code'] === T_BITWISE_AND) { return true; } } return false; } private function isReference(File $phpcsFile, int $scopeOwnerPointer, int $variablePointer): bool { $tokens = $phpcsFile->getTokens(); $scopeOpenerPointer = $tokens[$scopeOwnerPointer]['code'] === T_OPEN_TAG ? $scopeOwnerPointer : $tokens[$scopeOwnerPointer]['scope_opener']; for ($i = $scopeOpenerPointer + 1; $i < $variablePointer; $i++) { if ($tokens[$i]['code'] !== T_VARIABLE) { continue; } if ($tokens[$i]['content'] !== $tokens[$variablePointer]['content']) { continue; } $assignmentPointer = TokenHelper::findNextEffective($phpcsFile, $i + 1); if ($tokens[$assignmentPointer]['code'] !== T_EQUAL) { continue; } $referencePointer = TokenHelper::findNextEffective($phpcsFile, $assignmentPointer + 1); if ($tokens[$referencePointer]['code'] === T_BITWISE_AND) { return true; } } return false; } private function isPartOfStatementAndWithIncrementOrDecrementOperator(File $phpcsFile, int $variablePointer): bool { $tokens = $phpcsFile->getTokens(); $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $variablePointer - 1); $nextPointer = TokenHelper::findNextEffective($phpcsFile, $variablePointer + 1); if (in_array($tokens[$previousPointer]['code'], [T_DEC, T_INC], true)) { $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $previousPointer - 1); } elseif ($nextPointer !== null && in_array($tokens[$nextPointer]['code'], [T_DEC, T_INC], true)) { // Nothing } else { return false; } if ($tokens[$previousPointer]['code'] === T_OPEN_PARENTHESIS) { $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $previousPointer - 1); } return in_array( $tokens[$previousPointer]['code'], array_merge( [T_STRING_CONCAT, T_ECHO, T_RETURN, T_EXIT, T_PRINT, T_COMMA, T_EMPTY, T_EVAL, T_YIELD], Tokens::$operators, Tokens::$assignmentTokens, Tokens::$booleanOperators, Tokens::$castTokens, ), true, ); } private function findNestedParenthesisWithOwner(File $phpcsFile, int $pointer): ?int { $tokens = $phpcsFile->getTokens(); if (!array_key_exists('nested_parenthesis', $tokens[$pointer])) { return null; } foreach (array_reverse(array_keys($tokens[$pointer]['nested_parenthesis'])) as $nestedParenthesisOpener) { if (array_key_exists('parenthesis_owner', $tokens[$nestedParenthesisOpener])) { return $tokens[$nestedParenthesisOpener]['parenthesis_owner']; } } return null; } private function findOpenerOfNestedParentheses(File $phpcsFile, int $pointer): ?int { $tokens = $phpcsFile->getTokens(); if (!array_key_exists('nested_parenthesis', $tokens[$pointer])) { return null; } return array_reverse(array_keys($tokens[$pointer]['nested_parenthesis']))[0]; } private function findOwnerOfNestedParentheses(File $phpcsFile, int $pointer): ?int { $tokens = $phpcsFile->getTokens(); $parenthesisOpenerPointer = $this->findOpenerOfNestedParentheses($phpcsFile, $pointer); if ($parenthesisOpenerPointer === null) { return null; } return array_key_exists('parenthesis_owner', $tokens[$parenthesisOpenerPointer]) ? $tokens[$parenthesisOpenerPointer]['parenthesis_owner'] : null; } } PK41]7-.-.Pcoding-standard/SlevomatCodingStandard/Sniffs/Variables/UselessVariableSniff.phpnu[ */ public function register(): array { return [ T_RETURN, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $returnPointer */ public function process(File $phpcsFile, $returnPointer): void { $tokens = $phpcsFile->getTokens(); /** @var int $variablePointer */ $variablePointer = TokenHelper::findNextEffective($phpcsFile, $returnPointer + 1); if ($tokens[$variablePointer]['code'] !== T_VARIABLE) { return; } $returnSemicolonPointer = TokenHelper::findNextEffective($phpcsFile, $variablePointer + 1); if ($tokens[$returnSemicolonPointer]['code'] !== T_SEMICOLON) { return; } $variableName = $tokens[$variablePointer]['content']; $functionPointer = $this->findFunctionPointer($phpcsFile, $variablePointer); if ($functionPointer !== null) { if ($this->isReturnedByReference($phpcsFile, $functionPointer)) { return; } if ($this->isStaticVariable($phpcsFile, $functionPointer, $variablePointer, $variableName)) { return; } if ($this->isFunctionParameter($phpcsFile, $functionPointer, $variableName)) { return; } } $previousVariablePointer = $this->findPreviousVariablePointer($phpcsFile, $returnPointer, $variableName); if ($previousVariablePointer === null) { return; } if (!$this->isAssignmentToVariable($phpcsFile, $previousVariablePointer)) { return; } if ($this->isAssignedInControlStructure($phpcsFile, $previousVariablePointer)) { return; } if ($this->isAssignedInFunctionCall($phpcsFile, $previousVariablePointer)) { return; } if ($this->hasVariableVarAnnotation($phpcsFile, $previousVariablePointer)) { return; } if ($this->hasAnotherAssignmentBefore($phpcsFile, $previousVariablePointer, $variableName)) { return; } if (!$this->areBothPointersNearby($phpcsFile, $previousVariablePointer, $returnPointer)) { return; } $errorParameters = [ sprintf('Useless variable %s.', $variableName), $previousVariablePointer, self::CODE_USELESS_VARIABLE, ]; $pointerBeforePreviousVariable = TokenHelper::findPreviousEffective($phpcsFile, $previousVariablePointer - 1); if ( !in_array($tokens[$pointerBeforePreviousVariable]['code'], [T_SEMICOLON, T_OPEN_CURLY_BRACKET, T_CLOSE_CURLY_BRACKET], true) && TokenHelper::findNextEffective($phpcsFile, $returnSemicolonPointer + 1) !== null ) { $phpcsFile->addError(...$errorParameters); return; } $fix = $phpcsFile->addFixableError(...$errorParameters); if (!$fix) { return; } /** @var int $assignmentPointer */ $assignmentPointer = TokenHelper::findNextEffective($phpcsFile, $previousVariablePointer + 1); $assignmentFixerMapping = [ T_PLUS_EQUAL => '+', T_MINUS_EQUAL => '-', T_MUL_EQUAL => '*', T_DIV_EQUAL => '/', T_POW_EQUAL => '**', T_MOD_EQUAL => '%', T_AND_EQUAL => '&', T_OR_EQUAL => '|', T_XOR_EQUAL => '^', T_SL_EQUAL => '<<', T_SR_EQUAL => '>>', T_CONCAT_EQUAL => '.', ]; $previousVariableSemicolonPointer = $this->findSemicolon($phpcsFile, $previousVariablePointer); $phpcsFile->fixer->beginChangeset(); if ($tokens[$assignmentPointer]['code'] === T_EQUAL) { FixerHelper::change($phpcsFile, $previousVariablePointer, $assignmentPointer, 'return'); } else { FixerHelper::addBefore($phpcsFile, $previousVariablePointer, 'return '); FixerHelper::replace($phpcsFile, $assignmentPointer, $assignmentFixerMapping[$tokens[$assignmentPointer]['code']]); } FixerHelper::removeBetweenIncluding($phpcsFile, $previousVariableSemicolonPointer + 1, $returnSemicolonPointer); $phpcsFile->fixer->endChangeset(); } private function findPreviousVariablePointer(File $phpcsFile, int $pointer, string $variableName): ?int { $tokens = $phpcsFile->getTokens(); for ($i = $pointer - 1; $i >= 0; $i--) { if ( in_array($tokens[$i]['code'], TokenHelper::FUNCTION_TOKEN_CODES, true) && ScopeHelper::isInSameScope($phpcsFile, $tokens[$i]['scope_opener'] + 1, $pointer) ) { return null; } if ($tokens[$i]['code'] !== T_VARIABLE) { continue; } if ($tokens[$i]['content'] !== $variableName) { continue; } $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $i - 1); if ($tokens[$previousPointer]['code'] === T_DOUBLE_COLON) { continue; } if (!ScopeHelper::isInSameScope($phpcsFile, $i, $pointer)) { continue; } return $i; } return null; } private function isAssignedInControlStructure(File $phpcsFile, int $pointer): bool { $controlStructure = TokenHelper::findPrevious($phpcsFile, [ T_WHILE, T_FOR, T_FOREACH, T_SWITCH, T_IF, T_ELSEIF, ], $pointer - 1); if ($controlStructure === null) { return false; } $tokens = $phpcsFile->getTokens(); return $tokens[$controlStructure]['parenthesis_opener'] < $pointer && $pointer < $tokens[$controlStructure]['parenthesis_closer']; } private function isAssignedInFunctionCall(File $phpcsFile, int $pointer): bool { $possibleFunctionNamePointer = TokenHelper::findPrevious($phpcsFile, T_STRING, $pointer - 1); if ($possibleFunctionNamePointer === null) { return false; } $tokens = $phpcsFile->getTokens(); $parenthesisOpenerPointer = TokenHelper::findNextEffective($phpcsFile, $possibleFunctionNamePointer + 1); if ($tokens[$parenthesisOpenerPointer]['code'] !== T_OPEN_PARENTHESIS) { return false; } return $parenthesisOpenerPointer < $pointer && $pointer < $tokens[$parenthesisOpenerPointer]['parenthesis_closer']; } private function isAssignmentToVariable(File $phpcsFile, int $pointer): bool { $assignmentPointer = TokenHelper::findNextEffective($phpcsFile, $pointer + 1); return in_array($phpcsFile->getTokens()[$assignmentPointer]['code'], [ T_EQUAL, T_PLUS_EQUAL, T_MINUS_EQUAL, T_MUL_EQUAL, T_DIV_EQUAL, T_POW_EQUAL, T_MOD_EQUAL, T_AND_EQUAL, T_OR_EQUAL, T_XOR_EQUAL, T_SL_EQUAL, T_SR_EQUAL, T_CONCAT_EQUAL, ], true); } private function findFunctionPointer(File $phpcsFile, int $pointer): ?int { $tokens = $phpcsFile->getTokens(); foreach (array_reverse($tokens[$pointer]['conditions'], true) as $conditionPointer => $conditionTokenCode) { if (in_array($conditionTokenCode, TokenHelper::FUNCTION_TOKEN_CODES, true)) { return $conditionPointer; } } return null; } private function isStaticVariable(File $phpcsFile, int $functionPointer, int $variablePointer, string $variableName): bool { $tokens = $phpcsFile->getTokens(); for ($i = $tokens[$functionPointer]['scope_opener'] + 1; $i < $variablePointer; $i++) { if ($tokens[$i]['code'] !== T_VARIABLE) { continue; } if ($tokens[$i]['content'] !== $variableName) { continue; } $pointerBeforeParameter = TokenHelper::findPreviousEffective($phpcsFile, $i - 1); if ($tokens[$pointerBeforeParameter]['code'] === T_STATIC) { return true; } } return false; } private function isFunctionParameter(File $phpcsFile, int $functionPointer, string $variableName): bool { $tokens = $phpcsFile->getTokens(); for ($i = $tokens[$functionPointer]['parenthesis_opener'] + 1; $i < $tokens[$functionPointer]['parenthesis_closer']; $i++) { if ($tokens[$i]['code'] !== T_VARIABLE) { continue; } if ($tokens[$i]['content'] !== $variableName) { continue; } return true; } return false; } private function isReturnedByReference(File $phpcsFile, int $functionPointer): bool { $tokens = $phpcsFile->getTokens(); $referencePointer = TokenHelper::findNextEffective($phpcsFile, $functionPointer + 1); return $tokens[$referencePointer]['code'] === T_BITWISE_AND; } private function hasVariableVarAnnotation(File $phpcsFile, int $variablePointer): bool { $tokens = $phpcsFile->getTokens(); $pointerBeforeVariable = TokenHelper::findPreviousNonWhitespace($phpcsFile, $variablePointer - 1); if ($tokens[$pointerBeforeVariable]['code'] !== T_DOC_COMMENT_CLOSE_TAG) { return false; } $docCommentContent = TokenHelper::getContent($phpcsFile, $tokens[$pointerBeforeVariable]['comment_opener'], $pointerBeforeVariable); return preg_match( '~@(?:(?:phpstan|psalm)-)?var\\s+.+\\s+' . preg_quote($tokens[$variablePointer]['content'], '~') . '(?:\\s|$)~', $docCommentContent, ) !== 0; } private function hasAnotherAssignmentBefore(File $phpcsFile, int $variablePointer, string $variableName): bool { $previousVariablePointer = $this->findPreviousVariablePointer($phpcsFile, $variablePointer, $variableName); if ($previousVariablePointer === null) { return false; } if (!$this->isAssignmentToVariable($phpcsFile, $previousVariablePointer)) { return false; } return $this->areBothVariablesNearby($phpcsFile, $previousVariablePointer, $variablePointer); } private function areBothPointersNearby(File $phpcsFile, int $firstPointer, int $secondPointer): bool { $firstVariableSemicolonPointer = $this->findSemicolon($phpcsFile, $firstPointer); $pointerAfterFirstVariableSemicolon = TokenHelper::findNextEffective($phpcsFile, $firstVariableSemicolonPointer + 1); return $pointerAfterFirstVariableSemicolon === $secondPointer; } private function areBothVariablesNearby(File $phpcsFile, int $firstVariablePointer, int $secondVariablePointer): bool { if ($this->areBothPointersNearby($phpcsFile, $firstVariablePointer, $secondVariablePointer)) { return true; } $tokens = $phpcsFile->getTokens(); $lastConditionPointer = array_reverse(array_keys($tokens[$firstVariablePointer]['conditions']))[0]; $lastConditionScopeCloserPointer = $tokens[$lastConditionPointer]['scope_closer']; if ($tokens[$lastConditionPointer]['code'] === T_DO) { $lastConditionScopeCloserPointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $lastConditionScopeCloserPointer + 1); } return TokenHelper::findNextEffective($phpcsFile, $lastConditionScopeCloserPointer + 1) === $secondVariablePointer; } private function findSemicolon(File $phpcsFile, int $pointer): int { $tokens = $phpcsFile->getTokens(); $semicolonPointer = null; for ($i = $pointer + 1; $i < count($tokens) - 1; $i++) { if ($tokens[$i]['code'] !== T_SEMICOLON) { continue; } if (!ScopeHelper::isInSameScope($phpcsFile, $pointer, $i)) { continue; } $semicolonPointer = $i; break; } /** @var int $semicolonPointer */ $semicolonPointer = $semicolonPointer; return $semicolonPointer; } } PK41] ((Ycoding-standard/SlevomatCodingStandard/Sniffs/Variables/DisallowVariableVariableSniff.phpnu[ */ public function register(): array { return [ T_DOLLAR, T_DOLLAR_OPEN_CURLY_BRACES, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { $phpcsFile->addError('Use of variable variable is disallowed.', $pointer, self::CODE_DISALLOWED_VARIABLE_VARIABLE); } } PK41]Q![coding-standard/SlevomatCodingStandard/Sniffs/Commenting/UselessFunctionDocCommentSniff.phpnu[ */ public array $traversableTypeHints = []; /** @var list|null */ private ?array $normalizedTraversableTypeHints = null; /** * @return array */ public function register(): array { return [ T_FUNCTION, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $functionPointer */ public function process(File $phpcsFile, $functionPointer): void { if (!DocCommentHelper::hasDocComment($phpcsFile, $functionPointer)) { return; } if (DocCommentHelper::hasInheritdocAnnotation($phpcsFile, $functionPointer)) { return; } if (DocCommentHelper::hasDocCommentDescription($phpcsFile, $functionPointer)) { return; } $returnTypeHint = FunctionHelper::findReturnTypeHint($phpcsFile, $functionPointer); $returnAnnotation = FunctionHelper::findReturnAnnotation($phpcsFile, $functionPointer); if ( $returnAnnotation !== null && !AnnotationHelper::isAnnotationUseless( $phpcsFile, $functionPointer, $returnTypeHint, $returnAnnotation, $this->getTraversableTypeHints(), ) ) { return; } $parameterTypeHints = FunctionHelper::getParametersTypeHints($phpcsFile, $functionPointer); $parametersAnnotations = FunctionHelper::getValidParametersAnnotations($phpcsFile, $functionPointer); foreach ($parametersAnnotations as $parameterName => $parameterAnnotation) { if (!array_key_exists($parameterName, $parameterTypeHints)) { return; } if (!AnnotationHelper::isAnnotationUseless( $phpcsFile, $functionPointer, $parameterTypeHints[$parameterName], $parameterAnnotation, $this->getTraversableTypeHints(), )) { return; } } foreach (AnnotationHelper::getAnnotations($phpcsFile, $functionPointer) as $annotation) { if (!in_array($annotation->getName(), ['@param', '@return'], true)) { return; } } $fix = $phpcsFile->addFixableError( sprintf( '%s %s() does not need documentation comment.', FunctionHelper::getTypeLabel($phpcsFile, $functionPointer), FunctionHelper::getFullyQualifiedName($phpcsFile, $functionPointer), ), $functionPointer, self::CODE_USELESS_DOC_COMMENT, ); if (!$fix) { return; } /** @var int $docCommentOpenPointer */ $docCommentOpenPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $functionPointer); $docCommentClosePointer = $phpcsFile->getTokens()[$docCommentOpenPointer]['comment_closer']; $changeStart = $docCommentOpenPointer; /** @var int $changeEnd */ $changeEnd = TokenHelper::findNextEffective($phpcsFile, $docCommentClosePointer + 1) - 1; $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $changeStart, $changeEnd); $phpcsFile->fixer->endChangeset(); } /** * @return list */ private function getTraversableTypeHints(): array { $this->normalizedTraversableTypeHints ??= array_map( static fn (string $typeHint): string => NamespaceHelper::isFullyQualifiedName($typeHint) ? $typeHint : sprintf('%s%s', NamespaceHelper::NAMESPACE_SEPARATOR, $typeHint), SniffSettingsHelper::normalizeArray($this->traversableTypeHints), ); return $this->normalizedTraversableTypeHints; } } PK41]4Zcoding-standard/SlevomatCodingStandard/Sniffs/Commenting/DisallowCommentAfterCodeSniff.phpnu[ */ public function register(): array { return [...TokenHelper::INLINE_COMMENT_TOKEN_CODES, T_DOC_COMMENT_OPEN_TAG]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $commentPointer */ public function process(File $phpcsFile, $commentPointer): void { $tokens = $phpcsFile->getTokens(); if ($tokens[$commentPointer]['column'] === 1) { return; } $firstNonWhitespacePointerOnLine = TokenHelper::findFirstNonWhitespaceOnLine($phpcsFile, $commentPointer); if ($firstNonWhitespacePointerOnLine === $commentPointer) { return; } if ( $tokens[$firstNonWhitespacePointerOnLine]['code'] === T_DOC_COMMENT_OPEN_TAG && $tokens[$firstNonWhitespacePointerOnLine]['comment_closer'] > $commentPointer ) { return; } $commentEndPointer = CommentHelper::getCommentEndPointer($phpcsFile, $commentPointer); $nextNonWhitespacePointer = TokenHelper::findNextNonWhitespace($phpcsFile, $commentEndPointer + 1); if ( $nextNonWhitespacePointer !== null && $commentEndPointer !== null && $tokens[$nextNonWhitespacePointer]['line'] === $tokens[$commentEndPointer]['line'] ) { return; } $fix = $phpcsFile->addFixableError('Comment after code is disallowed.', $commentPointer, self::CODE_DISALLOWED_COMMENT_AFTER_CODE); if (!$fix) { return; } $commentContent = TokenHelper::getContent($phpcsFile, $commentPointer, $commentEndPointer); $commentHasNewLineAtTheEnd = substr($commentContent, -strlen($phpcsFile->eolChar)) === $phpcsFile->eolChar; if (!$commentHasNewLineAtTheEnd) { $commentContent .= $phpcsFile->eolChar; } $firstNonWhiteSpacePointerBeforeComment = TokenHelper::findPreviousNonWhitespace($phpcsFile, $commentPointer - 1); $newLineAfterComment = $commentHasNewLineAtTheEnd ? $commentEndPointer : TokenHelper::findLastTokenOnLine($phpcsFile, $commentEndPointer); $indentation = IndentationHelper::getIndentation($phpcsFile, $firstNonWhitespacePointerOnLine); $firstPointerOnLine = TokenHelper::findFirstTokenOnLine($phpcsFile, $firstNonWhitespacePointerOnLine); $phpcsFile->fixer->beginChangeset(); if ( $tokens[$firstNonWhiteSpacePointerBeforeComment]['code'] === T_OPEN_CURLY_BRACKET && array_key_exists('scope_condition', $tokens[$firstNonWhiteSpacePointerBeforeComment]) && in_array( $tokens[$tokens[$firstNonWhiteSpacePointerBeforeComment]['scope_condition']]['code'], [T_ELSEIF, T_ELSE, T_CLOSURE], true, ) ) { FixerHelper::add( $phpcsFile, $firstNonWhiteSpacePointerBeforeComment, $phpcsFile->eolChar . IndentationHelper::addIndentation($phpcsFile, $indentation) . $commentContent, ); } elseif ($tokens[$firstNonWhitespacePointerOnLine]['code'] === T_CLOSE_CURLY_BRACKET) { FixerHelper::add($phpcsFile, $firstNonWhiteSpacePointerBeforeComment, $phpcsFile->eolChar . $indentation . $commentContent); } elseif (isset(Tokens::$stringTokens[$tokens[$firstPointerOnLine]['code']])) { $prevNonStringToken = TokenHelper::findPreviousExcluding( $phpcsFile, [T_WHITESPACE] + Tokens::$stringTokens, $firstPointerOnLine - 1, ); $firstTokenOnNonStringTokenLine = TokenHelper::findFirstTokenOnLine($phpcsFile, $prevNonStringToken); $firstNonWhitespacePointerOnNonStringTokenLine = TokenHelper::findFirstNonWhitespaceOnLine($phpcsFile, $prevNonStringToken); $prevLineIndentation = IndentationHelper::getIndentation($phpcsFile, $firstNonWhitespacePointerOnNonStringTokenLine); FixerHelper::addBefore($phpcsFile, $firstTokenOnNonStringTokenLine, $prevLineIndentation . $commentContent); $phpcsFile->fixer->addNewline($firstNonWhiteSpacePointerBeforeComment); } else { FixerHelper::addBefore($phpcsFile, $firstPointerOnLine, $indentation . $commentContent); $phpcsFile->fixer->addNewline($firstNonWhiteSpacePointerBeforeComment); } FixerHelper::removeBetweenIncluding($phpcsFile, $firstNonWhiteSpacePointerBeforeComment + 1, $newLineAfterComment); $phpcsFile->fixer->endChangeset(); } } PK41]prř ccoding-standard/SlevomatCodingStandard/Sniffs/Commenting/DisallowOneLinePropertyDocCommentSniff.phpnu[ */ public function register(): array { return [T_VARIABLE]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $propertyPointer */ public function process(File $phpcsFile, $propertyPointer): void { $tokens = $phpcsFile->getTokens(); // Not a property if (!PropertyHelper::isProperty($phpcsFile, $propertyPointer)) { return; } // Only validate properties with comment if (!DocCommentHelper::hasDocComment($phpcsFile, $propertyPointer)) { return; } /** @var int $docCommentStartPointer */ $docCommentStartPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $propertyPointer); $docCommentEndPointer = $tokens[$docCommentStartPointer]['comment_closer']; $lineDifference = $tokens[$docCommentEndPointer]['line'] - $tokens[$docCommentStartPointer]['line']; // Already multi-line if ($lineDifference !== 0) { return; } $fix = $phpcsFile->addFixableError( sprintf( 'Found one-line comment for property %s, use multi-line comment instead.', PropertyHelper::getFullyQualifiedName($phpcsFile, $propertyPointer), ), $docCommentStartPointer, self::CODE_ONE_LINE_PROPERTY_COMMENT, ); if (!$fix) { return; } $commentWhitespacePointer = TokenHelper::findPrevious($phpcsFile, [T_WHITESPACE], $docCommentStartPointer); $indent = ($commentWhitespacePointer !== null ? $tokens[$commentWhitespacePointer]['content'] : '') . ' '; $phpcsFile->fixer->beginChangeset(); $phpcsFile->fixer->addNewline($docCommentStartPointer); FixerHelper::add($phpcsFile, $docCommentStartPointer, $indent); FixerHelper::add($phpcsFile, $docCommentStartPointer, '*'); if ($docCommentEndPointer - 1 !== $docCommentStartPointer) { FixerHelper::replace( $phpcsFile, $docCommentEndPointer - 1, rtrim($phpcsFile->fixer->getTokenContent($docCommentEndPointer - 1), ' '), ); } FixerHelper::addBefore($phpcsFile, $docCommentEndPointer, $indent); $phpcsFile->fixer->addNewlineBefore($docCommentEndPointer); $phpcsFile->fixer->endChangeset(); } } PK41]72ZddScoding-standard/SlevomatCodingStandard/Sniffs/Commenting/DocCommentSpacingSniff.phpnu[ */ public array $annotationsGroups = []; /** @var array>|null */ private ?array $normalizedAnnotationsGroups = null; /** * @return array */ public function register(): array { return [ T_DOC_COMMENT_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $docCommentOpenerPointer */ public function process(File $phpcsFile, $docCommentOpenerPointer): void { $this->linesCountBeforeFirstContent = SniffSettingsHelper::normalizeInteger($this->linesCountBeforeFirstContent); $this->linesCountBetweenDescriptionAndAnnotations = SniffSettingsHelper::normalizeInteger( $this->linesCountBetweenDescriptionAndAnnotations, ); $this->linesCountBetweenDifferentAnnotationsTypes = SniffSettingsHelper::normalizeInteger( $this->linesCountBetweenDifferentAnnotationsTypes, ); $this->linesCountBetweenAnnotationsGroups = SniffSettingsHelper::normalizeInteger($this->linesCountBetweenAnnotationsGroups); $this->linesCountAfterLastContent = SniffSettingsHelper::normalizeInteger($this->linesCountAfterLastContent); if (DocCommentHelper::isInline($phpcsFile, $docCommentOpenerPointer)) { return; } $tokens = $phpcsFile->getTokens(); if (TokenHelper::findNextExcluding( $phpcsFile, [T_DOC_COMMENT_WHITESPACE, T_DOC_COMMENT_STAR], $docCommentOpenerPointer + 1, $tokens[$docCommentOpenerPointer]['comment_closer'], ) === null) { return; } $parsedDocComment = DocCommentHelper::parseDocComment($phpcsFile, $docCommentOpenerPointer); if ($parsedDocComment === null) { return; } $firstContentStartPointer = $parsedDocComment->getNodeStartPointer($phpcsFile, $parsedDocComment->getNode()->children[0]); $firstContentEndPointer = $parsedDocComment->getNodeEndPointer( $phpcsFile, $parsedDocComment->getNode()->children[0], $firstContentStartPointer, ); $annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenerPointer); usort($annotations, static fn (Annotation $a, Annotation $b): int => $a->getStartPointer() <=> $b->getStartPointer()); $annotationsCount = count($annotations); $firstAnnotationPointer = $annotationsCount > 0 ? $annotations[0]->getStartPointer() : null; /** @var int $lastContentEndPointer */ $lastContentEndPointer = $annotationsCount > 0 ? $annotations[$annotationsCount - 1]->getEndPointer() : $firstContentEndPointer; $this->checkLinesBeforeFirstContent($phpcsFile, $docCommentOpenerPointer, $firstContentStartPointer); $this->checkLinesBetweenDescriptionAndFirstAnnotation( $phpcsFile, $docCommentOpenerPointer, $firstContentStartPointer, $firstContentEndPointer, $firstAnnotationPointer, ); if (count($annotations) > 1) { if (count($this->getAnnotationsGroups()) === 0) { $this->checkLinesBetweenDifferentAnnotationsTypes($phpcsFile, $docCommentOpenerPointer, $annotations); } else { $this->checkAnnotationsGroups($phpcsFile, $docCommentOpenerPointer, $annotations); } } $this->checkLinesAfterLastContent( $phpcsFile, $docCommentOpenerPointer, $tokens[$docCommentOpenerPointer]['comment_closer'], $lastContentEndPointer, ); } private function checkLinesBeforeFirstContent(File $phpcsFile, int $docCommentOpenerPointer, int $firstContentStartPointer): void { $tokens = $phpcsFile->getTokens(); $whitespaceBeforeFirstContent = substr($tokens[$docCommentOpenerPointer]['content'], 0, strlen('/**')); $whitespaceBeforeFirstContent .= TokenHelper::getContent($phpcsFile, $docCommentOpenerPointer + 1, $firstContentStartPointer - 1); $linesCountBeforeFirstContent = max(substr_count($whitespaceBeforeFirstContent, $phpcsFile->eolChar) - 1, 0); if ($linesCountBeforeFirstContent === $this->linesCountBeforeFirstContent) { return; } $fix = $phpcsFile->addFixableError( sprintf( 'Expected %d line%s before first content, found %d.', $this->linesCountBeforeFirstContent, $this->linesCountBeforeFirstContent === 1 ? '' : 's', $linesCountBeforeFirstContent, ), $firstContentStartPointer, self::CODE_INCORRECT_LINES_COUNT_BEFORE_FIRST_CONTENT, ); if (!$fix) { return; } $indentation = IndentationHelper::getIndentation($phpcsFile, $docCommentOpenerPointer); $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $docCommentOpenerPointer, $firstContentStartPointer - 1, '/**' . $phpcsFile->eolChar); for ($i = 1; $i <= $this->linesCountBeforeFirstContent; $i++) { FixerHelper::add($phpcsFile, $docCommentOpenerPointer, sprintf('%s *%s', $indentation, $phpcsFile->eolChar)); } FixerHelper::addBefore($phpcsFile, $firstContentStartPointer, $indentation . ' * '); $phpcsFile->fixer->endChangeset(); } private function checkLinesBetweenDescriptionAndFirstAnnotation( File $phpcsFile, int $docCommentOpenerPointer, int $firstContentStartPointer, int $firstContentEndPointer, ?int $firstAnnotationPointer ): void { if ($firstAnnotationPointer === null) { return; } if ($firstContentStartPointer === $firstAnnotationPointer) { return; } $tokens = $phpcsFile->getTokens(); preg_match('~(\\s+)$~', $tokens[$firstContentEndPointer]['content'], $matches); $whitespaceBetweenDescriptionAndFirstAnnotation = $matches[1] ?? ''; $whitespaceBetweenDescriptionAndFirstAnnotation .= TokenHelper::getContent( $phpcsFile, $firstContentEndPointer + 1, $firstAnnotationPointer - 1, ); $linesCountBetweenDescriptionAndAnnotations = max( substr_count($whitespaceBetweenDescriptionAndFirstAnnotation, $phpcsFile->eolChar) - 1, 0, ); if ($linesCountBetweenDescriptionAndAnnotations === $this->linesCountBetweenDescriptionAndAnnotations) { return; } $fix = $phpcsFile->addFixableError( sprintf( 'Expected %d line%s between description and annotations, found %d.', $this->linesCountBetweenDescriptionAndAnnotations, $this->linesCountBetweenDescriptionAndAnnotations === 1 ? '' : 's', $linesCountBetweenDescriptionAndAnnotations, ), $firstAnnotationPointer, self::CODE_INCORRECT_LINES_COUNT_BETWEEN_DESCRIPTION_AND_ANNOTATIONS, ); if (!$fix) { return; } $indentation = IndentationHelper::getIndentation($phpcsFile, $docCommentOpenerPointer); $phpcsFile->fixer->beginChangeset(); $phpcsFile->fixer->addNewline($firstContentEndPointer); FixerHelper::removeBetween($phpcsFile, $firstContentEndPointer, $firstAnnotationPointer); for ($i = 1; $i <= $this->linesCountBetweenDescriptionAndAnnotations; $i++) { FixerHelper::add($phpcsFile, $firstContentEndPointer, sprintf('%s *%s', $indentation, $phpcsFile->eolChar)); } FixerHelper::addBefore($phpcsFile, $firstAnnotationPointer, $indentation . ' * '); $phpcsFile->fixer->endChangeset(); } /** * @param list $annotations */ private function checkLinesBetweenDifferentAnnotationsTypes(File $phpcsFile, int $docCommentOpenerPointer, array $annotations): void { $indentation = IndentationHelper::getIndentation($phpcsFile, $docCommentOpenerPointer); $previousAnnotation = null; foreach ($annotations as $annotation) { if ($previousAnnotation === null) { $previousAnnotation = $annotation; continue; } if ($annotation->getName() === $previousAnnotation->getName()) { $previousAnnotation = $annotation; continue; } $whitespaceAfterPreviousAnnotation = TokenHelper::getContent( $phpcsFile, $previousAnnotation->getEndPointer() + 1, $annotation->getStartPointer() - 1, ); $linesCountAfterPreviousAnnotation = max(substr_count($whitespaceAfterPreviousAnnotation, $phpcsFile->eolChar) - 1, 0); if ($linesCountAfterPreviousAnnotation === $this->linesCountBetweenDifferentAnnotationsTypes) { $previousAnnotation = $annotation; continue; } $fix = $phpcsFile->addFixableError( sprintf( 'Expected %d line%s between different annotations types, found %d.', $this->linesCountBetweenDifferentAnnotationsTypes, $this->linesCountBetweenDifferentAnnotationsTypes === 1 ? '' : 's', $linesCountAfterPreviousAnnotation, ), $annotation->getStartPointer(), self::CODE_INCORRECT_LINES_COUNT_BETWEEN_DIFFERENT_ANNOTATIONS_TYPES, ); if (!$fix) { $previousAnnotation = $annotation; continue; } $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetween($phpcsFile, $previousAnnotation->getEndPointer(), $annotation->getStartPointer()); $phpcsFile->fixer->addNewline($previousAnnotation->getEndPointer()); for ($i = 1; $i <= $this->linesCountBetweenDifferentAnnotationsTypes; $i++) { FixerHelper::add($phpcsFile, $previousAnnotation->getEndPointer(), sprintf('%s *%s', $indentation, $phpcsFile->eolChar)); } FixerHelper::addBefore($phpcsFile, $annotation->getStartPointer(), $indentation . ' * '); $phpcsFile->fixer->endChangeset(); $previousAnnotation = $annotation; } } /** * @param list $annotations */ private function checkAnnotationsGroups(File $phpcsFile, int $docCommentOpenerPointer, array $annotations): void { $tokens = $phpcsFile->getTokens(); $annotationsGroups = []; $annotationsGroup = []; $previousAnnotation = null; foreach ($annotations as $annotation) { if ( $previousAnnotation === null || $tokens[$previousAnnotation->getEndPointer()]['line'] + 1 === $tokens[$annotation->getStartPointer()]['line'] ) { $annotationsGroup[] = $annotation; $previousAnnotation = $annotation; continue; } $annotationsGroups[] = $annotationsGroup; $annotationsGroup = [$annotation]; $previousAnnotation = $annotation; } if (count($annotationsGroup) > 0) { $annotationsGroups[] = $annotationsGroup; } $this->checkAnnotationsGroupsOrder($phpcsFile, $docCommentOpenerPointer, $annotationsGroups, $annotations); $this->checkLinesBetweenAnnotationsGroups($phpcsFile, $docCommentOpenerPointer, $annotationsGroups); } /** * @param list> $annotationsGroups */ private function checkLinesBetweenAnnotationsGroups(File $phpcsFile, int $docCommentOpenerPointer, array $annotationsGroups): void { $tokens = $phpcsFile->getTokens(); $previousAnnotationsGroup = null; foreach ($annotationsGroups as $annotationsGroup) { if ($previousAnnotationsGroup === null) { $previousAnnotationsGroup = $annotationsGroup; continue; } $lastAnnotationInPreviousGroup = $previousAnnotationsGroup[count($previousAnnotationsGroup) - 1]; $firstAnnotationInActualGroup = $annotationsGroup[0]; $actualLinesCountBetweenAnnotationsGroups = $tokens[$firstAnnotationInActualGroup->getStartPointer()]['line'] - $tokens[$lastAnnotationInPreviousGroup->getEndPointer()]['line'] - 1; if ($actualLinesCountBetweenAnnotationsGroups === $this->linesCountBetweenAnnotationsGroups) { $previousAnnotationsGroup = $annotationsGroup; continue; } $fix = $phpcsFile->addFixableError( sprintf( 'Expected %d line%s between annotations groups, found %d.', $this->linesCountBetweenAnnotationsGroups, $this->linesCountBetweenAnnotationsGroups === 1 ? '' : 's', $actualLinesCountBetweenAnnotationsGroups, ), $firstAnnotationInActualGroup->getStartPointer(), self::CODE_INCORRECT_LINES_COUNT_BETWEEN_ANNOTATIONS_GROUPS, ); if (!$fix) { $previousAnnotationsGroup = $annotationsGroup; continue; } $indentation = IndentationHelper::getIndentation($phpcsFile, $docCommentOpenerPointer); $phpcsFile->fixer->beginChangeset(); $phpcsFile->fixer->addNewline($lastAnnotationInPreviousGroup->getEndPointer()); FixerHelper::removeBetween( $phpcsFile, $lastAnnotationInPreviousGroup->getEndPointer(), $firstAnnotationInActualGroup->getStartPointer(), ); for ($i = 1; $i <= $this->linesCountBetweenAnnotationsGroups; $i++) { FixerHelper::add( $phpcsFile, $lastAnnotationInPreviousGroup->getEndPointer(), sprintf('%s *%s', $indentation, $phpcsFile->eolChar), ); } FixerHelper::addBefore( $phpcsFile, $firstAnnotationInActualGroup->getStartPointer(), $indentation . ' * ', ); $phpcsFile->fixer->endChangeset(); } } /** * @param list> $annotationsGroups * @param list $annotations */ private function checkAnnotationsGroupsOrder( File $phpcsFile, int $docCommentOpenerPointer, array $annotationsGroups, array $annotations ): void { $getAnnotationsPointers = static fn (Annotation $annotation): int => $annotation->getStartPointer(); $equals = static function (array $firstAnnotationsGroup, array $secondAnnotationsGroup) use ($getAnnotationsPointers): bool { $firstAnnotationsPointers = array_map($getAnnotationsPointers, $firstAnnotationsGroup); $secondAnnotationsPointers = array_map($getAnnotationsPointers, $secondAnnotationsGroup); return count(array_diff($firstAnnotationsPointers, $secondAnnotationsPointers)) === 0 && count(array_diff($secondAnnotationsPointers, $firstAnnotationsPointers)) === 0; }; $sortedAnnotationsGroups = $this->sortAnnotationsToGroups($annotations); $incorrectAnnotationsGroupsExist = false; $annotationsGroupsPositions = []; $fix = false; $undefinedAnnotationsGroups = []; foreach ($annotationsGroups as $annotationsGroupPosition => $annotationsGroup) { foreach ($sortedAnnotationsGroups as $sortedAnnotationsGroupPosition => $sortedAnnotationsGroup) { if ($equals($annotationsGroup, $sortedAnnotationsGroup)) { $annotationsGroupsPositions[$annotationsGroupPosition] = $sortedAnnotationsGroupPosition; continue 2; } $undefinedAnnotationsGroup = true; foreach ($annotationsGroup as $annotation) { foreach ($this->getAnnotationsGroups() as $annotationNames) { foreach ($annotationNames as $annotationName) { if ($this->isAnnotationMatched($annotation, $annotationName)) { $undefinedAnnotationsGroup = false; break 3; } } } } if ($undefinedAnnotationsGroup) { $undefinedAnnotationsGroups[] = $annotationsGroupPosition; continue 2; } } $incorrectAnnotationsGroupsExist = true; $fix = $phpcsFile->addFixableError( 'Incorrect annotations group.', $annotationsGroup[0]->getStartPointer(), self::CODE_INCORRECT_ANNOTATIONS_GROUP, ); } if (count($annotationsGroupsPositions) === 0 && count($undefinedAnnotationsGroups) > 1) { $incorrectAnnotationsGroupsExist = true; $fix = $phpcsFile->addFixableError( 'Incorrect annotations group.', $annotationsGroups[0][0]->getStartPointer(), self::CODE_INCORRECT_ANNOTATIONS_GROUP, ); } if (!$incorrectAnnotationsGroupsExist) { foreach ($undefinedAnnotationsGroups as $undefinedAnnotationsGroupPosition) { $annotationsGroupsPositions[$undefinedAnnotationsGroupPosition] = (count($annotationsGroupsPositions) > 0 ? max($annotationsGroupsPositions) : 0) + 1; } ksort($annotationsGroupsPositions); $positionsMappedToGroups = array_keys($annotationsGroupsPositions); $tmp = array_values($annotationsGroupsPositions); asort($tmp); $normalizedAnnotationsGroupsPositions = array_combine(array_keys($positionsMappedToGroups), array_keys($tmp)); foreach ($normalizedAnnotationsGroupsPositions as $normalizedAnnotationsGroupPosition => $sortedAnnotationsGroupPosition) { if ($normalizedAnnotationsGroupPosition === $sortedAnnotationsGroupPosition) { continue; } $fix = $phpcsFile->addFixableError( 'Incorrect order of annotations groups.', $annotationsGroups[$positionsMappedToGroups[$normalizedAnnotationsGroupPosition]][0]->getStartPointer(), self::CODE_INCORRECT_ORDER_OF_ANNOTATIONS_GROUPS, ); break; } } foreach ($annotationsGroups as $annotationsGroupPosition => $annotationsGroup) { if (!array_key_exists($annotationsGroupPosition, $annotationsGroupsPositions)) { continue; } if (!array_key_exists($annotationsGroupsPositions[$annotationsGroupPosition], $sortedAnnotationsGroups)) { continue; } $sortedAnnotationsGroup = $sortedAnnotationsGroups[$annotationsGroupsPositions[$annotationsGroupPosition]]; foreach ($annotationsGroup as $annotationPosition => $annotation) { if ($annotation === $sortedAnnotationsGroup[$annotationPosition]) { continue; } $fix = $phpcsFile->addFixableError( 'Incorrect order of annotations in group.', $annotation->getStartPointer(), self::CODE_INCORRECT_ORDER_OF_ANNOTATIONS_IN_GROUP, ); break; } } if (!$fix) { return; } $firstAnnotation = $annotationsGroups[0][0]; $lastAnnotationsGroup = $annotationsGroups[count($annotationsGroups) - 1]; $lastAnnotation = $lastAnnotationsGroup[count($lastAnnotationsGroup) - 1]; $indentation = IndentationHelper::getIndentation($phpcsFile, $docCommentOpenerPointer); $fixedAnnotations = ''; $firstGroup = true; foreach ($sortedAnnotationsGroups as $sortedAnnotationsGroup) { if ($firstGroup) { $firstGroup = false; } else { for ($i = 0; $i < $this->linesCountBetweenAnnotationsGroups; $i++) { $fixedAnnotations .= sprintf('%s *%s', $indentation, $phpcsFile->eolChar); } } foreach ($sortedAnnotationsGroup as $sortedAnnotation) { $fixedAnnotations .= sprintf( '%s * %s%s', $indentation, trim(TokenHelper::getContent($phpcsFile, $sortedAnnotation->getStartPointer(), $sortedAnnotation->getEndPointer())), $phpcsFile->eolChar, ); } } $tokens = $phpcsFile->getTokens(); $docCommentCloserPointer = $tokens[$docCommentOpenerPointer]['comment_closer']; $endOfLineBeforeFirstAnnotation = TokenHelper::findPreviousContent( $phpcsFile, T_DOC_COMMENT_WHITESPACE, $phpcsFile->eolChar, $firstAnnotation->getStartPointer() - 1, $docCommentOpenerPointer, ); $docCommentContentEndPointer = TokenHelper::findNextContent( $phpcsFile, T_DOC_COMMENT_WHITESPACE, $phpcsFile->eolChar, $lastAnnotation->getEndPointer() + 1, $docCommentCloserPointer, ); $docCommentContentEndPointer ??= $lastAnnotation->getEndPointer(); $phpcsFile->fixer->beginChangeset(); if ($endOfLineBeforeFirstAnnotation === null) { FixerHelper::change( $phpcsFile, $docCommentOpenerPointer, $docCommentContentEndPointer, '/**' . $phpcsFile->eolChar . $fixedAnnotations, ); } else { FixerHelper::change($phpcsFile, $endOfLineBeforeFirstAnnotation + 1, $docCommentContentEndPointer, $fixedAnnotations); } $phpcsFile->fixer->endChangeset(); } /** * @param list $annotations * @return list> */ private function sortAnnotationsToGroups(array $annotations): array { $expectedAnnotationsGroups = $this->getAnnotationsGroups(); $sortedAnnotationsGroups = []; $annotationsNotInAnyGroup = []; foreach ($annotations as $annotation) { foreach ($expectedAnnotationsGroups as $annotationsGroupPosition => $annotationsGroup) { foreach ($annotationsGroup as $annotationName) { if ($this->isAnnotationMatched($annotation, $annotationName)) { $sortedAnnotationsGroups[$annotationsGroupPosition][] = $annotation; continue 3; } } } $annotationsNotInAnyGroup[] = $annotation; } ksort($sortedAnnotationsGroups); foreach (array_keys($sortedAnnotationsGroups) as $annotationsGroupPosition) { $expectedAnnotationsGroupOrder = array_flip($expectedAnnotationsGroups[$annotationsGroupPosition]); usort( $sortedAnnotationsGroups[$annotationsGroupPosition], function (Annotation $firstAnnotation, Annotation $secondAnnotation) use ($expectedAnnotationsGroupOrder): int { $getExpectedOrder = function (string $annotationName) use ($expectedAnnotationsGroupOrder): int { if (array_key_exists($annotationName, $expectedAnnotationsGroupOrder)) { return $expectedAnnotationsGroupOrder[$annotationName]; } $order = 0; foreach ($expectedAnnotationsGroupOrder as $expectedAnnotationName => $expectedAnnotationOrder) { if ($this->isAnnotationNameInAnnotationNamespace($expectedAnnotationName, $annotationName)) { $order = $expectedAnnotationOrder; break; } } return $order; }; $expectedOrder = $getExpectedOrder($firstAnnotation->getName()) <=> $getExpectedOrder($secondAnnotation->getName()); return $expectedOrder !== 0 ? $expectedOrder : $firstAnnotation->getStartPointer() <=> $secondAnnotation->getStartPointer(); }, ); } if (count($annotationsNotInAnyGroup) > 0) { $sortedAnnotationsGroups[] = $annotationsNotInAnyGroup; } return array_values($sortedAnnotationsGroups); } private function isAnnotationNameInAnnotationNamespace(string $annotationNamespace, string $annotationName): bool { return $this->isAnnotationStartedFrom($annotationNamespace, $annotationName) || ( in_array(substr($annotationNamespace, -1), ['\\', '-', ':'], true) && strpos($annotationName, $annotationNamespace) === 0 ); } private function isAnnotationStartedFrom(string $annotationNamespace, string $annotationName): bool { return substr($annotationNamespace, -1) === '*' && strpos($annotationName, substr($annotationNamespace, 0, -1)) === 0; } private function isAnnotationMatched(Annotation $annotation, string $annotationName): bool { if ($annotation->getName() === $annotationName) { return true; } return $this->isAnnotationNameInAnnotationNamespace($annotationName, $annotation->getName()); } private function checkLinesAfterLastContent( File $phpcsFile, int $docCommentOpenerPointer, int $docCommentCloserPointer, int $lastContentEndPointer ): void { $whitespaceAfterLastContent = TokenHelper::getContent($phpcsFile, $lastContentEndPointer + 1, $docCommentCloserPointer); $linesCountAfterLastContent = max(substr_count($whitespaceAfterLastContent, $phpcsFile->eolChar) - 1, 0); if ($linesCountAfterLastContent === $this->linesCountAfterLastContent) { return; } $fix = $phpcsFile->addFixableError( sprintf( 'Expected %d line%s after last content, found %d.', $this->linesCountAfterLastContent, $this->linesCountAfterLastContent === 1 ? '' : 's', $linesCountAfterLastContent, ), $lastContentEndPointer, self::CODE_INCORRECT_LINES_COUNT_AFTER_LAST_CONTENT, ); if (!$fix) { return; } $indentation = IndentationHelper::getIndentation($phpcsFile, $docCommentOpenerPointer); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetween($phpcsFile, $lastContentEndPointer, $docCommentCloserPointer); $phpcsFile->fixer->addNewline($lastContentEndPointer); for ($i = 1; $i <= $this->linesCountAfterLastContent; $i++) { FixerHelper::add($phpcsFile, $lastContentEndPointer, sprintf('%s *%s', $indentation, $phpcsFile->eolChar)); } FixerHelper::addBefore($phpcsFile, $docCommentCloserPointer, $indentation . ' '); $phpcsFile->fixer->endChangeset(); } /** * @return array> */ private function getAnnotationsGroups(): array { if ($this->normalizedAnnotationsGroups === null) { $this->normalizedAnnotationsGroups = []; foreach ($this->annotationsGroups as $annotationsGroup) { $this->normalizedAnnotationsGroups[] = SniffSettingsHelper::normalizeArray(explode(',', $annotationsGroup)); } } return $this->normalizedAnnotationsGroups; } } PK41]o88Zcoding-standard/SlevomatCodingStandard/Sniffs/Commenting/RequireOneLineDocCommentSniff.phpnu[addFixableError($error, $docCommentStartPointer, self::CODE_MULTI_LINE_DOC_COMMENT); } } PK41]L..acoding-standard/SlevomatCodingStandard/Sniffs/Commenting/DeprecatedAnnotationDeclarationSniff.phpnu[ */ public function register(): array { return [T_DOC_COMMENT_OPEN_TAG]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $docCommentStartPointer */ public function process(File $phpcsFile, $docCommentStartPointer): void { /** @var list> $annotations */ $annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentStartPointer, '@deprecated'); if (count($annotations) === 0) { return; } foreach ($annotations as $annotation) { if ($annotation->getValue()->description !== '') { continue; } $phpcsFile->addError( 'Deprecated annotation must have a description.', $annotation->getStartPointer(), self::MISSING_DESCRIPTION, ); } } } PK41]bcoding-standard/SlevomatCodingStandard/Sniffs/Commenting/RequireOneLinePropertyDocCommentSniff.phpnu[findNext(T_VARIABLE, $docCommentStartPointer); return $phpcsFile->addFixableError( sprintf($error, PropertyHelper::getFullyQualifiedName($phpcsFile, $propertyPointer)), $docCommentStartPointer, self::CODE_MULTI_LINE_PROPERTY_COMMENT, ); } } PK41]jNcoding-standard/SlevomatCodingStandard/Sniffs/Commenting/EmptyCommentSniff.phpnu[ */ public function register(): array { return [ T_DOC_COMMENT_OPEN_TAG, T_COMMENT, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $commentStartPointer */ public function process(File $phpcsFile, $commentStartPointer): void { $commentEndPointer = CommentHelper::getCommentEndPointer($phpcsFile, $commentStartPointer); if ($commentEndPointer === null) { // Part of block comment return; } $commentContent = $this->getCommentContent($phpcsFile, $commentStartPointer, $commentEndPointer); $isLineComment = CommentHelper::isLineComment($phpcsFile, $commentStartPointer); $isEmpty = $this->isEmpty($commentContent, $isLineComment); if (!$isEmpty) { return; } if ( $isLineComment && $this->isPartOfMultiLineInlineComments($phpcsFile, $commentStartPointer, $commentEndPointer) ) { return; } $fix = $phpcsFile->addFixableError('Empty comment', $commentStartPointer, self::CODE_EMPTY_COMMENT); if (!$fix) { return; } $tokens = $phpcsFile->getTokens(); /** @var int $pointerBeforeWhitespaceBeforeComment */ $pointerBeforeWhitespaceBeforeComment = TokenHelper::findPreviousNonWhitespace($phpcsFile, $commentStartPointer - 1); $whitespaceBeforeComment = $pointerBeforeWhitespaceBeforeComment !== $commentStartPointer - 1 ? TokenHelper::getContent($phpcsFile, $pointerBeforeWhitespaceBeforeComment + 1, $commentStartPointer - 1) : ''; $fixedWhitespaceBeforeComment = preg_replace('~[ \\t]+$~', '', $whitespaceBeforeComment); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetween($phpcsFile, $pointerBeforeWhitespaceBeforeComment, $commentStartPointer); FixerHelper::add($phpcsFile, $pointerBeforeWhitespaceBeforeComment, $fixedWhitespaceBeforeComment); FixerHelper::removeBetweenIncluding($phpcsFile, $commentStartPointer, $commentEndPointer); $whitespacePointerAfterComment = $commentEndPointer + 1; if ($tokens[$pointerBeforeWhitespaceBeforeComment]['line'] === $tokens[$commentStartPointer]['line']) { if (StringHelper::endsWith($tokens[$commentEndPointer]['content'], $phpcsFile->eolChar)) { $phpcsFile->fixer->addNewline($commentEndPointer); } } elseif ( array_key_exists($whitespacePointerAfterComment, $tokens) && $tokens[$whitespacePointerAfterComment]['code'] === T_WHITESPACE ) { $fixedWhitespaceAfterComment = preg_replace( '~^[ \\t]*' . $phpcsFile->eolChar . '~', '', $tokens[$whitespacePointerAfterComment]['content'], ); FixerHelper::replace($phpcsFile, $whitespacePointerAfterComment, $fixedWhitespaceAfterComment); } $phpcsFile->fixer->endChangeset(); } private function isEmpty(string $comment, bool $isLineComment): bool { return $isLineComment ? (bool) preg_match('~^\\s*$~', $comment) : (bool) preg_match('~^[\\s\*]*$~', $comment); } private function getCommentContent(File $phpcsFile, int $commentStartPointer, int $commentEndPointer): string { $tokens = $phpcsFile->getTokens(); if ($tokens[$commentStartPointer]['code'] === T_DOC_COMMENT_OPEN_TAG) { return TokenHelper::getContent($phpcsFile, $commentStartPointer + 1, $commentEndPointer - 1); } if (preg_match('~^(?://|#)(.*)~', $tokens[$commentStartPointer]['content'], $matches) === 1) { return $matches[1]; } return substr(TokenHelper::getContent($phpcsFile, $commentStartPointer, $commentEndPointer), 2, -2); } private function isPartOfMultiLineInlineComments(File $phpcsFile, int $commentStartPointer, int $commentEndPointer): bool { if (!$this->isNonEmptyLineCommentBefore($phpcsFile, $commentStartPointer)) { return false; } return $this->isNonEmptyLineCommentAfter($phpcsFile, $commentEndPointer); } private function isNonEmptyLineCommentBefore(File $phpcsFile, int $commentStartPointer): bool { $tokens = $phpcsFile->getTokens(); /** @var int $beforeCommentStartPointer */ $beforeCommentStartPointer = TokenHelper::findPreviousNonWhitespace($phpcsFile, $commentStartPointer - 1); if ($tokens[$beforeCommentStartPointer]['code'] !== T_COMMENT) { return false; } if (!CommentHelper::isLineComment($phpcsFile, $beforeCommentStartPointer)) { return false; } if ($tokens[$beforeCommentStartPointer]['line'] + 1 !== $tokens[$commentStartPointer]['line']) { return false; } /** @var int $beforeCommentEndPointer */ $beforeCommentEndPointer = CommentHelper::getCommentEndPointer($phpcsFile, $beforeCommentStartPointer); if (!$this->isEmpty($this->getCommentContent($phpcsFile, $beforeCommentStartPointer, $beforeCommentEndPointer), true)) { return true; } return $this->isNonEmptyLineCommentBefore($phpcsFile, $beforeCommentStartPointer); } private function isNonEmptyLineCommentAfter(File $phpcsFile, int $commentEndPointer): bool { $tokens = $phpcsFile->getTokens(); $afterCommentStartPointer = TokenHelper::findNextNonWhitespace($phpcsFile, $commentEndPointer + 1); if ($afterCommentStartPointer === null) { return false; } if ($tokens[$afterCommentStartPointer]['code'] !== T_COMMENT) { return false; } if (!CommentHelper::isLineComment($phpcsFile, $afterCommentStartPointer)) { return false; } if ($tokens[$commentEndPointer]['line'] + 1 !== $tokens[$afterCommentStartPointer]['line']) { return false; } /** @var int $afterCommentEndPointer */ $afterCommentEndPointer = CommentHelper::getCommentEndPointer($phpcsFile, $afterCommentStartPointer); if (!$this->isEmpty($this->getCommentContent($phpcsFile, $afterCommentStartPointer, $afterCommentEndPointer), true)) { return true; } return $this->isNonEmptyLineCommentAfter($phpcsFile, $afterCommentEndPointer); } } PK41]"JPcoding-standard/SlevomatCodingStandard/Sniffs/Commenting/AnnotationNameSniff.phpnu[|null */ public ?array $annotations = null; /** @var array|null */ private ?array $normalizedAnnotations = null; /** * @return array */ public function register(): array { return [ T_DOC_COMMENT_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $docCommentOpenPointer */ public function process(File $phpcsFile, $docCommentOpenPointer): void { $annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenPointer); $correctAnnotationNames = $this->getNormalizedAnnotationNames(); foreach ($annotations as $annotation) { $lowerCasedAnnotationName = strtolower($annotation->getName()); if (!array_key_exists($lowerCasedAnnotationName, $correctAnnotationNames)) { continue; } $correctAnnotationName = $correctAnnotationNames[$lowerCasedAnnotationName]; if ($correctAnnotationName === $annotation->getName()) { continue; } $annotationNameWithoutAtSign = ltrim($annotation->getName(), '@'); $fullyQualifiedAnnotationName = NamespaceHelper::resolveClassName( $phpcsFile, $annotationNameWithoutAtSign, $annotation->getStartPointer(), ); if (NamespaceHelper::normalizeToCanonicalName($fullyQualifiedAnnotationName) !== $annotationNameWithoutAtSign) { continue; } $fix = $phpcsFile->addFixableError( sprintf('Annotation name is incorrect. Expected %s, found %s.', $correctAnnotationName, $annotation->getName()), $annotation->getStartPointer(), self::CODE_ANNOTATION_NAME_INCORRECT, ); if (!$fix) { continue; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $annotation->getStartPointer(), $correctAnnotationName); $phpcsFile->fixer->endChangeset(); } $tokens = $phpcsFile->getTokens(); $docCommentContent = TokenHelper::getContent($phpcsFile, $docCommentOpenPointer, $tokens[$docCommentOpenPointer]['comment_closer']); if (preg_match_all( '~\{(' . implode('|', $correctAnnotationNames) . ')\}~i', $docCommentContent, $matches, PREG_OFFSET_CAPTURE, ) === 0) { return; } foreach ($matches[1] as $match) { $correctAnnotationName = $correctAnnotationNames[strtolower($match[0])]; if ($correctAnnotationName === $match[0]) { continue; } $fix = $phpcsFile->addFixableError( sprintf('Annotation name is incorrect. Expected %s, found %s.', $correctAnnotationName, $match[0]), $docCommentOpenPointer, self::CODE_ANNOTATION_NAME_INCORRECT, ); if (!$fix) { continue; } $phpcsFile->fixer->beginChangeset(); $fixedDocCommentContent = substr($docCommentContent, 0, $match[1]) . $correctAnnotationName . substr( $docCommentContent, $match[1] + strlen($match[0]), ); FixerHelper::change( $phpcsFile, $docCommentOpenPointer, $tokens[$docCommentOpenPointer]['comment_closer'], $fixedDocCommentContent, ); $phpcsFile->fixer->endChangeset(); } } /** * @return array */ private function getNormalizedAnnotationNames(): array { if ($this->normalizedAnnotations !== null) { return $this->normalizedAnnotations; } if ($this->annotations !== null) { $annotationNames = array_map( static fn (string $annotationName): string => ltrim($annotationName, '@'), SniffSettingsHelper::normalizeArray($this->annotations), ); } else { $annotationNames = [...self::STANDARD_ANNOTATIONS, ...self::PHPUNIT_ANNOTATIONS, ...self::STATIC_ANALYSIS_ANNOTATIONS]; foreach (self::STATIC_ANALYSIS_ANNOTATIONS as $annotationName) { if (strpos($annotationName, 'psalm') === 0) { continue; } foreach (AnnotationHelper::STATIC_ANALYSIS_PREFIXES as $prefix) { $annotationNames[] = sprintf('%s-%s', $prefix, $annotationName); } } } $annotationNames = array_map(static fn (string $annotationName): string => '@' . $annotationName, array_unique($annotationNames)); $this->normalizedAnnotations = array_combine( array_map(static fn (string $annotationName): string => strtolower($annotationName), $annotationNames), $annotationNames, ); return $this->normalizedAnnotations; } } PK41]؂B22]coding-standard/SlevomatCodingStandard/Sniffs/Commenting/AbstractRequireOneLineDocComment.phpnu[ */ public function register(): array { return [T_DOC_COMMENT_OPEN_TAG]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $docCommentStartPointer */ public function process(File $phpcsFile, $docCommentStartPointer): void { $tokens = $phpcsFile->getTokens(); // Only validate properties without description if (DocCommentHelper::hasDocCommentDescription($phpcsFile, $docCommentStartPointer)) { return; } $docCommentEndPointer = $tokens[$docCommentStartPointer]['comment_closer']; $lineDifference = $tokens[$docCommentEndPointer]['line'] - $tokens[$docCommentStartPointer]['line']; // Already one-line if ($lineDifference === 0) { return; } // Ignore empty lines $currentLinePointer = $docCommentStartPointer; do { $currentLinePointer = TokenHelper::findFirstTokenOnNextLine($phpcsFile, $currentLinePointer); if ($currentLinePointer === null || $currentLinePointer >= $docCommentEndPointer) { break; } $types = [T_DOC_COMMENT_STAR, T_DOC_COMMENT_CLOSE_TAG]; $startingPointer = TokenHelper::findNext($phpcsFile, $types, $currentLinePointer, $docCommentEndPointer); if ($startingPointer === null || $tokens[$startingPointer]['code'] === T_DOC_COMMENT_CLOSE_TAG) { break; } $nextEffectivePointer = TokenHelper::findNextExcluding( $phpcsFile, [T_DOC_COMMENT_WHITESPACE], $startingPointer + 1, $docCommentEndPointer + 1, ); if ($tokens[$currentLinePointer]['line'] === $tokens[$nextEffectivePointer]['line']) { continue; } $lineDifference--; } while (true); // Looks like a compound doc-comment if ($lineDifference > 2) { return; } $fix = $this->addError($phpcsFile, $docCommentStartPointer); if (!$fix) { return; } $contentStartPointer = TokenHelper::findNextExcluding( $phpcsFile, [ T_DOC_COMMENT_WHITESPACE, T_DOC_COMMENT_STAR, ], $docCommentStartPointer + 1, $docCommentEndPointer, ); $contentEndPointer = TokenHelper::findPreviousExcluding( $phpcsFile, [ T_DOC_COMMENT_WHITESPACE, T_DOC_COMMENT_STAR, ], $docCommentEndPointer - 1, $docCommentStartPointer, ); if ($contentStartPointer === null) { FixerHelper::removeBetween($phpcsFile, $docCommentStartPointer, $docCommentEndPointer); return; } $phpcsFile->fixer->beginChangeset(); for ($i = $docCommentStartPointer + 1; $i < $docCommentEndPointer; $i++) { if ($i >= $contentStartPointer && $i <= $contentEndPointer) { if ($i === $contentEndPointer) { FixerHelper::replace( $phpcsFile, $i, rtrim($phpcsFile->fixer->getTokenContent($i), ' '), ); } continue; } FixerHelper::replace($phpcsFile, $i, ''); } FixerHelper::addBefore($phpcsFile, $contentStartPointer, ' '); FixerHelper::addBefore($phpcsFile, $docCommentEndPointer, ' '); $phpcsFile->fixer->endChangeset(); } } PK41]77]coding-standard/SlevomatCodingStandard/Sniffs/Commenting/InlineDocCommentDeclarationSniff.phpnu[ */ public function register(): array { return [ T_DOC_COMMENT_OPEN_TAG, T_COMMENT, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $commentOpenPointer */ public function process(File $phpcsFile, $commentOpenPointer): void { $tokens = $phpcsFile->getTokens(); $commentClosePointer = $tokens[$commentOpenPointer]['code'] === T_COMMENT ? $commentOpenPointer : $tokens[$commentOpenPointer]['comment_closer']; $pointerAfterCommentClosePointer = TokenHelper::findNextEffective($phpcsFile, $commentClosePointer + 1); if ($pointerAfterCommentClosePointer !== null) { do { if ($tokens[$pointerAfterCommentClosePointer]['code'] !== T_ATTRIBUTE) { break; } $pointerAfterCommentClosePointer = TokenHelper::findNextEffective( $phpcsFile, $tokens[$pointerAfterCommentClosePointer]['attribute_closer'] + 1, ); } while (true); if (in_array( $tokens[$pointerAfterCommentClosePointer]['code'], [T_PRIVATE, T_PROTECTED, T_PUBLIC, T_READONLY, T_FINAL, T_CONST], true, )) { return; } if ($tokens[$pointerAfterCommentClosePointer]['code'] === T_STATIC) { $pointerAfterStatic = TokenHelper::findNextEffective($phpcsFile, $pointerAfterCommentClosePointer + 1); if (in_array($tokens[$pointerAfterStatic]['code'], [T_PRIVATE, T_PROTECTED, T_PUBLIC, T_READONLY], true)) { return; } if ($tokens[$pointerAfterStatic]['code'] === T_VARIABLE && PropertyHelper::isProperty($phpcsFile, $pointerAfterStatic)) { return; } } } if ($tokens[$commentOpenPointer]['code'] === T_COMMENT) { $this->checkCommentType($phpcsFile, $commentOpenPointer); return; } /** @var list> $annotations */ $annotations = AnnotationHelper::getAnnotations($phpcsFile, $commentOpenPointer, '@var'); if ($annotations === []) { return; } if ($this->allowDocCommentAboveReturn) { $pointerAfterCommentClosePointer = TokenHelper::findNextEffective($phpcsFile, $commentClosePointer + 1); if ($pointerAfterCommentClosePointer === null || $tokens[$pointerAfterCommentClosePointer]['code'] === T_RETURN) { return; } } $this->checkFormat($phpcsFile, $annotations); $this->checkVariable($phpcsFile, $annotations, $commentOpenPointer, $commentClosePointer); } private function checkCommentType(File $phpcsFile, int $commentOpenPointer): void { $tokens = $phpcsFile->getTokens(); if (preg_match('~^/\*\\s*@var\\s+~', $tokens[$commentOpenPointer]['content']) === 0) { return; } $fix = $phpcsFile->addFixableError( 'Invalid comment type /* */ for inline documentation comment, use /** */.', $commentOpenPointer, self::CODE_INVALID_COMMENT_TYPE, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace( $phpcsFile, $commentOpenPointer, sprintf('/**%s', substr($tokens[$commentOpenPointer]['content'], 2)), ); $phpcsFile->fixer->endChangeset(); } /** * @param list> $annotations */ private function checkFormat(File $phpcsFile, array $annotations): void { foreach ($annotations as $annotation) { if (!$annotation->isInvalid() && $annotation->getValue()->variableName !== '') { continue; } $variableName = '$variableName'; $annotationContent = (string) $annotation->getValue(); $type = null; if ( $annotationContent !== '' && preg_match('~(\$\w+)(?:\s+(.+))?$~i', $annotationContent, $matches) === 1 ) { $variableName = $matches[1]; $type = $matches[2] ?? null; } // It may be description when it contains whitespaces $isFixable = $type !== null && preg_match('~\s~', $type) === 0; if (!$isFixable) { $phpcsFile->addError( sprintf( 'Invalid inline documentation comment format "@var %1$s", expected "@var type %2$s Optional description".', $annotationContent, $variableName, ), $annotation->getStartPointer(), self::CODE_INVALID_FORMAT, ); continue; } $fix = $phpcsFile->addFixableError( sprintf( 'Invalid inline documentation comment format "@var %1$s", expected "@var %2$s %3$s".', $annotationContent, $type, $variableName, ), $annotation->getStartPointer(), self::CODE_INVALID_FORMAT, ); if (!$fix) { continue; } $phpcsFile->fixer->beginChangeset(); FixerHelper::add( $phpcsFile, $annotation->getStartPointer(), sprintf( ' %s %s ', $type, $variableName, ), ); FixerHelper::removeBetweenIncluding($phpcsFile, $annotation->getStartPointer() + 1, $annotation->getEndPointer()); $phpcsFile->fixer->endChangeset(); } } /** * @param list> $annotations */ private function checkVariable(File $phpcsFile, array $annotations, int $docCommentOpenerPointer, int $docCommentCloserPointer): void { $tokens = $phpcsFile->getTokens(); $checkedTokens = [T_VARIABLE, T_FOREACH, T_WHILE, T_LIST, T_OPEN_SHORT_ARRAY, T_CLOSURE, T_FN]; $variableNames = []; foreach ($annotations as $variableAnnotation) { if ($variableAnnotation->isInvalid()) { continue; } $variableName = $variableAnnotation->getValue()->variableName; if ($variableName === '') { continue; } $variableNames[] = $variableName; } $improveCodePointer = function (int $codePointer) use ($phpcsFile, $tokens, $checkedTokens, $variableNames): int { $shouldSearchClosure = false; if (!in_array($tokens[$codePointer]['code'], $checkedTokens, true)) { $shouldSearchClosure = true; } elseif ( $tokens[$codePointer]['code'] === T_VARIABLE && ( !$this->isAssignment($phpcsFile, $codePointer) || !in_array($tokens[$codePointer]['content'], $variableNames, true) ) ) { $shouldSearchClosure = true; } if (!$shouldSearchClosure) { return $codePointer; } $closurePointer = TokenHelper::findNext($phpcsFile, [T_CLOSURE, T_FN], $codePointer + 1); if ($closurePointer !== null && $tokens[$codePointer]['line'] === $tokens[$closurePointer]['line']) { return $closurePointer; } return $codePointer; }; $firstPointerOnNextLine = TokenHelper::findFirstNonWhitespaceOnNextLine($phpcsFile, $docCommentCloserPointer); $codePointerAfter = $firstPointerOnNextLine; while ($codePointerAfter !== null && $tokens[$codePointerAfter]['code'] === T_DOC_COMMENT_OPEN_TAG) { $codePointerAfter = TokenHelper::findFirstNonWhitespaceOnNextLine($phpcsFile, $codePointerAfter + 1); } if ($codePointerAfter !== null) { if ($tokens[$codePointerAfter]['code'] === T_STATIC) { $codePointerAfter = TokenHelper::findNextEffective($phpcsFile, $codePointerAfter + 1); } $codePointerAfter = $improveCodePointer($codePointerAfter); } $codePointerBefore = TokenHelper::findFirstNonWhitespaceOnPreviousLine($phpcsFile, $docCommentOpenerPointer); while ($codePointerBefore !== null && $tokens[$codePointerBefore]['code'] === T_DOC_COMMENT_OPEN_TAG) { $codePointerBefore = TokenHelper::findFirstNonWhitespaceOnPreviousLine($phpcsFile, $codePointerBefore - 1); } if ($codePointerBefore !== null) { $codePointerBefore = $improveCodePointer($codePointerBefore); } foreach ($annotations as $variableAnnotation) { if ($variableAnnotation->isInvalid()) { continue; } $variableName = $variableAnnotation->getValue()->variableName; if ($variableName === '') { continue; } $missingVariableErrorParameters = [ sprintf('Missing variable %s before or after the documentation comment.', $variableName), $docCommentOpenerPointer, self::CODE_MISSING_VARIABLE, ]; $noAssignmentErrorParameters = [ sprintf('No assignment to %s variable before or after the documentation comment.', $variableName), $docCommentOpenerPointer, self::CODE_NO_ASSIGNMENT, ]; if ($this->allowAboveNonAssignment && $firstPointerOnNextLine !== null) { for ($i = $firstPointerOnNextLine; $i < count($tokens); $i++) { if ($tokens[$i]['line'] > $tokens[$firstPointerOnNextLine]['line']) { break; } if ($tokens[$i]['code'] !== T_VARIABLE) { continue; } if ($tokens[$i]['content'] === $variableName) { return; } } } foreach ([1 => $codePointerBefore, 2 => $codePointerAfter] as $tryNo => $codePointer) { if ($codePointer === null || !in_array($tokens[$codePointer]['code'], $checkedTokens, true)) { if ($tryNo === 2) { $phpcsFile->addError(...$missingVariableErrorParameters); } continue; } if ($tokens[$codePointer]['code'] === T_VARIABLE) { if ($tokens[$codePointer]['content'] !== '$this' && !$this->isAssignment($phpcsFile, $codePointer)) { if ($tryNo === 2) { $phpcsFile->addError(...$noAssignmentErrorParameters); } continue; } if ($variableName !== $tokens[$codePointer]['content']) { if ($tryNo === 2) { $phpcsFile->addError(...$missingVariableErrorParameters); } continue; } } elseif ($tokens[$codePointer]['code'] === T_LIST) { $listParenthesisOpener = TokenHelper::findNextEffective($phpcsFile, $codePointer + 1); $variablePointerInList = TokenHelper::findNextContent( $phpcsFile, T_VARIABLE, $variableName, $listParenthesisOpener + 1, $tokens[$listParenthesisOpener]['parenthesis_closer'], ); if ($variablePointerInList === null) { if ($tryNo === 2) { $phpcsFile->addError(...$missingVariableErrorParameters); } continue; } } elseif ($tokens[$codePointer]['code'] === T_OPEN_SHORT_ARRAY) { $pointerAfterList = TokenHelper::findNextEffective($phpcsFile, $tokens[$codePointer]['bracket_closer'] + 1); if ($tokens[$pointerAfterList]['code'] !== T_EQUAL) { if ($tryNo === 2) { $phpcsFile->addError(...$noAssignmentErrorParameters); } continue; } $variablePointerInList = TokenHelper::findNextContent( $phpcsFile, T_VARIABLE, $variableName, $codePointer + 1, $tokens[$codePointer]['bracket_closer'], ); if ($variablePointerInList === null) { if ($tryNo === 2) { $phpcsFile->addError(...$missingVariableErrorParameters); } continue; } } elseif (in_array($tokens[$codePointer]['code'], [T_CLOSURE, T_FN], true)) { $parameterPointer = TokenHelper::findNextContent( $phpcsFile, T_VARIABLE, $variableName, $tokens[$codePointer]['parenthesis_opener'] + 1, $tokens[$codePointer]['parenthesis_closer'], ); if ($parameterPointer === null) { if ($tryNo === 2) { $phpcsFile->addError(...$missingVariableErrorParameters); } continue; } } else { if ($tokens[$codePointer]['code'] === T_WHILE) { $variablePointerInWhile = TokenHelper::findNextContent( $phpcsFile, T_VARIABLE, $variableName, $tokens[$codePointer]['parenthesis_opener'] + 1, $tokens[$codePointer]['parenthesis_closer'], ); if ($variablePointerInWhile === null) { if ($tryNo === 2) { $phpcsFile->addError(...$missingVariableErrorParameters); } continue; } $pointerAfterVariableInWhile = TokenHelper::findNextEffective($phpcsFile, $variablePointerInWhile + 1); if ($tokens[$pointerAfterVariableInWhile]['code'] !== T_EQUAL) { if ($tryNo === 2) { $phpcsFile->addError(...$noAssignmentErrorParameters); } continue; } } else { $asPointer = TokenHelper::findNext( $phpcsFile, T_AS, $tokens[$codePointer]['parenthesis_opener'] + 1, $tokens[$codePointer]['parenthesis_closer'], ); $variablePointerInForeach = TokenHelper::findNextContent( $phpcsFile, T_VARIABLE, $variableName, $asPointer + 1, $tokens[$codePointer]['parenthesis_closer'], ); if ($variablePointerInForeach === null) { if ($tryNo === 2) { $phpcsFile->addError(...$missingVariableErrorParameters); } continue; } } } // No error, don't check second $codePointer continue 2; } } } private function isAssignment(File $phpcsFile, int $pointer): bool { $tokens = $phpcsFile->getTokens(); $pointerAfterVariable = TokenHelper::findNextEffective($phpcsFile, $pointer + 1); if ($tokens[$pointerAfterVariable]['code'] === T_SEMICOLON) { $pointerBeforeVariable = TokenHelper::findPreviousEffective($phpcsFile, $pointer - 1); return $tokens[$pointerBeforeVariable]['code'] === T_STATIC; } return in_array($tokens[$pointerAfterVariable]['code'], [T_EQUAL, T_COALESCE_EQUAL], true); } } PK41]NNVcoding-standard/SlevomatCodingStandard/Sniffs/Commenting/ForbiddenAnnotationsSniff.phpnu[ */ public array $forbiddenAnnotations = []; /** @var list|null */ private ?array $normalizedForbiddenAnnotations = null; /** * @return array */ public function register(): array { return [ T_DOC_COMMENT_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $docCommentOpenPointer */ public function process(File $phpcsFile, $docCommentOpenPointer): void { $tokens = $phpcsFile->getTokens(); $annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenPointer); foreach ($annotations as $annotation) { if (!in_array($annotation->getName(), $this->getNormalizedForbiddenAnnotations(), true)) { continue; } $fix = $phpcsFile->addFixableError( sprintf('Use of annotation %s is forbidden.', $annotation->getName()), $annotation->getStartPointer(), self::CODE_ANNOTATION_FORBIDDEN, ); if (!$fix) { continue; } $starPointer = TokenHelper::findPrevious( $phpcsFile, T_DOC_COMMENT_STAR, $annotation->getStartPointer() - 1, $docCommentOpenPointer, ); $annotationStartPointer = $starPointer ?? $annotation->getStartPointer(); /** @var int $nextPointer */ $nextPointer = TokenHelper::findNext( $phpcsFile, [T_DOC_COMMENT_TAG, T_DOC_COMMENT_CLOSE_TAG], $annotation->getEndPointer() + 1, ); if ($tokens[$nextPointer]['code'] === T_DOC_COMMENT_TAG) { $nextPointer = TokenHelper::findPrevious($phpcsFile, T_DOC_COMMENT_STAR, $nextPointer - 1); } $annotationEndPointer = $nextPointer - 1; if ($tokens[$nextPointer]['code'] === T_DOC_COMMENT_CLOSE_TAG && $starPointer !== null) { $pointerBeforeWhitespace = TokenHelper::findPreviousExcluding( $phpcsFile, [T_DOC_COMMENT_WHITESPACE, T_DOC_COMMENT_STAR], $annotationStartPointer - 1, ); /** @var int $annotationStartPointer */ $annotationStartPointer = TokenHelper::findNext($phpcsFile, T_DOC_COMMENT_STAR, $pointerBeforeWhitespace + 1); } $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $annotationStartPointer, $annotationEndPointer); $docCommentUseful = false; $docCommentClosePointer = $tokens[$docCommentOpenPointer]['comment_closer']; for ($i = $docCommentOpenPointer + 1; $i < $docCommentClosePointer; $i++) { $tokenContent = trim($phpcsFile->fixer->getTokenContent($i)); if ($tokenContent === '' || $tokenContent === '*') { continue; } $docCommentUseful = true; break; } if (!$docCommentUseful) { /** @var int $nextPointerAfterDocComment */ $nextPointerAfterDocComment = TokenHelper::findNextEffective($phpcsFile, $docCommentClosePointer + 1); FixerHelper::removeBetweenIncluding($phpcsFile, $docCommentOpenPointer, $nextPointerAfterDocComment - 1); } $phpcsFile->fixer->endChangeset(); } } /** * @return list */ private function getNormalizedForbiddenAnnotations(): array { $this->normalizedForbiddenAnnotations ??= SniffSettingsHelper::normalizeArray($this->forbiddenAnnotations); return $this->normalizedForbiddenAnnotations; } } PK41]TJScoding-standard/SlevomatCodingStandard/Sniffs/Commenting/ForbiddenCommentsSniff.phpnu[ */ public array $forbiddenCommentPatterns = []; /** * @return array */ public function register(): array { return [ T_DOC_COMMENT_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $docCommentOpenPointer */ public function process(File $phpcsFile, $docCommentOpenPointer): void { $tokens = $phpcsFile->getTokens(); $comments = DocCommentHelper::getDocCommentDescription($phpcsFile, $docCommentOpenPointer); if ($comments === null) { return; } foreach (SniffSettingsHelper::normalizeArray($this->forbiddenCommentPatterns) as $forbiddenCommentPattern) { if (!SniffSettingsHelper::isValidRegularExpression($forbiddenCommentPattern)) { throw new Exception(sprintf('%s is not valid PCRE pattern.', $forbiddenCommentPattern)); } foreach ($comments as $comment) { if (preg_match($forbiddenCommentPattern, $comment->getContent()) === 0) { continue; } $fix = $phpcsFile->addFixableError( sprintf('Documentation comment contains forbidden comment "%s".', $comment->getContent()), $comment->getPointer(), self::CODE_COMMENT_FORBIDDEN, ); if (!$fix) { continue; } $phpcsFile->fixer->beginChangeset(); $fixedDocComment = preg_replace($forbiddenCommentPattern, '', $comment->getContent()); FixerHelper::replace($phpcsFile, $comment->getPointer(), $fixedDocComment); for ($i = $comment->getPointer() - 1; $i > $docCommentOpenPointer; $i--) { $contentWithoutSpaces = preg_replace('~ +$~', '', $tokens[$i]['content'], -1, $replacedCount); if ($replacedCount === 0) { break; } FixerHelper::replace($phpcsFile, $i, $contentWithoutSpaces); } $docCommentContent = ''; for ($i = $docCommentOpenPointer + 1; $i < $tokens[$docCommentOpenPointer]['comment_closer']; $i++) { $token = $phpcsFile->fixer->getTokenContent($i); $docCommentContent .= $token; } if (preg_match('~^[\\s\*]*$~', $docCommentContent) !== 0) { $pointerBeforeDocComment = $docCommentOpenPointer - 1; $contentBeforeWithoutSpaces = preg_replace( '~[\t ]+$~', '', $tokens[$pointerBeforeDocComment]['content'], -1, $replacedCount, ); if ($replacedCount !== 0) { FixerHelper::replace($phpcsFile, $pointerBeforeDocComment, $contentBeforeWithoutSpaces); } FixerHelper::removeBetweenIncluding( $phpcsFile, $docCommentOpenPointer, $tokens[$docCommentOpenPointer]['comment_closer'], ); $pointerAfterDocComment = $tokens[$docCommentOpenPointer]['comment_closer'] + 1; if (array_key_exists($pointerAfterDocComment, $tokens)) { $contentAfterWithoutSpaces = preg_replace( '~^[\r\n]+~', '', $tokens[$pointerAfterDocComment]['content'], -1, $replacedCount, ); if ($replacedCount !== 0) { FixerHelper::replace($phpcsFile, $pointerAfterDocComment, $contentAfterWithoutSpaces); } } } $phpcsFile->fixer->endChangeset(); } } } } PK41]P Zcoding-standard/SlevomatCodingStandard/Sniffs/Commenting/UselessInheritDocCommentSniff.phpnu[ */ public function register(): array { return [ T_DOC_COMMENT_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $docCommentOpenPointer */ public function process(File $phpcsFile, $docCommentOpenPointer): void { $tokens = $phpcsFile->getTokens(); $docCommentContent = ''; for ($i = $docCommentOpenPointer + 1; $i < $tokens[$docCommentOpenPointer]['comment_closer']; $i++) { if (in_array($tokens[$i]['code'], [T_DOC_COMMENT_WHITESPACE, T_DOC_COMMENT_STAR], true)) { continue; } $docCommentContent .= $tokens[$i]['content']; } if (preg_match('~^(?:\{@inheritDoc\}|@inheritDoc)$~i', $docCommentContent) === 0) { return; } $searchPointer = $tokens[$docCommentOpenPointer]['comment_closer'] + 1; do { $docCommentOwnerPointer = TokenHelper::findNext( $phpcsFile, [...TokenHelper::FUNCTION_TOKEN_CODES, ...TokenHelper::TYPE_HINT_TOKEN_CODES, T_ATTRIBUTE], $searchPointer, ); if ($docCommentOwnerPointer === null) { return; } if ($tokens[$docCommentOwnerPointer]['code'] === T_ATTRIBUTE) { $searchPointer = $tokens[$docCommentOwnerPointer]['attribute_closer'] + 1; continue; } break; } while (true); if (in_array($tokens[$docCommentOwnerPointer]['code'], TokenHelper::FUNCTION_TOKEN_CODES, true)) { $returnTypeHint = FunctionHelper::findReturnTypeHint($phpcsFile, $docCommentOwnerPointer); if ($returnTypeHint === null) { return; } if (TypeHintHelper::isSimpleIterableTypeHint($returnTypeHint->getTypeHintWithoutNullabilitySymbol())) { return; } $parametersTypeHints = FunctionHelper::getParametersTypeHints($phpcsFile, $docCommentOwnerPointer); foreach ($parametersTypeHints as $parameterTypeHint) { if ($parameterTypeHint === null) { return; } if (TypeHintHelper::isSimpleIterableTypeHint($parameterTypeHint->getTypeHint())) { return; } } } $fix = $phpcsFile->addFixableError( 'Useless documentation comment with @inheritDoc.', $docCommentOpenPointer, self::CODE_USELESS_INHERIT_DOC_COMMENT, ); if (!$fix) { return; } /** @var int $fixerStart */ $fixerStart = TokenHelper::findLastTokenOnPreviousLine($phpcsFile, $docCommentOpenPointer); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $fixerStart, $tokens[$docCommentOpenPointer]['comment_closer']); $phpcsFile->fixer->endChangeset(); } } PK41]8: [coding-standard/SlevomatCodingStandard/Sniffs/Functions/RequireTrailingCommaInCallSniff.phpnu[ */ public function register(): array { return [ T_OPEN_PARENTHESIS, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $parenthesisOpenerPointer */ public function process(File $phpcsFile, $parenthesisOpenerPointer): void { $this->enable = SniffSettingsHelper::isEnabledByPhpVersion($this->enable, 70300); if (!$this->enable) { return; } $tokens = $phpcsFile->getTokens(); $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1); if (!in_array( $tokens[$pointerBeforeParenthesisOpener]['code'], [...TokenHelper::ONLY_NAME_TOKEN_CODES, T_STRING, T_VARIABLE, T_ISSET, T_UNSET, T_CLOSE_PARENTHESIS, T_SELF, T_STATIC, T_PARENT], true, )) { return; } $functionPointer = TokenHelper::findPreviousEffective($phpcsFile, $pointerBeforeParenthesisOpener - 1); if (in_array($tokens[$functionPointer]['code'], TokenHelper::FUNCTION_TOKEN_CODES, true)) { return; } $parenthesisCloserPointer = $tokens[$parenthesisOpenerPointer]['parenthesis_closer']; if ($tokens[$parenthesisOpenerPointer]['line'] === $tokens[$parenthesisCloserPointer]['line']) { return; } $pointerBeforeParenthesisCloser = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisCloserPointer - 1); if ($pointerBeforeParenthesisCloser === $parenthesisOpenerPointer) { return; } if ($tokens[$parenthesisCloserPointer]['line'] === $tokens[$pointerBeforeParenthesisCloser]['line']) { return; } if ($tokens[$pointerBeforeParenthesisCloser]['code'] === T_COMMA) { return; } $fix = $phpcsFile->addFixableError( 'Multi-line function calls must have a trailing comma after the last parameter.', $pointerBeforeParenthesisCloser, self::CODE_MISSING_TRAILING_COMMA, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::add($phpcsFile, $pointerBeforeParenthesisCloser, ','); $phpcsFile->fixer->endChangeset(); } } PK41].bcoding-standard/SlevomatCodingStandard/Sniffs/Functions/RequireTrailingCommaInDeclarationSniff.phpnu[ */ public function register(): array { return TokenHelper::FUNCTION_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $functionPointer */ public function process(File $phpcsFile, $functionPointer): void { $this->enable = SniffSettingsHelper::isEnabledByPhpVersion($this->enable, 80000); if (!$this->enable) { return; } $tokens = $phpcsFile->getTokens(); $parenthesisOpenerPointer = $tokens[$functionPointer]['parenthesis_opener']; $parenthesisCloserPointer = $tokens[$functionPointer]['parenthesis_closer']; if ($tokens[$parenthesisOpenerPointer]['line'] === $tokens[$parenthesisCloserPointer]['line']) { return; } $pointerBeforeParenthesisCloser = TokenHelper::findPreviousEffective( $phpcsFile, $parenthesisCloserPointer - 1, $parenthesisOpenerPointer, ); if ($pointerBeforeParenthesisCloser === $parenthesisOpenerPointer) { return; } if ($tokens[$pointerBeforeParenthesisCloser]['code'] === T_COMMA) { return; } $fix = $phpcsFile->addFixableError( 'Multi-line function declaration must have a trailing comma after the last parameter.', $pointerBeforeParenthesisCloser, self::CODE_MISSING_TRAILING_COMMA, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::add($phpcsFile, $pointerBeforeParenthesisCloser, ','); $phpcsFile->fixer->endChangeset(); } } PK41] ΝWcoding-standard/SlevomatCodingStandard/Sniffs/Functions/DisallowNamedArgumentsSniff.phpnu[ */ public function register(): array { return [ T_PARAM_NAME, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $argumentNamePointer */ public function process(File $phpcsFile, $argumentNamePointer): void { $tokens = $phpcsFile->getTokens(); $phpcsFile->addError( sprintf('Named arguments are disallowed, usage of named argument "%s" found.', $tokens[$argumentNamePointer]['content']), $argumentNamePointer, self::CODE_DISALLOWED_NAMED_ARGUMENT, ); } } PK41]y Sccoding-standard/SlevomatCodingStandard/Sniffs/Functions/DisallowTrailingCommaInDeclarationSniff.phpnu[ */ public function register(): array { return TokenHelper::FUNCTION_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $functionPointer */ public function process(File $phpcsFile, $functionPointer): void { $tokens = $phpcsFile->getTokens(); $parenthesisOpenerPointer = $tokens[$functionPointer]['parenthesis_opener']; $parenthesisCloserPointer = $tokens[$functionPointer]['parenthesis_closer']; $pointerBeforeParenthesisCloser = TokenHelper::findPreviousExcluding( $phpcsFile, T_WHITESPACE, $parenthesisCloserPointer - 1, $parenthesisOpenerPointer, ); if ($tokens[$pointerBeforeParenthesisCloser]['code'] !== T_COMMA) { return; } if ($this->onlySingleLine && $tokens[$parenthesisOpenerPointer]['line'] !== $tokens[$parenthesisCloserPointer]['line']) { return; } $fix = $phpcsFile->addFixableError( 'Trailing comma after the last parameter in function declaration is disallowed.', $pointerBeforeParenthesisCloser, self::CODE_DISALLOWED_TRAILING_COMMA, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $pointerBeforeParenthesisCloser, ''); if ($tokens[$pointerBeforeParenthesisCloser]['line'] === $tokens[$parenthesisCloserPointer]['line']) { FixerHelper::removeBetween($phpcsFile, $pointerBeforeParenthesisCloser, $parenthesisCloserPointer); } $phpcsFile->fixer->endChangeset(); } } PK41]7%v v Kcoding-standard/SlevomatCodingStandard/Sniffs/Functions/StrictCallSniff.phpnu[ 3, 'array_search' => 3, 'base64_decode' => 2, 'array_keys' => 3, ]; /** * @return array */ public function register(): array { return TokenHelper::ONLY_NAME_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $stringPointer */ public function process(File $phpcsFile, $stringPointer): void { $tokens = $phpcsFile->getTokens(); $parenthesisOpenerPointer = TokenHelper::findNextEffective($phpcsFile, $stringPointer + 1); if ($tokens[$parenthesisOpenerPointer]['code'] !== T_OPEN_PARENTHESIS) { return; } $parenthesisCloserPointer = $tokens[$parenthesisOpenerPointer]['parenthesis_closer']; $functionName = ltrim(strtolower($tokens[$stringPointer]['content']), '\\'); if (!array_key_exists($functionName, self::FUNCTIONS)) { return; } $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $stringPointer - 1); if (in_array($tokens[$previousPointer]['code'], [T_OBJECT_OPERATOR, T_DOUBLE_COLON, T_FUNCTION], true)) { return; } $commaPointers = []; for ($i = $parenthesisOpenerPointer + 1; $i < $parenthesisCloserPointer; $i++) { if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS) { $i = $tokens[$i]['parenthesis_closer']; continue; } if ($tokens[$i]['code'] === T_OPEN_SHORT_ARRAY) { $i = $tokens[$i]['bracket_closer']; continue; } if ($tokens[$i]['code'] === T_COMMA) { $commaPointers[] = $i; } } $commaPointersCount = count($commaPointers); $parametersCount = $commaPointersCount + 1; $lastCommaPointer = $commaPointersCount > 0 ? $commaPointers[$commaPointersCount - 1] : null; $hasTrailingComma = false; if ( $lastCommaPointer !== null && TokenHelper::findNextEffective($phpcsFile, $lastCommaPointer + 1, $parenthesisCloserPointer) === null ) { $hasTrailingComma = true; $parametersCount--; } if ($parametersCount === self::FUNCTIONS[$functionName]) { $strictParameterValue = TokenHelper::getContent( $phpcsFile, $commaPointers[self::FUNCTIONS[$functionName] - 2] + 1, ($hasTrailingComma ? $lastCommaPointer : $parenthesisCloserPointer) - 1, ); if (strtolower(trim($strictParameterValue)) !== 'false') { return; } $phpcsFile->addError( sprintf('Strict parameter should be set to true in %s() call.', $functionName), $stringPointer, self::CODE_NON_STRICT_COMPARISON, ); } elseif ($parametersCount === self::FUNCTIONS[$functionName] - 1) { $phpcsFile->addError( sprintf('Strict parameter missing in %s() call.', $functionName), $stringPointer, self::CODE_STRICT_PARAMETER_MISSING, ); } } } PK41]yp;;Ycoding-standard/SlevomatCodingStandard/Sniffs/Functions/ArrowFunctionDeclarationSniff.phpnu[ */ public function register(): array { return [ T_FN, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $arrowFunctionPointer */ public function process(File $phpcsFile, $arrowFunctionPointer): void { $this->spacesCountAfterKeyword = SniffSettingsHelper::normalizeInteger($this->spacesCountAfterKeyword); $this->spacesCountBeforeArrow = SniffSettingsHelper::normalizeInteger($this->spacesCountBeforeArrow); $this->spacesCountAfterArrow = SniffSettingsHelper::normalizeInteger($this->spacesCountAfterArrow); $this->checkSpacesAfterKeyword($phpcsFile, $arrowFunctionPointer); $arrowPointer = TokenHelper::findNext($phpcsFile, T_FN_ARROW, $arrowFunctionPointer); $this->checkSpacesBeforeArrow($phpcsFile, $arrowPointer); $this->checkSpacesAfterArrow($phpcsFile, $arrowPointer); } private function checkSpacesAfterKeyword(File $phpcsFile, int $arrowFunctionPointer): void { $pointerAfter = TokenHelper::findNextNonWhitespace($phpcsFile, $arrowFunctionPointer + 1); $spaces = TokenHelper::getContent($phpcsFile, $arrowFunctionPointer + 1, $pointerAfter - 1); if ($this->allowMultiLine && strpos($spaces, $phpcsFile->eolChar) === 0) { return; } $actualSpaces = strlen($spaces); if ( $actualSpaces === $this->spacesCountAfterKeyword && ( $this->spacesCountAfterKeyword === 0 || preg_match('~^ +$~', $spaces) === 1 ) ) { return; } $fix = $phpcsFile->addFixableError( $this->formatErrorMessage('after "fn" keyword', $this->spacesCountAfterKeyword), $arrowFunctionPointer, self::CODE_INCORRECT_SPACES_AFTER_KEYWORD, ); if (!$fix) { return; } $this->fixSpaces($phpcsFile, $arrowFunctionPointer, $pointerAfter, $this->spacesCountAfterKeyword); } private function checkSpacesBeforeArrow(File $phpcsFile, int $arrowPointer): void { $pointerBefore = TokenHelper::findPreviousNonWhitespace($phpcsFile, $arrowPointer - 1); $spaces = TokenHelper::getContent($phpcsFile, $pointerBefore + 1, $arrowPointer - 1); if ($this->allowMultiLine && strpos($spaces, $phpcsFile->eolChar) === 0) { return; } $actualSpaces = strlen($spaces); if ( $actualSpaces === $this->spacesCountBeforeArrow && ( $this->spacesCountBeforeArrow === 0 || preg_match('~^ +$~', $spaces) === 1 ) ) { return; } $fix = $phpcsFile->addFixableError( $this->formatErrorMessage('before =>', $this->spacesCountBeforeArrow), $arrowPointer, self::CODE_INCORRECT_SPACES_BEFORE_ARROW, ); if (!$fix) { return; } $this->fixSpaces($phpcsFile, $pointerBefore, $arrowPointer, $this->spacesCountBeforeArrow); } private function checkSpacesAfterArrow(File $phpcsFile, int $arrowPointer): void { $pointerAfter = TokenHelper::findNextNonWhitespace($phpcsFile, $arrowPointer + 1); $spaces = TokenHelper::getContent($phpcsFile, $arrowPointer + 1, $pointerAfter - 1); if ($this->allowMultiLine && strpos($spaces, $phpcsFile->eolChar) === 0) { return; } $actualSpaces = strlen($spaces); if ($actualSpaces === $this->spacesCountAfterArrow && ($this->spacesCountAfterArrow === 0 || preg_match('~^ +$~', $spaces) === 1)) { return; } $fix = $phpcsFile->addFixableError( $this->formatErrorMessage('after =>', $this->spacesCountAfterArrow), $arrowPointer, self::CODE_INCORRECT_SPACES_AFTER_ARROW, ); if (!$fix) { return; } $this->fixSpaces($phpcsFile, $arrowPointer, $pointerAfter, $this->spacesCountAfterArrow); } private function formatErrorMessage(string $suffix, int $requiredSpaces): string { return $requiredSpaces === 0 ? sprintf('There must be no whitespace %s.', $suffix) : sprintf('There must be exactly %d whitespace%s %s.', $requiredSpaces, $requiredSpaces !== 1 ? 's' : '', $suffix); } private function fixSpaces(File $phpcsFile, int $pointerBefore, int $pointerAfter, int $requiredSpaces): void { $phpcsFile->fixer->beginChangeset(); if ($requiredSpaces > 0) { FixerHelper::add($phpcsFile, $pointerBefore, str_repeat(' ', $requiredSpaces)); } FixerHelper::removeBetween($phpcsFile, $pointerBefore, $pointerAfter); $phpcsFile->fixer->endChangeset(); } } PK41]!Ucoding-standard/SlevomatCodingStandard/Sniffs/Functions/RequireArrowFunctionSniff.phpnu[ */ public function register(): array { return [ T_CLOSURE, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $closurePointer */ public function process(File $phpcsFile, $closurePointer): void { $this->enable = SniffSettingsHelper::isEnabledByPhpVersion($this->enable, 70400); if (!$this->enable) { return; } $tokens = $phpcsFile->getTokens(); $returnPointer = TokenHelper::findNextEffective($phpcsFile, $tokens[$closurePointer]['scope_opener'] + 1); if ($tokens[$returnPointer]['code'] !== T_RETURN) { return; } $usePointer = TokenHelper::findNextEffective($phpcsFile, $tokens[$closurePointer]['parenthesis_closer'] + 1); if ($tokens[$usePointer]['code'] === T_USE) { $useOpenParenthesisPointer = TokenHelper::findNextEffective($phpcsFile, $usePointer + 1); if (TokenHelper::findNext( $phpcsFile, T_BITWISE_AND, $useOpenParenthesisPointer + 1, $tokens[$useOpenParenthesisPointer]['parenthesis_closer'], ) !== null) { return; } } if (!$this->allowNested) { $closureOrArrowFunctionPointer = TokenHelper::findNext( $phpcsFile, [T_CLOSURE, T_FN], $tokens[$closurePointer]['scope_opener'] + 1, $tokens[$closurePointer]['scope_closer'], ); if ($closureOrArrowFunctionPointer !== null) { return; } } $fix = $phpcsFile->addFixableError('Use arrow function.', $closurePointer, self::CODE_REQUIRED_ARROW_FUNCTION); if (!$fix) { return; } $pointerAfterReturn = TokenHelper::findNextNonWhitespace($phpcsFile, $returnPointer + 1); $semicolonAfterReturn = $this->findSemicolon($phpcsFile, $returnPointer); $usePointer = TokenHelper::findNext( $phpcsFile, T_USE, $tokens[$closurePointer]['parenthesis_closer'] + 1, $tokens[$closurePointer]['scope_opener'], ); $nonWhitespacePointerBeforeScopeOpener = TokenHelper::findPreviousExcluding( $phpcsFile, T_WHITESPACE, $tokens[$closurePointer]['scope_opener'] - 1, ); $nonWhitespacePointerAfterUseParenthesisCloser = null; if ($usePointer !== null) { $useParenthesiCloserPointer = TokenHelper::findNext($phpcsFile, T_CLOSE_PARENTHESIS, $usePointer + 1); $nonWhitespacePointerAfterUseParenthesisCloser = TokenHelper::findNextExcluding( $phpcsFile, T_WHITESPACE, $useParenthesiCloserPointer + 1, ); } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $closurePointer, 'fn'); if ($nonWhitespacePointerAfterUseParenthesisCloser !== null) { FixerHelper::removeBetween( $phpcsFile, $tokens[$closurePointer]['parenthesis_closer'], $nonWhitespacePointerAfterUseParenthesisCloser, ); } FixerHelper::removeBetween($phpcsFile, $nonWhitespacePointerBeforeScopeOpener, $pointerAfterReturn); FixerHelper::add($phpcsFile, $nonWhitespacePointerBeforeScopeOpener, ' => '); FixerHelper::removeBetweenIncluding($phpcsFile, $semicolonAfterReturn, $tokens[$closurePointer]['scope_closer']); $phpcsFile->fixer->endChangeset(); } private function findSemicolon(File $phpcsFile, int $pointer): int { $tokens = $phpcsFile->getTokens(); $semicolonPointer = null; for ($i = $pointer + 1; $i < count($tokens) - 1; $i++) { if ($tokens[$i]['code'] !== T_SEMICOLON) { continue; } if (!ScopeHelper::isInSameScope($phpcsFile, $pointer, $i)) { continue; } $semicolonPointer = $i; break; } /** @var int $semicolonPointer */ $semicolonPointer = $semicolonPointer; return $semicolonPointer; } } PK41]BXC C Ncoding-standard/SlevomatCodingStandard/Sniffs/Functions/StaticClosureSniff.phpnu[ */ public function register(): array { return [ T_CLOSURE, T_FN, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $closurePointer */ public function process(File $phpcsFile, $closurePointer): void { $tokens = $phpcsFile->getTokens(); $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $closurePointer - 1); if ($tokens[$previousPointer]['code'] === T_STATIC) { return; } if ($tokens[$previousPointer]['code'] === T_OPEN_PARENTHESIS) { $pointerBeforeParenthesis = TokenHelper::findPreviousEffective($phpcsFile, $previousPointer - 1); if ( $tokens[$pointerBeforeParenthesis]['code'] === T_STRING && $tokens[$pointerBeforeParenthesis]['content'] === 'bind' ) { return; } } $closureScopeOpenerPointer = $tokens[$closurePointer]['scope_opener']; $closureScopeCloserPointer = $tokens[$closurePointer]['scope_closer']; $thisPointer = TokenHelper::findNextContent( $phpcsFile, T_VARIABLE, '$this', $closureScopeOpenerPointer + 1, $closureScopeCloserPointer + 1, ); if ($thisPointer !== null) { return; } $stringPointers = TokenHelper::findNextAll( $phpcsFile, T_DOUBLE_QUOTED_STRING, $closureScopeOpenerPointer + 1, $closureScopeCloserPointer, ); foreach ($stringPointers as $stringPointer) { if (VariableHelper::isUsedInScopeInString($phpcsFile, '$this', $stringPointer)) { return; } } $parentPointer = TokenHelper::findNext($phpcsFile, T_PARENT, $closureScopeOpenerPointer + 1, $closureScopeCloserPointer); if ($parentPointer !== null) { return; } $fix = $phpcsFile->addFixableError( 'Closure not using "$this" should be declared static.', $closurePointer, self::CODE_CLOSURE_NOT_STATIC, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::addBefore($phpcsFile, $closurePointer, 'static '); $phpcsFile->fixer->endChangeset(); } } PK41]9x` ` Lcoding-standard/SlevomatCodingStandard/Sniffs/Functions/AbstractLineCall.phpnu[ */ public function register(): array { return [...TokenHelper::ONLY_NAME_TOKEN_CODES, T_SELF, T_STATIC, T_PARENT]; } protected function isCall(File $phpcsFile, int $stringPointer): bool { $tokens = $phpcsFile->getTokens(); $nextPointer = TokenHelper::findNextEffective($phpcsFile, $stringPointer + 1); if ($tokens[$nextPointer]['code'] !== T_OPEN_PARENTHESIS) { return false; } $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $stringPointer - 1); return $tokens[$previousPointer]['code'] !== T_FUNCTION; } protected function getLineStart(File $phpcsFile, int $pointer): string { $firstPointerOnLine = TokenHelper::findFirstTokenOnLine($phpcsFile, $pointer); return TokenHelper::getContent($phpcsFile, $firstPointerOnLine, $pointer); } protected function getCall(File $phpcsFile, int $parenthesisOpenerPointer, int $parenthesisCloserPointer): string { $tokens = $phpcsFile->getTokens(); $pointerBeforeParenthesisCloser = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisCloserPointer - 1); $endPointer = $tokens[$pointerBeforeParenthesisCloser]['code'] === T_COMMA ? $pointerBeforeParenthesisCloser : $parenthesisCloserPointer; $call = ''; for ($i = $parenthesisOpenerPointer + 1; $i < $endPointer; $i++) { if ($tokens[$i]['code'] === T_COMMA) { $nextPointer = TokenHelper::findNextEffective($phpcsFile, $i + 1); if ($tokens[$nextPointer]['code'] === T_CLOSE_PARENTHESIS) { $i = $nextPointer - 1; continue; } } if ($tokens[$i]['code'] === T_WHITESPACE) { if ($tokens[$i]['content'] === $phpcsFile->eolChar) { if ($tokens[$i - 1]['code'] === T_COMMA) { $call .= ' '; } continue; } if ($tokens[$i]['column'] === 1) { // Nothing continue; } } $call .= $tokens[$i]['content']; } return trim($call); } protected function getLineEnd(File $phpcsFile, int $pointer): string { $firstPointerOnNextLine = TokenHelper::findFirstTokenOnNextLine($phpcsFile, $pointer); return rtrim(TokenHelper::getContent($phpcsFile, $pointer, $firstPointerOnNextLine - 1)); } } PK41]x^U U \coding-standard/SlevomatCodingStandard/Sniffs/Functions/DisallowTrailingCommaInCallSniff.phpnu[ */ public function register(): array { return [ T_OPEN_PARENTHESIS, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $parenthesisOpenerPointer */ public function process(File $phpcsFile, $parenthesisOpenerPointer): void { $tokens = $phpcsFile->getTokens(); $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1); if (!in_array( $tokens[$pointerBeforeParenthesisOpener]['code'], [...TokenHelper::ONLY_NAME_TOKEN_CODES, T_STRING, T_VARIABLE, T_ISSET, T_UNSET, T_CLOSE_PARENTHESIS, T_SELF, T_STATIC, T_PARENT], true, )) { return; } $functionPointer = TokenHelper::findPreviousEffective($phpcsFile, $pointerBeforeParenthesisOpener - 1); if (in_array($tokens[$functionPointer]['code'], TokenHelper::FUNCTION_TOKEN_CODES, true)) { return; } $parenthesisCloserPointer = $tokens[$parenthesisOpenerPointer]['parenthesis_closer']; $pointerBeforeParenthesisCloser = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisCloserPointer - 1); if ($tokens[$pointerBeforeParenthesisCloser]['code'] !== T_COMMA) { return; } if ($this->onlySingleLine && $tokens[$parenthesisOpenerPointer]['line'] !== $tokens[$parenthesisCloserPointer]['line']) { return; } $fix = $phpcsFile->addFixableError( 'Trailing comma after the last parameter in function call is disallowed.', $pointerBeforeParenthesisCloser, self::CODE_DISALLOWED_TRAILING_COMMA, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $pointerBeforeParenthesisCloser, ''); if ($tokens[$pointerBeforeParenthesisCloser]['line'] === $tokens[$parenthesisCloserPointer]['line']) { FixerHelper::removeBetween($phpcsFile, $pointerBeforeParenthesisCloser, $parenthesisCloserPointer); } $phpcsFile->fixer->endChangeset(); } } PK41]~zVcoding-standard/SlevomatCodingStandard/Sniffs/Functions/DisallowArrowFunctionSniff.phpnu[ */ public function register(): array { return [ T_FN, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $arrowFunctionPointer */ public function process(File $phpcsFile, $arrowFunctionPointer): void { $phpcsFile->addError('Use of arrow function is disallowed.', $arrowFunctionPointer, self::CODE_DISALLOWED_ARROW_FUNCTION); } } PK41]]coding-standard/SlevomatCodingStandard/Sniffs/Functions/UselessParameterDefaultValueSniff.phpnu[ */ public function register(): array { return TokenHelper::FUNCTION_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $functionPointer */ public function process(File $phpcsFile, $functionPointer): void { $parameters = $phpcsFile->getMethodParameters($functionPointer); $parametersCount = count($parameters); if ($parametersCount === 0) { return; } for ($i = 0; $i < $parametersCount; $i++) { $parameter = $parameters[$i]; if (!array_key_exists('default', $parameter)) { continue; } $defaultValue = strtolower($parameter['default']); if ($defaultValue === 'null' && !$parameter['nullable_type']) { continue; } for ($j = $i + 1; $j < $parametersCount; $j++) { $nextParameter = $parameters[$j]; if (array_key_exists('default', $nextParameter)) { continue; } if ($nextParameter['variable_length']) { break; } $fix = $phpcsFile->addFixableError( sprintf('Useless default value of parameter %s.', $parameter['name']), $parameter['token'], self::CODE_USELESS_PARAMETER_DEFAULT_VALUE, ); if (!$fix) { continue; } $commaPointer = TokenHelper::findPrevious($phpcsFile, T_COMMA, $parameters[$i + 1]['token'] - 1); /** @var int $parameterPointer */ $parameterPointer = $parameter['token']; $phpcsFile->fixer->beginChangeset(); for ($k = $parameterPointer + 1; $k < $commaPointer; $k++) { FixerHelper::replace($phpcsFile, $k, ''); } $phpcsFile->fixer->endChangeset(); break; } } } } PK41]F0acoding-standard/SlevomatCodingStandard/Sniffs/Functions/RequireTrailingCommaInClosureUseSniff.phpnu[ */ public function register(): array { return [T_CLOSURE]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $functionPointer */ public function process(File $phpcsFile, $functionPointer): void { $this->enable = SniffSettingsHelper::isEnabledByPhpVersion($this->enable, 80000); if (!$this->enable) { return; } $tokens = $phpcsFile->getTokens(); $parenthesisCloserPointer = $tokens[$functionPointer]['parenthesis_closer']; $usePointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisCloserPointer + 1); if ($tokens[$usePointer]['code'] !== T_USE) { return; } $useParenthesisOpenerPointer = TokenHelper::findNextEffective($phpcsFile, $usePointer + 1); $useParenthesisCloserPointer = $tokens[$useParenthesisOpenerPointer]['parenthesis_closer']; if ($tokens[$useParenthesisOpenerPointer]['line'] === $tokens[$useParenthesisCloserPointer]['line']) { return; } $pointerBeforeUseParenthesisCloser = TokenHelper::findPreviousExcluding( $phpcsFile, T_WHITESPACE, $useParenthesisCloserPointer - 1, $useParenthesisOpenerPointer, ); if ($tokens[$pointerBeforeUseParenthesisCloser]['code'] === T_COMMA) { return; } $fix = $phpcsFile->addFixableError( 'Multi-line "use" of closure declaration must have a trailing comma after the last inherited variable.', $pointerBeforeUseParenthesisCloser, self::CODE_MISSING_TRAILING_COMMA, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::add($phpcsFile, $pointerBeforeUseParenthesisCloser, ','); $phpcsFile->fixer->endChangeset(); } } PK41]}cp!!Ucoding-standard/SlevomatCodingStandard/Sniffs/Functions/RequireMultiLineCallSniff.phpnu[minLineLength = SniffSettingsHelper::normalizeInteger($this->minLineLength); if (!$this->isCall($phpcsFile, $stringPointer)) { return; } $tokens = $phpcsFile->getTokens(); $parenthesisOpenerPointer = TokenHelper::findNextEffective($phpcsFile, $stringPointer + 1); $parenthesisCloserPointer = $tokens[$parenthesisOpenerPointer]['parenthesis_closer']; // No parameters $effectivePointerAfterParenthesisOpener = TokenHelper::findNextEffective($phpcsFile, $parenthesisOpenerPointer + 1); if ($effectivePointerAfterParenthesisOpener === $parenthesisCloserPointer) { return; } $parametersPointers = [TokenHelper::findNextEffective($phpcsFile, $parenthesisOpenerPointer + 1)]; $level = 0; $pointers = TokenHelper::findNextAll( $phpcsFile, [T_COMMA, T_OPEN_PARENTHESIS, T_CLOSE_PARENTHESIS, T_OPEN_SHORT_ARRAY, T_CLOSE_SHORT_ARRAY], $parenthesisOpenerPointer + 1, $parenthesisCloserPointer, ); foreach ($pointers as $pointer) { if (in_array($tokens[$pointer]['code'], [T_OPEN_PARENTHESIS, T_OPEN_SHORT_ARRAY], true)) { $level++; continue; } if (in_array($tokens[$pointer]['code'], [T_CLOSE_PARENTHESIS, T_CLOSE_SHORT_ARRAY], true)) { $level--; continue; } if ($level !== 0) { continue; } $parameterPointer = TokenHelper::findNextEffective($phpcsFile, $pointer + 1, $parenthesisCloserPointer); if ($parameterPointer !== null) { $parametersPointers[] = $parameterPointer; } } $lines = [ $tokens[$parenthesisOpenerPointer]['line'], $tokens[$parenthesisCloserPointer]['line'], ]; foreach ($parametersPointers as $parameterPointer) { $lines[] = $tokens[$parameterPointer]['line']; } // Each parameter on its line if (count(array_unique($lines)) - 2 >= count($parametersPointers)) { return; } if ($this->shouldBeSkipped($phpcsFile, $stringPointer, $parenthesisCloserPointer)) { return; } $lineStart = $this->getLineStart($phpcsFile, $parenthesisOpenerPointer); if ($tokens[$parenthesisCloserPointer]['line'] === $tokens[$stringPointer]['line']) { $call = $this->getCall($phpcsFile, $parenthesisOpenerPointer, $parenthesisCloserPointer); $lineEnd = $this->getLineEnd($phpcsFile, $parenthesisCloserPointer); $lineLength = strlen($lineStart . $call . $lineEnd); } else { $lineEnd = $this->getLineEnd($phpcsFile, $parenthesisOpenerPointer + 1); $lineLength = strlen($lineStart . $lineEnd); } $firstNonWhitespaceOnLine = TokenHelper::findFirstNonWhitespaceOnLine($phpcsFile, $stringPointer); $indentation = IndentationHelper::getIndentation($phpcsFile, $firstNonWhitespaceOnLine); $oneIndentation = IndentationHelper::getOneIndentationLevel($phpcsFile); if (!$this->shouldReportError( $lineLength, $lineStart, $lineEnd, count($parametersPointers), strlen($oneIndentation), )) { return; } $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $stringPointer - 1); $name = ltrim($tokens[$stringPointer]['content'], '\\'); if (in_array($tokens[$previousPointer]['code'], [T_OBJECT_OPERATOR, T_DOUBLE_COLON], true)) { $error = sprintf('Call of method %s() should be split to more lines.', $name); } elseif ($tokens[$previousPointer]['code'] === T_NEW) { $error = 'Constructor call should be split to more lines.'; } else { $error = sprintf('Call of function %s() should be split to more lines.', $name); } $fix = $phpcsFile->addFixableError($error, $stringPointer, self::CODE_REQUIRED_MULTI_LINE_CALL); if (!$fix) { return; } $parametersIndentation = IndentationHelper::addIndentation($phpcsFile, $indentation); $phpcsFile->fixer->beginChangeset(); for ($i = $parenthesisOpenerPointer + 1; $i < $parenthesisCloserPointer; $i++) { if (in_array($i, $parametersPointers, true)) { FixerHelper::removeWhitespaceBefore($phpcsFile, $i); FixerHelper::addBefore($phpcsFile, $i, $phpcsFile->eolChar . $parametersIndentation); } elseif ($tokens[$i]['content'] === $phpcsFile->eolChar) { FixerHelper::add($phpcsFile, $i, $oneIndentation); } else { // Create conflict so inner calls are fixed in next loop FixerHelper::replace($phpcsFile, $i, $tokens[$i]['content']); } } FixerHelper::addBefore($phpcsFile, $parenthesisCloserPointer, $phpcsFile->eolChar . $indentation); $phpcsFile->fixer->endChangeset(); } private function shouldBeSkipped(File $phpcsFile, int $stringPointer, int $parenthesisCloserPointer): bool { $tokens = $phpcsFile->getTokens(); $searchStartPointer = TokenHelper::findFirstNonWhitespaceOnLine($phpcsFile, $stringPointer); while (true) { $stringPointerBefore = TokenHelper::findNext( $phpcsFile, TokenHelper::ONLY_NAME_TOKEN_CODES, $searchStartPointer, $stringPointer, ); if ($stringPointerBefore === null) { break; } $pointerAfterStringPointerBefore = TokenHelper::findNextEffective($phpcsFile, $stringPointerBefore + 1); if ( $tokens[$pointerAfterStringPointerBefore]['code'] === T_OPEN_PARENTHESIS && $tokens[$pointerAfterStringPointerBefore]['parenthesis_closer'] > $stringPointer ) { return true; } $searchStartPointer = $stringPointerBefore + 1; } $lastPointerOnLine = TokenHelper::findLastTokenOnLine($phpcsFile, $parenthesisCloserPointer); $searchStartPointer = $parenthesisCloserPointer + 1; while (true) { $stringPointerAfter = TokenHelper::findNext( $phpcsFile, TokenHelper::ONLY_NAME_TOKEN_CODES, $searchStartPointer, $lastPointerOnLine + 1, ); if ($stringPointerAfter === null) { break; } $pointerAfterStringPointerAfter = TokenHelper::findNextEffective($phpcsFile, $stringPointerAfter + 1); if ( $pointerAfterStringPointerAfter !== null && $tokens[$pointerAfterStringPointerAfter]['code'] === T_OPEN_PARENTHESIS && $tokens[$tokens[$pointerAfterStringPointerAfter]['parenthesis_closer']]['line'] === $tokens[$stringPointer]['line'] && $tokens[$pointerAfterStringPointerAfter]['parenthesis_closer'] !== TokenHelper::findNextEffective( $phpcsFile, $pointerAfterStringPointerAfter + 1, ) ) { return true; } $searchStartPointer = $stringPointerAfter + 1; } return false; } private function shouldReportError( int $lineLength, string $lineStart, string $lineEnd, int $parametersCount, int $indentationLength ): bool { if ($this->minLineLength === 0) { return true; } if ($lineLength < $this->minLineLength) { return false; } if ($parametersCount > 1) { return true; } return strlen(trim($lineStart) . trim($lineEnd)) > $indentationLength; } } PK41]o Pcoding-standard/SlevomatCodingStandard/Sniffs/Functions/UnusedParameterSniff.phpnu[ */ public array $allowedParameterPatterns = []; /** * @return array */ public function register(): array { return TokenHelper::FUNCTION_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $functionPointer */ public function process(File $phpcsFile, $functionPointer): void { if (FunctionHelper::isAbstract($phpcsFile, $functionPointer)) { return; } $isSuppressed = SuppressHelper::isSniffSuppressed($phpcsFile, $functionPointer, $this->getSniffName(self::CODE_UNUSED_PARAMETER)); $suppressUseless = true; $tokens = $phpcsFile->getTokens(); $currentPointer = $tokens[$functionPointer]['parenthesis_opener'] + 1; while (true) { $parameterPointer = TokenHelper::findNext( $phpcsFile, T_VARIABLE, $currentPointer, $tokens[$functionPointer]['parenthesis_closer'], ); if ($parameterPointer === null) { break; } $previousPointer = TokenHelper::findPrevious( $phpcsFile, array_merge([T_COMMA], Tokens::$scopeModifiers), $parameterPointer - 1, $tokens[$functionPointer]['parenthesis_opener'], ); if ($previousPointer !== null && in_array($tokens[$previousPointer]['code'], Tokens::$scopeModifiers, true)) { $currentPointer = $parameterPointer + 1; continue; } if ( $this->variableIsSuppressedViaName($tokens[$parameterPointer]['content']) || VariableHelper::isUsedInScope($phpcsFile, $functionPointer, $parameterPointer) ) { $currentPointer = $parameterPointer + 1; continue; } if (!$isSuppressed) { $phpcsFile->addError( sprintf('Unused parameter %s.', $tokens[$parameterPointer]['content']), $parameterPointer, self::CODE_UNUSED_PARAMETER, ); } else { $suppressUseless = false; } $currentPointer = $parameterPointer + 1; } if (!$isSuppressed || !$suppressUseless) { return; } $phpcsFile->addError( sprintf('Useless %s %s', SuppressHelper::ANNOTATION, self::NAME), $functionPointer, self::CODE_USELESS_SUPPRESS, ); } private function getSniffName(string $sniffName): string { return sprintf('%s.%s', self::NAME, $sniffName); } private function variableIsSuppressedViaName(string $variableName): bool { foreach (SniffSettingsHelper::normalizeArray($this->allowedParameterPatterns) as $allowedParamPattern) { if (!SniffSettingsHelper::isValidRegularExpression($allowedParamPattern)) { throw new Exception(sprintf('%s is not valid PCRE pattern.', $allowedParamPattern)); } if (preg_match($allowedParamPattern, substr($variableName, 1)) === 1) { return true; } } return false; } } PK41]{433gcoding-standard/SlevomatCodingStandard/Sniffs/Functions/UnusedInheritedVariablePassedToClosureSniff.phpnu[ */ public function register(): array { return [ T_USE, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $usePointer */ public function process(File $phpcsFile, $usePointer): void { $tokens = $phpcsFile->getTokens(); /** @var int $parenthesisOpenerPointer */ $parenthesisOpenerPointer = TokenHelper::findNextEffective($phpcsFile, $usePointer + 1); if ($tokens[$parenthesisOpenerPointer]['code'] !== T_OPEN_PARENTHESIS) { return; } /** @var int $closurePointer */ $closurePointer = TokenHelper::findPrevious($phpcsFile, T_CLOSURE, $usePointer - 1); $currentPointer = $parenthesisOpenerPointer + 1; do { $variablePointer = TokenHelper::findNext( $phpcsFile, T_VARIABLE, $currentPointer, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'], ); if ($variablePointer === null) { break; } $this->checkVariableUsage( $phpcsFile, $usePointer, $parenthesisOpenerPointer, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'], $variablePointer, $closurePointer, ); $currentPointer = $variablePointer + 1; } while (true); } private function checkVariableUsage( File $phpcsFile, int $usePointer, int $useParenthesisOpenerPointer, int $useParenthesisCloserPointer, int $variablePointer, int $scopeOwnerPointer ): void { $tokens = $phpcsFile->getTokens(); if (VariableHelper::isUsedInScope($phpcsFile, $scopeOwnerPointer, $variablePointer)) { return; } $fix = $phpcsFile->addFixableError( sprintf('Unused inherited variable %s passed to closure.', $tokens[$variablePointer]['content']), $variablePointer, self::CODE_UNUSED_INHERITED_VARIABLE, ); if (!$fix) { return; } $fixStartPointer = $variablePointer; do { if ($tokens[$fixStartPointer - 1]['code'] === T_OPEN_PARENTHESIS) { break; } $fixStartPointer--; if ($tokens[$fixStartPointer]['code'] === T_COMMA) { break; } } while (true); $fixEndPointer = $variablePointer; do { if ($tokens[$fixEndPointer + 1]['code'] === T_CLOSE_PARENTHESIS) { break; } if ($tokens[$fixEndPointer + 1]['code'] === T_COMMA && $tokens[$fixStartPointer]['code'] === T_COMMA) { break; } if (in_array($tokens[$fixEndPointer + 1]['code'], [T_VARIABLE, T_BITWISE_AND], true)) { break; } $fixEndPointer++; } while (true); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $fixStartPointer, $fixEndPointer); $emptyUse = true; for ($i = $useParenthesisOpenerPointer + 1; $i < $useParenthesisCloserPointer; $i++) { if ($phpcsFile->fixer->getTokenContent($i) !== '') { $emptyUse = false; break; } } if ($emptyUse) { FixerHelper::removeBetweenIncluding($phpcsFile, $usePointer, $useParenthesisCloserPointer); } $phpcsFile->fixer->endChangeset(); } } PK41]`""Vcoding-standard/SlevomatCodingStandard/Sniffs/Functions/DisallowEmptyFunctionSniff.phpnu[ */ public function register(): array { return [T_FUNCTION]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $functionPointer */ public function process(File $phpcsFile, $functionPointer): void { $tokens = $phpcsFile->getTokens(); if (FunctionHelper::isAbstract($phpcsFile, $functionPointer)) { return; } if (FunctionHelper::getName($phpcsFile, $functionPointer) === '__construct') { $previousPointer = TokenHelper::findPrevious( $phpcsFile, [T_PRIVATE, T_SEMICOLON, T_CLOSE_CURLY_BRACKET, T_OPEN_CURLY_BRACKET], $functionPointer - 1, ); if ($previousPointer !== null && in_array($tokens[$previousPointer]['code'], [T_PRIVATE, T_PROTECTED], true)) { return; } $propertyPromotion = TokenHelper::findNext( $phpcsFile, Tokens::$scopeModifiers, $tokens[$functionPointer]['parenthesis_opener'] + 1, $tokens[$functionPointer]['parenthesis_closer'], ); if ($propertyPromotion !== null) { return; } } $firstContent = TokenHelper::findNextExcluding( $phpcsFile, T_WHITESPACE, $tokens[$functionPointer]['scope_opener'] + 1, $tokens[$functionPointer]['scope_closer'], ); if ($firstContent !== null) { return; } $phpcsFile->addError( 'Empty function body must have at least a comment to explain why is empty.', $functionPointer, self::CODE_EMPTY_FUNCTION, ); } } PK41](jUcoding-standard/SlevomatCodingStandard/Sniffs/Functions/NamedArgumentSpacingSniff.phpnu[ */ public function register(): array { return [ T_PARAM_NAME, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { $tokens = $phpcsFile->getTokens(); /** @var int $colonPointer */ $colonPointer = TokenHelper::findNext($phpcsFile, T_COLON, $pointer + 1); $parameterName = $tokens[$pointer]['content']; if ($colonPointer !== $pointer + 1) { $fix = $phpcsFile->addFixableError( sprintf('There must be no whitespace between named argument "%s" and colon.', $parameterName), $colonPointer, self::CODE_WHITESPACE_BEFORE_COLON, ); if ($fix) { FixerHelper::replace($phpcsFile, $colonPointer - 1, ''); } } $whitespacePointer = $colonPointer + 1; if ( $tokens[$whitespacePointer]['code'] === T_WHITESPACE && $tokens[$whitespacePointer]['content'] === ' ' ) { return; } $fix = $phpcsFile->addFixableError( sprintf('There must be exactly one space after colon in named argument "%s".', $parameterName), $colonPointer, self::CODE_NO_WHITESPACE_AFTER_COLON, ); if (!$fix) { return; } if ($tokens[$whitespacePointer]['code'] === T_WHITESPACE) { FixerHelper::replace($phpcsFile, $whitespacePointer, ' '); } else { FixerHelper::add($phpcsFile, $colonPointer, ' '); } } } PK41]dexxOcoding-standard/SlevomatCodingStandard/Sniffs/Functions/FunctionLengthSniff.phpnu[ */ public function register(): array { return [T_FUNCTION]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $functionPointer */ public function process(File $file, $functionPointer): void { $this->maxLinesLength = SniffSettingsHelper::normalizeInteger($this->maxLinesLength); $flags = array_keys(array_filter([ FunctionHelper::LINE_INCLUDE_COMMENT => $this->includeComments, FunctionHelper::LINE_INCLUDE_WHITESPACE => $this->includeWhitespace, ])); $flags = array_reduce($flags, static fn ($carry, $flag): int => $carry | $flag, 0); $length = FunctionHelper::getFunctionLengthInLines($file, $functionPointer, $flags); if ($length <= $this->maxLinesLength) { return; } $errorMessage = sprintf( 'Your function is too long. Currently using %d lines. Can be up to %d lines.', $length, $this->maxLinesLength, ); $file->addError($errorMessage, $functionPointer, self::CODE_FUNCTION_LENGTH); } } PK41]?7~~Vcoding-standard/SlevomatCodingStandard/Sniffs/Functions/RequireSingleLineCallSniff.phpnu[maxLineLength = SniffSettingsHelper::normalizeInteger($this->maxLineLength); if (!$this->isCall($phpcsFile, $stringPointer)) { return; } if ($this->shouldBeSkipped($phpcsFile, $stringPointer)) { return; } $tokens = $phpcsFile->getTokens(); $parenthesisOpenerPointer = TokenHelper::findNextEffective($phpcsFile, $stringPointer + 1); $parenthesisCloserPointer = $tokens[$parenthesisOpenerPointer]['parenthesis_closer']; if ($tokens[$parenthesisOpenerPointer]['line'] === $tokens[$parenthesisCloserPointer]['line']) { return; } if (TokenHelper::findNext( $phpcsFile, array_merge(TokenHelper::INLINE_COMMENT_TOKEN_CODES, Tokens::$heredocTokens), $parenthesisOpenerPointer + 1, $parenthesisCloserPointer, ) !== null) { return; } for ($i = $parenthesisOpenerPointer + 1; $i < $parenthesisCloserPointer; $i++) { if ($tokens[$i]['code'] !== T_CONSTANT_ENCAPSED_STRING && $tokens[$i]['code'] !== T_DOUBLE_QUOTED_STRING) { continue; } if (strpos($tokens[$i]['content'], $phpcsFile->eolChar) !== false) { return; } } if ($this->ignoreWithComplexParameter) { if ( TokenHelper::findNext( $phpcsFile, [T_CLOSURE, T_FN, T_OPEN_SHORT_ARRAY], $parenthesisOpenerPointer + 1, $parenthesisCloserPointer, ) !== null ) { return; } // Contains inner call $callSearchStartPointer = $parenthesisOpenerPointer + 1; while (true) { $innerStringPointer = TokenHelper::findNext( $phpcsFile, TokenHelper::ONLY_NAME_TOKEN_CODES, $callSearchStartPointer, $parenthesisCloserPointer, ); if ($innerStringPointer === null) { break; } $pointerAfterInnerString = TokenHelper::findNextEffective($phpcsFile, $innerStringPointer + 1); if ( $pointerAfterInnerString !== null && $tokens[$pointerAfterInnerString]['code'] === T_OPEN_PARENTHESIS ) { return; } $callSearchStartPointer = $innerStringPointer + 1; } } $lineStart = $this->getLineStart($phpcsFile, $parenthesisOpenerPointer); $call = $this->getCall($phpcsFile, $parenthesisOpenerPointer, $parenthesisCloserPointer); $lineEnd = $this->getLineEnd($phpcsFile, $parenthesisCloserPointer); $lineLength = strlen($lineStart . $call . $lineEnd); if (!$this->shouldReportError($lineLength)) { return; } $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $stringPointer - 1); $name = ltrim($tokens[$stringPointer]['content'], '\\'); if (in_array($tokens[$previousPointer]['code'], [T_OBJECT_OPERATOR, T_DOUBLE_COLON], true)) { $error = sprintf('Call of method %s() should be placed on a single line.', $name); } elseif ($tokens[$previousPointer]['code'] === T_NEW) { $error = 'Constructor call should be placed on a single line.'; } else { $error = sprintf('Call of function %s() should be placed on a single line.', $name); } $fix = $phpcsFile->addFixableError($error, $stringPointer, self::CODE_REQUIRED_SINGLE_LINE_CALL); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::add($phpcsFile, $parenthesisOpenerPointer, $call); FixerHelper::removeBetween($phpcsFile, $parenthesisOpenerPointer, $parenthesisCloserPointer); $phpcsFile->fixer->endChangeset(); } private function shouldBeSkipped(File $phpcsFile, int $stringPointer): bool { $tokens = $phpcsFile->getTokens(); foreach (array_reverse(TokenHelper::findNextAll($phpcsFile, [T_OPEN_PARENTHESIS, T_FUNCTION], 0, $stringPointer)) as $pointer) { if ($tokens[$pointer]['code'] === T_FUNCTION) { if (array_key_exists('scope_closer', $tokens[$pointer]) && $tokens[$pointer]['scope_closer'] > $stringPointer) { return false; } continue; } if ($tokens[$pointer]['parenthesis_closer'] < $stringPointer) { continue; } $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $pointer - 1); if ( $pointerBeforeParenthesisOpener === null || $tokens[$pointerBeforeParenthesisOpener]['code'] !== T_STRING ) { continue; } return true; } return false; } private function shouldReportError(int $lineLength): bool { if ($this->maxLineLength === 0) { return true; } return $lineLength <= $this->maxLineLength; } } PK41]Ɛ .P P bcoding-standard/SlevomatCodingStandard/Sniffs/Functions/DisallowTrailingCommaInClosureUseSniff.phpnu[ */ public function register(): array { return [ T_CLOSURE, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $functionPointer */ public function process(File $phpcsFile, $functionPointer): void { $tokens = $phpcsFile->getTokens(); $parenthesisCloserPointer = $tokens[$functionPointer]['parenthesis_closer']; $usePointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisCloserPointer + 1); if ($tokens[$usePointer]['code'] !== T_USE) { return; } $useParenthesisOpenerPointer = TokenHelper::findNextEffective($phpcsFile, $usePointer + 1); $useParenthesisCloserPointer = $tokens[$useParenthesisOpenerPointer]['parenthesis_closer']; $pointerBeforeUseParenthesisCloser = TokenHelper::findPreviousExcluding( $phpcsFile, T_WHITESPACE, $tokens[$useParenthesisOpenerPointer]['parenthesis_closer'] - 1, $useParenthesisOpenerPointer, ); if ($tokens[$pointerBeforeUseParenthesisCloser]['code'] !== T_COMMA) { return; } if ($this->onlySingleLine && $tokens[$useParenthesisOpenerPointer]['line'] !== $tokens[$useParenthesisCloserPointer]['line']) { return; } $fix = $phpcsFile->addFixableError( 'Trailing comma after the last inherited variable in "use" of closure declaration is disallowed.', $pointerBeforeUseParenthesisCloser, self::CODE_DISALLOWED_TRAILING_COMMA, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $pointerBeforeUseParenthesisCloser, ''); if ($tokens[$pointerBeforeUseParenthesisCloser]['line'] === $tokens[$useParenthesisCloserPointer]['line']) { FixerHelper::removeBetween($phpcsFile, $pointerBeforeUseParenthesisCloser, $useParenthesisCloserPointer); } $phpcsFile->fixer->endChangeset(); } } PK41]JтN N ccoding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/RequireSingleLineConditionSniff.phpnu[maxLineLength = SniffSettingsHelper::normalizeInteger($this->maxLineLength); if ($this->shouldBeSkipped($phpcsFile, $controlStructurePointer)) { return; } $tokens = $phpcsFile->getTokens(); $parenthesisOpenerPointer = $tokens[$controlStructurePointer]['parenthesis_opener']; $parenthesisCloserPointer = $tokens[$controlStructurePointer]['parenthesis_closer']; if ($tokens[$parenthesisOpenerPointer]['line'] === $tokens[$parenthesisCloserPointer]['line']) { return; } if (TokenHelper::findNext( $phpcsFile, TokenHelper::INLINE_COMMENT_TOKEN_CODES, $parenthesisOpenerPointer + 1, $parenthesisCloserPointer, ) !== null) { return; } $lineStart = $this->getLineStart($phpcsFile, $parenthesisOpenerPointer); $condition = $this->getCondition($phpcsFile, $parenthesisOpenerPointer, $parenthesisCloserPointer); $lineEnd = $this->getLineEnd($phpcsFile, $parenthesisCloserPointer); $lineLength = strlen($lineStart . $condition . $lineEnd); $isSimpleCondition = TokenHelper::findNext( $phpcsFile, Tokens::$booleanOperators, $parenthesisOpenerPointer + 1, $parenthesisCloserPointer, ) === null; if (!$this->shouldReportError($lineLength, $isSimpleCondition)) { return; } $fix = $phpcsFile->addFixableError( sprintf( 'Condition of "%s" should be placed on a single line.', $this->getControlStructureName($phpcsFile, $controlStructurePointer), ), $controlStructurePointer, self::CODE_REQUIRED_SINGLE_LINE_CONDITION, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::add($phpcsFile, $parenthesisOpenerPointer, $condition); FixerHelper::removeBetween($phpcsFile, $parenthesisOpenerPointer, $parenthesisCloserPointer); $phpcsFile->fixer->endChangeset(); } private function shouldReportError(int $lineLength, bool $isSimpleCondition): bool { if ($this->maxLineLength === 0) { return true; } if ($lineLength <= $this->maxLineLength) { return true; } return $isSimpleCondition && $this->alwaysForSimpleConditions; } } PK41]/!q66Rcoding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/EarlyExitSniff.phpnu[ */ public function register(): array { return [ T_IF, T_ELSEIF, T_ELSE, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { $tokens = $phpcsFile->getTokens(); if ($tokens[$pointer]['code'] === T_IF) { $this->processIf($phpcsFile, $pointer); } elseif ($tokens[$pointer]['code'] === T_ELSEIF) { $this->processElseIf($phpcsFile, $pointer); } else { $this->processElse($phpcsFile, $pointer); } } private function processElse(File $phpcsFile, int $elsePointer): void { $tokens = $phpcsFile->getTokens(); if (!array_key_exists('scope_opener', $tokens[$elsePointer])) { // Else without curly braces is not supported. return; } try { $allConditionsPointers = $this->getAllConditionsPointers($phpcsFile, $elsePointer); } catch (Throwable $e) { // Else without curly braces is not supported. return; } if (TokenHelper::findNext( $phpcsFile, T_FUNCTION, $tokens[$elsePointer]['scope_opener'] + 1, $tokens[$elsePointer]['scope_closer'], ) !== null) { return; } $ifPointer = $allConditionsPointers[0]; $ifEarlyExitPointer = null; $elseEarlyExitPointer = null; $previousConditionPointer = null; $previousConditionEarlyExitPointer = null; foreach ($allConditionsPointers as $conditionPointer) { $conditionEarlyExitPointer = $this->findEarlyExitInScope( $phpcsFile, $tokens[$conditionPointer]['scope_opener'], $tokens[$conditionPointer]['scope_closer'], ); if ($conditionPointer === $elsePointer) { $elseEarlyExitPointer = $conditionEarlyExitPointer; continue; } if (count($allConditionsPointers) > 2 && $conditionEarlyExitPointer === null) { return; } $previousConditionPointer = $conditionPointer; $previousConditionEarlyExitPointer = $conditionEarlyExitPointer; if ($conditionPointer === $ifPointer) { $ifEarlyExitPointer = $conditionEarlyExitPointer; continue; } } if ($ifEarlyExitPointer === null && $elseEarlyExitPointer === null) { return; } if ($elseEarlyExitPointer !== null && $previousConditionEarlyExitPointer === null) { $fix = $phpcsFile->addFixableError('Use early exit instead of "else".', $elsePointer, self::CODE_EARLY_EXIT_NOT_USED); if (!$fix) { return; } $ifCodePointers = $this->getScopeCodePointers($phpcsFile, $ifPointer); $elseCode = $this->getScopeCode($phpcsFile, $elsePointer); $negativeIfCondition = ConditionHelper::getNegativeCondition( $phpcsFile, $tokens[$ifPointer]['parenthesis_opener'], $tokens[$ifPointer]['parenthesis_closer'], ); $afterIfCode = IndentationHelper::removeIndentation( $phpcsFile, $ifCodePointers, IndentationHelper::getIndentation($phpcsFile, $ifPointer), ); $ifContent = sprintf('if %s {%s}%s%s', $negativeIfCondition, $elseCode, $phpcsFile->eolChar, $afterIfCode); $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $ifPointer, $tokens[$elsePointer]['scope_closer'], $ifContent); $phpcsFile->fixer->endChangeset(); return; } $fix = $phpcsFile->addFixableError('Remove useless "else" to reduce code nesting.', $elsePointer, self::CODE_USELESS_ELSE); if (!$fix) { return; } $elseCodePointers = $this->getScopeCodePointers($phpcsFile, $elsePointer); $afterIfCode = IndentationHelper::removeIndentation( $phpcsFile, $elseCodePointers, IndentationHelper::getIndentation($phpcsFile, $ifPointer), ); $phpcsFile->fixer->beginChangeset(); $previousConditionContent = sprintf('%s%s', $phpcsFile->eolChar, $afterIfCode); FixerHelper::change( $phpcsFile, $tokens[$previousConditionPointer]['scope_closer'] + 1, $tokens[$elsePointer]['scope_closer'], $previousConditionContent, ); $phpcsFile->fixer->endChangeset(); } private function processElseIf(File $phpcsFile, int $elseIfPointer): void { $tokens = $phpcsFile->getTokens(); try { $allConditionsPointers = $this->getAllConditionsPointers($phpcsFile, $elseIfPointer); } catch (Throwable $e) { // Elseif without curly braces is not supported. return; } if (TokenHelper::findNext( $phpcsFile, T_FUNCTION, $tokens[$elseIfPointer]['scope_opener'] + 1, $tokens[$elseIfPointer]['scope_closer'], ) !== null) { return; } foreach ($allConditionsPointers as $conditionPointer) { $conditionEarlyExitPointer = $this->findEarlyExitInScope( $phpcsFile, $tokens[$conditionPointer]['scope_opener'], $tokens[$conditionPointer]['scope_closer'], ); if ($conditionPointer === $elseIfPointer) { break; } if ($conditionEarlyExitPointer === null) { return; } } $fix = $phpcsFile->addFixableError('Use "if" instead of "elseif".', $elseIfPointer, self::CODE_USELESS_ELSEIF); if (!$fix) { return; } /** @var int $pointerBeforeElseIfPointer */ $pointerBeforeElseIfPointer = TokenHelper::findPreviousNonWhitespace($phpcsFile, $elseIfPointer - 1); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetween($phpcsFile, $pointerBeforeElseIfPointer, $elseIfPointer); $phpcsFile->fixer->addNewline($pointerBeforeElseIfPointer); $phpcsFile->fixer->addNewline($pointerBeforeElseIfPointer); FixerHelper::replace( $phpcsFile, $elseIfPointer, sprintf('%sif', IndentationHelper::getIndentation($phpcsFile, $allConditionsPointers[0])), ); $phpcsFile->fixer->endChangeset(); } private function processIf(File $phpcsFile, int $ifPointer): void { $tokens = $phpcsFile->getTokens(); if (!array_key_exists('scope_closer', $tokens[$ifPointer])) { // If without curly braces is not supported. return; } $nextPointer = TokenHelper::findNextNonWhitespace($phpcsFile, $tokens[$ifPointer]['scope_closer'] + 1); if ($nextPointer === null || $tokens[$nextPointer]['code'] !== T_CLOSE_CURLY_BRACKET) { return; } $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $ifPointer - 1); if ( $this->ignoreStandaloneIfInScope && in_array($tokens[$previousPointer]['code'], [T_OPEN_CURLY_BRACKET, T_COLON], true) ) { return; } if ( $this->ignoreOneLineTrailingIf && $tokens[$tokens[$ifPointer]['scope_opener']]['line'] + 2 === $tokens[$tokens[$ifPointer]['scope_closer']]['line'] ) { return; } if ($this->ignoreTrailingIfWithOneInstruction) { $pointerBeforeScopeCloser = TokenHelper::findPreviousEffective($phpcsFile, $tokens[$ifPointer]['scope_closer'] - 1); if ($tokens[$pointerBeforeScopeCloser]['code'] === T_SEMICOLON) { $ignore = true; $searchStartPointer = $tokens[$ifPointer]['scope_opener'] + 1; while (true) { $anotherSemicolonPointer = TokenHelper::findNext( $phpcsFile, T_SEMICOLON, $searchStartPointer, $pointerBeforeScopeCloser, ); if ($anotherSemicolonPointer === null) { break; } if (ScopeHelper::isInSameScope($phpcsFile, $anotherSemicolonPointer, $pointerBeforeScopeCloser)) { $ignore = false; break; } $searchStartPointer = $anotherSemicolonPointer + 1; } if ($ignore) { return; } } } $scopePointer = $tokens[$nextPointer]['scope_condition']; if (!in_array($tokens[$scopePointer]['code'], [T_FUNCTION, T_CLOSURE, T_WHILE, T_DO, T_FOREACH, T_FOR], true)) { return; } if ($this->isEarlyExitInScope($phpcsFile, $tokens[$ifPointer]['scope_opener'], $tokens[$ifPointer]['scope_closer'])) { return; } $fix = $phpcsFile->addFixableError('Use early exit to reduce code nesting.', $ifPointer, self::CODE_EARLY_EXIT_NOT_USED); if (!$fix) { return; } $ifCodePointers = $this->getScopeCodePointers($phpcsFile, $ifPointer); $ifIndentation = IndentationHelper::getIndentation($phpcsFile, $ifPointer); $earlyExitCode = $this->getEarlyExitCode($tokens[$scopePointer]['code']); $earlyExitCodeIndentation = IndentationHelper::addIndentation($phpcsFile, $ifIndentation); $negativeIfCondition = ConditionHelper::getNegativeCondition( $phpcsFile, $tokens[$ifPointer]['parenthesis_opener'], $tokens[$ifPointer]['parenthesis_closer'], ); $afterIfCode = IndentationHelper::removeIndentation($phpcsFile, $ifCodePointers, $ifIndentation); $ifContent = sprintf( 'if %s {%s%s%s;%s%s}%s%s', $negativeIfCondition, $phpcsFile->eolChar, $earlyExitCodeIndentation, $earlyExitCode, $phpcsFile->eolChar, $ifIndentation, $phpcsFile->eolChar, $afterIfCode, ); $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $ifPointer, $tokens[$ifPointer]['scope_closer'], $ifContent); $phpcsFile->fixer->endChangeset(); } private function getScopeCode(File $phpcsFile, int $scopePointer): string { $tokens = $phpcsFile->getTokens(); return TokenHelper::getContent($phpcsFile, $tokens[$scopePointer]['scope_opener'] + 1, $tokens[$scopePointer]['scope_closer'] - 1); } /** * @return list */ private function getScopeCodePointers(File $phpcsFile, int $scopePointer): array { $tokens = $phpcsFile->getTokens(); return range($tokens[$scopePointer]['scope_opener'] + 1, $tokens[$scopePointer]['scope_closer'] - 1); } /** * @param string|int $code */ private function getEarlyExitCode($code): string { if (in_array($code, [T_WHILE, T_DO, T_FOREACH, T_FOR], true)) { return 'continue'; } return 'return'; } private function findEarlyExitInScope(File $phpcsFile, int $startPointer, int $endPointer): ?int { $tokens = $phpcsFile->getTokens(); $ifPointers = TokenHelper::findNextAll($phpcsFile, T_IF, $startPointer + 1, $endPointer); foreach ($ifPointers as $ifPointer) { if ($tokens[$ifPointer]['level'] - 1 !== $tokens[$startPointer]['level']) { continue; } $conditionPointers = $this->getAllConditionsPointers($phpcsFile, $ifPointer); foreach ($conditionPointers as $conditionPointer) { if ($this->findEarlyExitInScope( $phpcsFile, $tokens[$conditionPointer]['scope_opener'], $tokens[$conditionPointer]['scope_closer'], ) === null) { return null; } } } $lastSemicolonInScopePointer = TokenHelper::findPreviousEffective($phpcsFile, $endPointer - 1, $startPointer); return $tokens[$lastSemicolonInScopePointer]['code'] === T_SEMICOLON ? TokenHelper::findPreviousLocal( $phpcsFile, TokenHelper::EARLY_EXIT_TOKEN_CODES, $lastSemicolonInScopePointer - 1, $startPointer, ) : null; } private function isEarlyExitInScope(File $phpcsFile, int $startPointer, int $endPointer): bool { return $this->findEarlyExitInScope($phpcsFile, $startPointer, $endPointer) !== null; } /** * @return list */ private function getAllConditionsPointers(File $phpcsFile, int $conditionPointer): array { $tokens = $phpcsFile->getTokens(); $conditionsPointers = [$conditionPointer]; if ( isset($tokens[$conditionPointer]['scope_opener']) && $tokens[$tokens[$conditionPointer]['scope_opener']]['code'] === T_COLON ) { // Alternative control structure syntax. throw new Exception(sprintf('"%s" without curly braces is not supported.', $tokens[$conditionPointer]['content'])); } if ($tokens[$conditionPointer]['code'] !== T_IF) { $currentConditionPointer = $conditionPointer; do { $previousConditionCloseParenthesisPointer = TokenHelper::findPreviousEffective($phpcsFile, $currentConditionPointer - 1); $currentConditionPointer = $tokens[$previousConditionCloseParenthesisPointer]['scope_condition']; $conditionsPointers[] = $currentConditionPointer; } while ($tokens[$currentConditionPointer]['code'] !== T_IF); } if ($tokens[$conditionPointer]['code'] !== T_ELSE) { if (!array_key_exists('scope_closer', $tokens[$conditionPointer])) { throw new Exception(sprintf('"%s" without curly braces is not supported.', $tokens[$conditionPointer]['content'])); } $currentConditionPointer = TokenHelper::findNextEffective($phpcsFile, $tokens[$conditionPointer]['scope_closer'] + 1); if ($currentConditionPointer !== null) { while (in_array($tokens[$currentConditionPointer]['code'], [T_ELSEIF, T_ELSE], true)) { $conditionsPointers[] = $currentConditionPointer; if (!array_key_exists('scope_closer', $tokens[$currentConditionPointer])) { throw new Exception( sprintf('"%s" without curly braces is not supported.', $tokens[$currentConditionPointer]['content']), ); } $currentConditionPointer = TokenHelper::findNextEffective( $phpcsFile, $tokens[$currentConditionPointer]['scope_closer'] + 1, ); } } } sort($conditionsPointers); return $conditionsPointers; } } PK41]fCk_coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/DisallowYodaComparisonSniff.phpnu[ (Foo::BAR, BAR) * > (true, false, null, 1, 1.0, arrays, 'foo') */ class DisallowYodaComparisonSniff implements Sniff { public const CODE_DISALLOWED_YODA_COMPARISON = 'DisallowedYodaComparison'; /** * @return array */ public function register(): array { return [ T_IS_IDENTICAL, T_IS_NOT_IDENTICAL, T_IS_EQUAL, T_IS_NOT_EQUAL, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $comparisonTokenPointer */ public function process(File $phpcsFile, $comparisonTokenPointer): void { $tokens = $phpcsFile->getTokens(); $leftSideTokens = YodaHelper::getLeftSideTokens($tokens, $comparisonTokenPointer); $rightSideTokens = YodaHelper::getRightSideTokens($tokens, $comparisonTokenPointer); $leftDynamism = YodaHelper::getDynamismForTokens($tokens, $leftSideTokens); $rightDynamism = YodaHelper::getDynamismForTokens($tokens, $rightSideTokens); if ($leftDynamism === null || $rightDynamism === null) { return; } if ($leftDynamism >= $rightDynamism) { return; } if ($leftDynamism >= 900 && $rightDynamism >= 900) { return; } $errorParameters = [ 'Yoda comparisons are disallowed.', $comparisonTokenPointer, self::CODE_DISALLOWED_YODA_COMPARISON, ]; $lastRightSideTokenPointer = array_keys($rightSideTokens)[count($rightSideTokens) - 1]; $nextPointer = TokenHelper::findNextEffective($phpcsFile, $lastRightSideTokenPointer + 1); if ($tokens[$nextPointer]['code'] === T_EQUAL) { $phpcsFile->addError(...$errorParameters); return; } $fix = $phpcsFile->addFixableError(...$errorParameters); if (!$fix) { return; } YodaHelper::fix($phpcsFile, $leftSideTokens, $rightSideTokens); } } PK41]n' _coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/UselessTernaryOperatorSniff.phpnu[ */ public function register(): array { return [ T_INLINE_THEN, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $inlineThenPointer */ public function process(File $phpcsFile, $inlineThenPointer): void { $tokens = $phpcsFile->getTokens(); $pointerAfterInlineThen = TokenHelper::findNextEffective($phpcsFile, $inlineThenPointer + 1); if ($tokens[$pointerAfterInlineThen]['code'] === T_INLINE_ELSE) { $inlineElsePointer = $pointerAfterInlineThen; } else { if (!in_array($tokens[$pointerAfterInlineThen]['code'], [T_TRUE, T_FALSE], true)) { return; } $inlineElsePointer = TokenHelper::findNextEffective($phpcsFile, $pointerAfterInlineThen + 1); if ($tokens[$inlineElsePointer]['code'] !== T_INLINE_ELSE) { return; } } $pointerAfterInlineElse = TokenHelper::findNextEffective($phpcsFile, $inlineElsePointer + 1); if (!in_array($tokens[$pointerAfterInlineElse]['code'], [T_TRUE, T_FALSE], true)) { return; } $conditionStartPointer = TernaryOperatorHelper::getStartPointer($phpcsFile, $inlineThenPointer); /** @var int $conditionEndPointer */ $conditionEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $inlineThenPointer - 1); $errorParameters = [ 'Useless ternary operator.', $inlineThenPointer, self::CODE_USELESS_TERNARY_OPERATOR, ]; if ( !$this->assumeAllConditionExpressionsAreAlreadyBoolean && !ConditionHelper::conditionReturnsBoolean($phpcsFile, $conditionStartPointer, $conditionEndPointer) ) { if ($tokens[$pointerAfterInlineThen]['code'] !== T_INLINE_ELSE) { $phpcsFile->addError(...$errorParameters); } return; } $fix = $phpcsFile->addFixableError(...$errorParameters); if (!$fix) { return; } $inlineElseEndPointer = TernaryOperatorHelper::getEndPointer($phpcsFile, $inlineThenPointer, $inlineElsePointer); $pointerAfterTernaryOperator = TokenHelper::findNextEffective($phpcsFile, $inlineElseEndPointer + 1); $phpcsFile->fixer->beginChangeset(); if ($tokens[$pointerAfterInlineThen]['code'] === T_FALSE) { $negativeCondition = ConditionHelper::getNegativeCondition($phpcsFile, $conditionStartPointer, $conditionEndPointer); FixerHelper::change($phpcsFile, $conditionStartPointer, $conditionEndPointer, $negativeCondition); } FixerHelper::removeBetween($phpcsFile, $conditionEndPointer, $pointerAfterTernaryOperator); $phpcsFile->fixer->endChangeset(); } } PK41]š ^coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/NewWithoutParenthesesSniff.phpnu[ */ public function register(): array { return [ T_NEW, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $newPointer */ public function process(File $phpcsFile, $newPointer): void { $tokens = $phpcsFile->getTokens(); /** @var int $nextPointer */ $nextPointer = TokenHelper::findNextEffective($phpcsFile, $newPointer + 1); if ($tokens[$nextPointer]['code'] === T_ANON_CLASS) { return; } $parenthesisOpenerPointer = $nextPointer + 1; do { /** @var int $parenthesisOpenerPointer */ $parenthesisOpenerPointer = TokenHelper::findNext( $phpcsFile, [ T_OPEN_PARENTHESIS, T_SEMICOLON, T_COMMA, T_INLINE_THEN, T_INLINE_ELSE, T_COALESCE, T_CLOSE_SHORT_ARRAY, T_CLOSE_SQUARE_BRACKET, T_CLOSE_PARENTHESIS, T_DOUBLE_ARROW, ], $parenthesisOpenerPointer, ); if ( $tokens[$parenthesisOpenerPointer]['code'] !== T_CLOSE_SQUARE_BRACKET || $tokens[$parenthesisOpenerPointer]['bracket_opener'] <= $newPointer ) { break; } $parenthesisOpenerPointer++; } while (true); if ($tokens[$parenthesisOpenerPointer]['code'] !== T_OPEN_PARENTHESIS) { return; } $nextPointer = TokenHelper::findNextNonWhitespace($phpcsFile, $parenthesisOpenerPointer + 1); if ($nextPointer !== $tokens[$parenthesisOpenerPointer]['parenthesis_closer']) { return; } $fix = $phpcsFile->addFixableError('Useless parentheses in "new".', $newPointer, self::CODE_USELESS_PARENTHESES); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding( $phpcsFile, $parenthesisOpenerPointer, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'], ); $phpcsFile->fixer->endChangeset(); } } PK41]F pp_coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/UnsupportedKeywordException.phpnu[ */ public function register(): array { return [ T_INLINE_THEN, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $inlineThenPointer */ public function process(File $phpcsFile, $inlineThenPointer): void { $tokens = $phpcsFile->getTokens(); $nextPointer = TokenHelper::findNextEffective($phpcsFile, $inlineThenPointer + 1); if ($tokens[$nextPointer]['code'] === T_INLINE_ELSE) { return; } if ($tokens[$inlineThenPointer]['line'] === $tokens[$nextPointer]['line']) { return; } $fix = $phpcsFile->addFixableError( 'Ternary operator should be reformatted as leading the line.', $inlineThenPointer, self::CODE_TRAILING_MULTI_LINE_TERNARY_OPERATOR_USED, ); if (!$fix) { return; } $inlineElsePointer = TernaryOperatorHelper::getElsePointer($phpcsFile, $inlineThenPointer); $pointerBeforeInlineThen = TokenHelper::findPreviousEffective($phpcsFile, $inlineThenPointer - 1); $pointerAfterInlineThen = TokenHelper::findNextExcluding($phpcsFile, [T_WHITESPACE], $inlineThenPointer + 1); $pointerBeforeInlineElse = TokenHelper::findPreviousEffective($phpcsFile, $inlineElsePointer - 1); $pointerAfterInlineElse = TokenHelper::findNextExcluding($phpcsFile, [T_WHITESPACE], $inlineElsePointer + 1); $indentation = IndentationHelper::addIndentation( $phpcsFile, IndentationHelper::getIndentation( $phpcsFile, TokenHelper::findFirstNonWhitespaceOnLine($phpcsFile, $inlineThenPointer), ), ); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetween($phpcsFile, $pointerBeforeInlineThen, $inlineThenPointer); FixerHelper::removeBetween($phpcsFile, $inlineThenPointer, $pointerAfterInlineThen); FixerHelper::addBefore($phpcsFile, $inlineThenPointer, $phpcsFile->eolChar . $indentation); FixerHelper::addBefore($phpcsFile, $pointerAfterInlineThen, ' '); FixerHelper::removeBetween($phpcsFile, $pointerBeforeInlineElse, $inlineElsePointer); FixerHelper::removeBetween($phpcsFile, $inlineElsePointer, $pointerAfterInlineElse); FixerHelper::addBefore($phpcsFile, $inlineElsePointer, $phpcsFile->eolChar . $indentation); FixerHelper::addBefore($phpcsFile, $pointerAfterInlineElse, ' '); $phpcsFile->fixer->endChangeset(); } } PK41]w,aP P icoding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/LanguageConstructWithParenthesesSniff.phpnu[ */ public function register(): array { return [ T_BREAK, T_CONTINUE, T_ECHO, T_EXIT, T_INCLUDE, T_INCLUDE_ONCE, T_PRINT, T_REQUIRE, T_REQUIRE_ONCE, T_RETURN, T_THROW, T_YIELD, T_YIELD_FROM, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $languageConstructPointer */ public function process(File $phpcsFile, $languageConstructPointer): void { $tokens = $phpcsFile->getTokens(); /** @var int $openParenthesisPointer */ $openParenthesisPointer = TokenHelper::findNextEffective($phpcsFile, $languageConstructPointer + 1); if ($tokens[$openParenthesisPointer]['code'] !== T_OPEN_PARENTHESIS) { return; } $closeParenthesisPointer = $tokens[$openParenthesisPointer]['parenthesis_closer']; $afterCloseParenthesisPointer = TokenHelper::findNextEffective($phpcsFile, $closeParenthesisPointer + 1); if (!in_array($tokens[$afterCloseParenthesisPointer]['code'], [T_SEMICOLON, T_CLOSE_PARENTHESIS, T_CLOSE_SHORT_ARRAY], true)) { return; } $containsContentBetweenParentheses = TokenHelper::findNextEffective( $phpcsFile, $openParenthesisPointer + 1, $closeParenthesisPointer, ) !== null; if ($tokens[$languageConstructPointer]['code'] === T_EXIT && $containsContentBetweenParentheses) { return; } $fix = $phpcsFile->addFixableError( sprintf('Usage of language construct "%s" with parentheses is disallowed.', $tokens[$languageConstructPointer]['content']), $languageConstructPointer, self::CODE_USED_WITH_PARENTHESES, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $openParenthesisPointer, ''); if ($tokens[$openParenthesisPointer - 1]['code'] !== T_WHITESPACE && $containsContentBetweenParentheses) { FixerHelper::add($phpcsFile, $openParenthesisPointer, ' '); } FixerHelper::replace($phpcsFile, $closeParenthesisPointer, ''); $phpcsFile->fixer->endChangeset(); } } PK41]f]EEccoding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/AbstractControlStructureSpacing.phpnu[|null */ private ?array $tokensToCheck = null; /** * @return list */ abstract protected function getSupportedKeywords(): array; /** * @return list */ abstract protected function getKeywordsToCheck(): array; abstract protected function getLinesCountBefore(): int; abstract protected function getLinesCountBeforeFirst(File $phpcsFile, int $controlStructurePointer): int; abstract protected function getLinesCountAfter(): int; abstract protected function getLinesCountAfterLast(File $phpcsFile, int $controlStructurePointer, int $controlStructureEndPointer): int; /** * @return array */ public function register(): array { return $this->getTokensToCheck(); } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $controlStructurePointer */ public function process(File $phpcsFile, $controlStructurePointer): void { $this->checkLinesBefore($phpcsFile, $controlStructurePointer); try { $this->checkLinesAfter($phpcsFile, $controlStructurePointer); } catch (Throwable $e) { // Unsupported syntax without curly braces. return; } } protected function checkLinesBefore(File $phpcsFile, int $controlStructurePointer): void { $tokens = $phpcsFile->getTokens(); if (in_array($tokens[$controlStructurePointer]['code'], [T_CASE, T_DEFAULT], true)) { $pointerBefore = TokenHelper::findPreviousEffective($phpcsFile, $controlStructurePointer - 1); if ($tokens[$pointerBefore]['code'] === T_COLON) { return; } } $nonWhitespacePointerBefore = TokenHelper::findPreviousNonWhitespace($phpcsFile, $controlStructurePointer - 1); $controlStructureStartPointer = $controlStructurePointer; $pointerBefore = $nonWhitespacePointerBefore; $pointerToCheckFirst = $pointerBefore; if (in_array($tokens[$nonWhitespacePointerBefore]['code'], Tokens::$commentTokens, true)) { $effectivePointerBefore = TokenHelper::findPreviousEffective($phpcsFile, $pointerBefore - 1); if ($tokens[$effectivePointerBefore]['line'] === $tokens[$nonWhitespacePointerBefore]['line']) { $pointerToCheckFirst = $effectivePointerBefore; } elseif ($tokens[$nonWhitespacePointerBefore]['line'] + 1 === $tokens[$controlStructurePointer]['line']) { if ($tokens[$effectivePointerBefore]['line'] !== $tokens[$nonWhitespacePointerBefore]['line']) { $controlStructureStartPointer = array_key_exists('comment_opener', $tokens[$nonWhitespacePointerBefore]) ? $tokens[$nonWhitespacePointerBefore]['comment_opener'] : CommentHelper::getMultilineCommentStartPointer($phpcsFile, $nonWhitespacePointerBefore); $pointerBefore = TokenHelper::findPreviousNonWhitespace($phpcsFile, $controlStructureStartPointer - 1); } $pointerToCheckFirst = $pointerBefore; } } $isFirstControlStructure = in_array($tokens[$pointerToCheckFirst]['code'], [T_OPEN_CURLY_BRACKET, T_COLON], true); $whitespaceBefore = ''; if ($tokens[$pointerBefore]['code'] === T_OPEN_TAG) { $whitespaceBefore .= substr($tokens[$pointerBefore]['content'], strlen('eolChar)) === $phpcsFile->eolChar; if ($hasCommentWithLineEndBefore) { $whitespaceBefore .= $phpcsFile->eolChar; } if ($pointerBefore + 1 !== $controlStructurePointer) { $whitespaceBefore .= TokenHelper::getContent($phpcsFile, $pointerBefore + 1, $controlStructureStartPointer - 1); } $requiredLinesCountBefore = $isFirstControlStructure ? $this->getLinesCountBeforeFirst($phpcsFile, $controlStructurePointer) : $this->getLinesCountBefore(); $actualLinesCountBefore = substr_count($whitespaceBefore, $phpcsFile->eolChar) - 1; if ($requiredLinesCountBefore === $actualLinesCountBefore) { return; } $fix = $phpcsFile->addFixableError( sprintf( 'Expected %d line%s before "%s", found %d.', $requiredLinesCountBefore, $requiredLinesCountBefore === 1 ? '' : 's', $tokens[$controlStructurePointer]['content'], $actualLinesCountBefore, ), $controlStructurePointer, $isFirstControlStructure ? self::CODE_INCORRECT_LINES_COUNT_BEFORE_FIRST_CONTROL_STRUCTURE : self::CODE_INCORRECT_LINES_COUNT_BEFORE_CONTROL_STRUCTURE, ); if (!$fix) { return; } $endOfLineBeforePointer = TokenHelper::findPreviousContent( $phpcsFile, T_WHITESPACE, $phpcsFile->eolChar, $controlStructureStartPointer - 1, ); $phpcsFile->fixer->beginChangeset(); if ($tokens[$pointerBefore]['code'] === T_OPEN_TAG) { FixerHelper::replace($phpcsFile, $pointerBefore, 'fixer->addNewline($pointerBefore); } $phpcsFile->fixer->endChangeset(); } protected function checkLinesAfter(File $phpcsFile, int $controlStructurePointer): void { $tokens = $phpcsFile->getTokens(); if (in_array($tokens[$controlStructurePointer]['code'], [T_CASE, T_DEFAULT], true)) { $colonPointer = TokenHelper::findNext($phpcsFile, T_COLON, $controlStructurePointer + 1); $pointerAfterColon = TokenHelper::findNextEffective($phpcsFile, $colonPointer + 1); if (in_array($tokens[$pointerAfterColon]['code'], [T_CASE, T_DEFAULT], true)) { return; } } $controlStructureEndPointer = $this->findControlStructureEnd($phpcsFile, $controlStructurePointer); $pointerAfterControlStructureEnd = TokenHelper::findNextEffective($phpcsFile, $controlStructureEndPointer + 1); if ( $pointerAfterControlStructureEnd !== null && $tokens[$pointerAfterControlStructureEnd]['code'] === T_SEMICOLON ) { $controlStructureEndPointer = $pointerAfterControlStructureEnd; } $notWhitespacePointerAfter = TokenHelper::findNextNonWhitespace($phpcsFile, $controlStructureEndPointer + 1); if ($notWhitespacePointerAfter === null) { return; } $hasCommentAfter = in_array($tokens[$notWhitespacePointerAfter]['code'], Tokens::$commentTokens, true); $isCommentAfterOnSameLine = false; $pointerAfter = $notWhitespacePointerAfter; $isControlStructureEndAfterPointer = static fn (int $pointer): bool => in_array( $tokens[$controlStructurePointer]['code'], [T_CASE, T_DEFAULT], true, ) ? $tokens[$pointer]['code'] === T_CLOSE_CURLY_BRACKET : in_array($tokens[$pointer]['code'], [T_CLOSE_CURLY_BRACKET, T_CASE, T_DEFAULT], true); if ($hasCommentAfter) { if ($tokens[$notWhitespacePointerAfter]['line'] === $tokens[$controlStructureEndPointer]['line'] + 1) { $commentEndPointer = CommentHelper::getCommentEndPointer($phpcsFile, $notWhitespacePointerAfter); $pointerAfterComment = TokenHelper::findNextNonWhitespace($phpcsFile, $commentEndPointer + 1); if ($isControlStructureEndAfterPointer($pointerAfterComment)) { $controlStructureEndPointer = $commentEndPointer; $pointerAfter = $pointerAfterComment; } } elseif ($tokens[$notWhitespacePointerAfter]['line'] === $tokens[$controlStructureEndPointer]['line']) { $isCommentAfterOnSameLine = true; $pointerAfter = TokenHelper::findNextNonWhitespace($phpcsFile, $notWhitespacePointerAfter + 1); } } $isLastControlStructure = $isControlStructureEndAfterPointer($pointerAfter); $requiredLinesCountAfter = $isLastControlStructure ? $this->getLinesCountAfterLast($phpcsFile, $controlStructurePointer, $controlStructureEndPointer) : $this->getLinesCountAfter(); $actualLinesCountAfter = $tokens[$pointerAfter]['line'] - $tokens[$controlStructureEndPointer]['line'] - 1; if ($requiredLinesCountAfter === $actualLinesCountAfter) { return; } $fix = $phpcsFile->addFixableError( sprintf( 'Expected %d line%s after "%s", found %d.', $requiredLinesCountAfter, $requiredLinesCountAfter === 1 ? '' : 's', $tokens[$controlStructurePointer]['content'], $actualLinesCountAfter, ), $controlStructurePointer, $isLastControlStructure ? self::CODE_INCORRECT_LINES_COUNT_AFTER_LAST_CONTROL_STRUCTURE : self::CODE_INCORRECT_LINES_COUNT_AFTER_CONTROL_STRUCTURE, ); if (!$fix) { return; } $replaceStartPointer = $isCommentAfterOnSameLine ? $notWhitespacePointerAfter : $controlStructureEndPointer; $endOfLineBeforeAfterPointer = TokenHelper::findLastTokenOnPreviousLine($phpcsFile, $pointerAfter); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $replaceStartPointer + 1, $endOfLineBeforeAfterPointer); if ($isCommentAfterOnSameLine) { for ($i = 0; $i < $requiredLinesCountAfter; $i++) { $phpcsFile->fixer->addNewline($notWhitespacePointerAfter); } } else { $linesToAdd = substr($tokens[$controlStructureEndPointer]['content'], -strlen($phpcsFile->eolChar)) === $phpcsFile->eolChar ? $requiredLinesCountAfter - 1 : $requiredLinesCountAfter; for ($i = 0; $i <= $linesToAdd; $i++) { $phpcsFile->fixer->addNewline($controlStructureEndPointer); } } $phpcsFile->fixer->endChangeset(); } /** * @return array */ private function getTokensToCheck(): array { if ($this->tokensToCheck === null) { $supportedKeywords = $this->getSupportedKeywords(); $supportedTokens = [ self::KEYWORD_IF => T_IF, self::KEYWORD_DO => T_DO, self::KEYWORD_WHILE => T_WHILE, self::KEYWORD_FOR => T_FOR, self::KEYWORD_FOREACH => T_FOREACH, self::KEYWORD_SWITCH => T_SWITCH, self::KEYWORD_CASE => T_CASE, self::KEYWORD_DEFAULT => T_DEFAULT, self::KEYWORD_TRY => T_TRY, self::KEYWORD_PARENT => T_PARENT, self::KEYWORD_GOTO => T_GOTO, self::KEYWORD_BREAK => T_BREAK, self::KEYWORD_CONTINUE => T_CONTINUE, self::KEYWORD_RETURN => T_RETURN, self::KEYWORD_THROW => T_THROW, self::KEYWORD_YIELD => T_YIELD, self::KEYWORD_YIELD_FROM => T_YIELD_FROM, ]; $this->tokensToCheck = array_map( static function (string $keyword) use ($supportedKeywords, $supportedTokens) { if (!in_array($keyword, $supportedKeywords, true)) { throw new UnsupportedKeywordException($keyword); } return $supportedTokens[$keyword]; }, SniffSettingsHelper::normalizeArray($this->getKeywordsToCheck()), ); if (count($this->tokensToCheck) === 0) { $this->tokensToCheck = array_map(static fn (string $keyword) => $supportedTokens[$keyword], $supportedKeywords); } } return $this->tokensToCheck; } private function findControlStructureEnd(File $phpcsFile, int $controlStructurePointer): int { $tokens = $phpcsFile->getTokens(); if ($tokens[$controlStructurePointer]['code'] === T_IF) { if (!array_key_exists('scope_closer', $tokens[$controlStructurePointer])) { throw new Exception('"if" without curly braces is not supported.'); } $pointerAfterParenthesisCloser = TokenHelper::findNextEffective( $phpcsFile, $tokens[$controlStructurePointer]['parenthesis_closer'] + 1, ); if ($pointerAfterParenthesisCloser !== null && $tokens[$pointerAfterParenthesisCloser]['code'] === T_COLON) { throw new Exception('"if" without curly braces is not supported.'); } $controlStructureEndPointer = $tokens[$controlStructurePointer]['scope_closer']; do { $nextPointer = TokenHelper::findNextEffective($phpcsFile, $controlStructureEndPointer + 1); if ($nextPointer === null) { return $controlStructureEndPointer; } if ($tokens[$nextPointer]['code'] === T_ELSE) { if (!array_key_exists('scope_closer', $tokens[$nextPointer])) { throw new Exception('"else" without curly braces is not supported.'); } return $tokens[$nextPointer]['scope_closer']; } if ($tokens[$nextPointer]['code'] !== T_ELSEIF) { return $controlStructureEndPointer; } $controlStructureEndPointer = $tokens[$nextPointer]['scope_closer']; } while (true); } if ($tokens[$controlStructurePointer]['code'] === T_DO) { $whilePointer = TokenHelper::findNext($phpcsFile, T_WHILE, $tokens[$controlStructurePointer]['scope_closer'] + 1); return (int) TokenHelper::findNext($phpcsFile, T_SEMICOLON, $tokens[$whilePointer]['parenthesis_closer'] + 1); } if ($tokens[$controlStructurePointer]['code'] === T_TRY) { $controlStructureEndPointer = $tokens[$controlStructurePointer]['scope_closer']; do { $nextPointer = TokenHelper::findNextEffective($phpcsFile, $controlStructureEndPointer + 1); if ($nextPointer === null) { return $controlStructureEndPointer; } if (!in_array($tokens[$nextPointer]['code'], [T_CATCH, T_FINALLY], true)) { return $controlStructureEndPointer; } $controlStructureEndPointer = $tokens[$nextPointer]['scope_closer']; } while (true); } if (in_array($tokens[$controlStructurePointer]['code'], [T_WHILE, T_FOR, T_FOREACH, T_SWITCH], true)) { return $tokens[$controlStructurePointer]['scope_closer']; } if (in_array($tokens[$controlStructurePointer]['code'], [T_CASE, T_DEFAULT], true)) { $switchPointer = TokenHelper::findPrevious($phpcsFile, T_SWITCH, $controlStructurePointer - 1); $pointers = TokenHelper::findNextAll( $phpcsFile, [T_CASE, T_DEFAULT], $controlStructurePointer + 1, $tokens[$switchPointer]['scope_closer'], ); foreach ($pointers as $pointer) { if (TokenHelper::findPrevious($phpcsFile, T_SWITCH, $pointer - 1) === $switchPointer) { $pointerBeforeCaseOrDefault = TokenHelper::findPreviousNonWhitespace($phpcsFile, $pointer - 1); if ( in_array($tokens[$pointerBeforeCaseOrDefault]['code'], Tokens::$commentTokens, true) && $tokens[$pointerBeforeCaseOrDefault]['line'] + 1 === $tokens[$pointer]['line'] ) { $pointerBeforeCaseOrDefault = TokenHelper::findPreviousExcluding( $phpcsFile, T_WHITESPACE, $pointerBeforeCaseOrDefault - 1, ); } return $pointerBeforeCaseOrDefault; } } return TokenHelper::findPreviousNonWhitespace($phpcsFile, $tokens[$switchPointer]['scope_closer'] - 1); } $nextPointer = TokenHelper::findNext( $phpcsFile, [T_SEMICOLON, T_ANON_CLASS, T_CLOSURE, T_FN, T_OPEN_SHORT_ARRAY], $controlStructurePointer + 1, ); if ($tokens[$nextPointer]['code'] === T_SEMICOLON) { return $nextPointer; } $scopeCloserPointer = $tokens[$nextPointer]['code'] === T_OPEN_SHORT_ARRAY ? $tokens[$nextPointer]['bracket_closer'] : $tokens[$nextPointer]['scope_closer']; if ($tokens[$scopeCloserPointer]['code'] === T_SEMICOLON) { return $scopeCloserPointer; } $nextPointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $scopeCloserPointer + 1); $level = $tokens[$controlStructurePointer]['level']; while ($level !== $tokens[$nextPointer]['level']) { $nextPointer = (int) TokenHelper::findNext($phpcsFile, T_SEMICOLON, $nextPointer + 1); } return $nextPointer; } } PK41][H[[ecoding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/DisallowShortTernaryOperatorSniff.phpnu[ */ public function register(): array { return [ T_INLINE_THEN, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $inlineThenPointer */ public function process(File $phpcsFile, $inlineThenPointer): void { $tokens = $phpcsFile->getTokens(); $nextPointer = TokenHelper::findNextEffective($phpcsFile, $inlineThenPointer + 1); if ($tokens[$nextPointer]['code'] !== T_INLINE_ELSE) { return; } $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $inlineThenPointer - 1); $message = 'Use of short ternary operator is disallowed.'; if ($tokens[$previousPointer]['code'] !== T_VARIABLE) { $phpcsFile->addError($message, $inlineThenPointer, self::CODE_DISALLOWED_SHORT_TERNARY_OPERATOR); return; } if (!$this->fixable) { $phpcsFile->addError($message, $inlineThenPointer, self::CODE_DISALLOWED_SHORT_TERNARY_OPERATOR); return; } $fix = $phpcsFile->addFixableError($message, $inlineThenPointer, self::CODE_DISALLOWED_SHORT_TERNARY_OPERATOR); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::add( $phpcsFile, $inlineThenPointer, sprintf(' %s ', $tokens[$previousPointer]['content']), ); $phpcsFile->fixer->endChangeset(); } } PK41]i^coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/JumpStatementsSpacingSniff.phpnu[ */ public array $jumpStatements = []; /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $jumpStatementPointer */ public function process(File $phpcsFile, $jumpStatementPointer): void { $this->linesCountBefore = SniffSettingsHelper::normalizeInteger($this->linesCountBefore); $this->linesCountBeforeFirst = SniffSettingsHelper::normalizeInteger($this->linesCountBeforeFirst); $this->linesCountBeforeWhenFirstInCaseOrDefault = SniffSettingsHelper::normalizeNullableInteger( $this->linesCountBeforeWhenFirstInCaseOrDefault, ); $this->linesCountAfter = SniffSettingsHelper::normalizeInteger($this->linesCountAfter); $this->linesCountAfterLast = SniffSettingsHelper::normalizeInteger($this->linesCountAfterLast); $this->linesCountAfterWhenLastInCaseOrDefault = SniffSettingsHelper::normalizeNullableInteger( $this->linesCountAfterWhenLastInCaseOrDefault, ); $this->linesCountAfterWhenLastInLastCaseOrDefault = SniffSettingsHelper::normalizeNullableInteger( $this->linesCountAfterWhenLastInLastCaseOrDefault, ); if ($this->isOneOfYieldSpecialCases($phpcsFile, $jumpStatementPointer)) { return; } parent::process($phpcsFile, $jumpStatementPointer); } /** * @return list */ protected function getSupportedKeywords(): array { return [ self::KEYWORD_GOTO, self::KEYWORD_BREAK, self::KEYWORD_CONTINUE, self::KEYWORD_RETURN, self::KEYWORD_THROW, self::KEYWORD_YIELD, self::KEYWORD_YIELD_FROM, ]; } /** * @return list */ protected function getKeywordsToCheck(): array { return $this->jumpStatements; } protected function getLinesCountBefore(): int { return $this->linesCountBefore; } protected function getLinesCountBeforeFirst(File $phpcsFile, int $jumpStatementPointer): int { if ( $this->linesCountBeforeWhenFirstInCaseOrDefault !== null && $this->isFirstInCaseOrDefault($phpcsFile, $jumpStatementPointer) ) { return $this->linesCountBeforeWhenFirstInCaseOrDefault; } return $this->linesCountBeforeFirst; } protected function getLinesCountAfter(): int { return $this->linesCountAfter; } /** * @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter */ protected function getLinesCountAfterLast(File $phpcsFile, int $jumpStatementPointer, int $jumpStatementEndPointer): int { if ( $this->linesCountAfterWhenLastInLastCaseOrDefault !== null && $this->isLastInLastCaseOrDefault($phpcsFile, $jumpStatementEndPointer) ) { return $this->linesCountAfterWhenLastInLastCaseOrDefault; } if ( $this->linesCountAfterWhenLastInCaseOrDefault !== null && $this->isLastInCaseOrDefault($phpcsFile, $jumpStatementEndPointer) ) { return $this->linesCountAfterWhenLastInCaseOrDefault; } return $this->linesCountAfterLast; } protected function checkLinesBefore(File $phpcsFile, int $jumpStatementPointer): void { if ( $this->allowSingleLineYieldStacking && $this->isStackedSingleLineYield($phpcsFile, $jumpStatementPointer, true) ) { return; } if ($this->isThrowExpression($phpcsFile, $jumpStatementPointer)) { return; } parent::checkLinesBefore($phpcsFile, $jumpStatementPointer); } protected function checkLinesAfter(File $phpcsFile, int $jumpStatementPointer): void { if ( $this->allowSingleLineYieldStacking && $this->isStackedSingleLineYield($phpcsFile, $jumpStatementPointer, false) ) { return; } if ($this->isThrowExpression($phpcsFile, $jumpStatementPointer)) { return; } parent::checkLinesAfter($phpcsFile, $jumpStatementPointer); } private function isOneOfYieldSpecialCases(File $phpcsFile, int $jumpStatementPointer): bool { $tokens = $phpcsFile->getTokens(); $jumpStatementToken = $tokens[$jumpStatementPointer]; if ($jumpStatementToken['code'] !== T_YIELD && $jumpStatementToken['code'] !== T_YIELD_FROM) { return false; } // check if yield is used inside parentheses (function call, while, ...) if (array_key_exists('nested_parenthesis', $jumpStatementToken)) { return true; } $pointerBefore = TokenHelper::findPreviousEffective($phpcsFile, $jumpStatementPointer - 1); // check if yield is used in assignment if (in_array($tokens[$pointerBefore]['code'], Tokens::$assignmentTokens, true)) { return true; } // check if yield is used in a return statement return $tokens[$pointerBefore]['code'] === T_RETURN; } private function isStackedSingleLineYield(File $phpcsFile, int $jumpStatementPointer, bool $previous): bool { $tokens = $phpcsFile->getTokens(); $yields = [T_YIELD, T_YIELD_FROM]; if (!in_array($tokens[$jumpStatementPointer]['code'], $yields, true)) { return false; } $adjoiningYieldPointer = $previous ? TokenHelper::findPrevious($phpcsFile, $yields, $jumpStatementPointer - 1) : TokenHelper::findNext($phpcsFile, $yields, $jumpStatementPointer + 1); return $adjoiningYieldPointer !== null && abs($tokens[$adjoiningYieldPointer]['line'] - $tokens[$jumpStatementPointer]['line']) === 1; } private function isThrowExpression(File $phpcsFile, int $jumpStatementPointer): bool { $tokens = $phpcsFile->getTokens(); if ($tokens[$jumpStatementPointer]['code'] !== T_THROW) { return false; } $pointerBefore = TokenHelper::findPreviousEffective($phpcsFile, $jumpStatementPointer - 1); return !in_array( $tokens[$pointerBefore]['code'], [T_SEMICOLON, T_COLON, T_OPEN_CURLY_BRACKET, T_CLOSE_CURLY_BRACKET, T_OPEN_TAG], true, ); } private function isFirstInCaseOrDefault(File $phpcsFile, int $jumpStatementPointer): bool { $tokens = $phpcsFile->getTokens(); $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $jumpStatementPointer - 1); if ($tokens[$previousPointer]['code'] !== T_COLON) { return false; } $firstPointerOnLine = TokenHelper::findFirstNonWhitespaceOnLine($phpcsFile, $previousPointer); return in_array($tokens[$firstPointerOnLine]['code'], [T_CASE, T_DEFAULT], true); } private function isLastInCaseOrDefault(File $phpcsFile, int $jumpStatementEndPointer): bool { $tokens = $phpcsFile->getTokens(); $nextPointer = TokenHelper::findNextEffective($phpcsFile, $jumpStatementEndPointer + 1); if (in_array($tokens[$nextPointer]['code'], [T_CASE, T_DEFAULT], true)) { return true; } return $tokens[$nextPointer]['code'] === T_CLOSE_CURLY_BRACKET && array_key_exists('scope_condition', $tokens[$nextPointer]) && $tokens[$tokens[$nextPointer]['scope_condition']]['code'] === T_SWITCH; } private function isLastInLastCaseOrDefault(File $phpcsFile, int $jumpStatementEndPointer): bool { if (!$this->isLastInCaseOrDefault($phpcsFile, $jumpStatementEndPointer)) { return false; } $nextPointer = TokenHelper::findNextEffective($phpcsFile, $jumpStatementEndPointer + 1); return !in_array($phpcsFile->getTokens()[$nextPointer]['code'], [T_CASE, T_DEFAULT], true); } } PK41]W3a#a#_coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/RequireTernaryOperatorSniff.phpnu[ */ public function register(): array { return [ T_IF, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $ifPointer */ public function process(File $phpcsFile, $ifPointer): void { $tokens = $phpcsFile->getTokens(); if (!array_key_exists('scope_closer', $tokens[$ifPointer])) { // If without curly braces is not supported. return; } $elsePointer = TokenHelper::findNextEffective($phpcsFile, $tokens[$ifPointer]['scope_closer'] + 1); if ($elsePointer === null || $tokens[$elsePointer]['code'] !== T_ELSE) { return; } if (!array_key_exists('scope_closer', $tokens[$elsePointer])) { // Else without curly braces is not supported. return; } if ( !$this->isCompatibleScope($phpcsFile, $tokens[$ifPointer]['scope_opener'], $tokens[$ifPointer]['scope_closer']) || !$this->isCompatibleScope($phpcsFile, $tokens[$elsePointer]['scope_opener'], $tokens[$elsePointer]['scope_closer']) ) { return; } /** @var int $firstPointerInIf */ $firstPointerInIf = TokenHelper::findNextEffective($phpcsFile, $tokens[$ifPointer]['scope_opener'] + 1); /** @var int $firstPointerInElse */ $firstPointerInElse = TokenHelper::findNextEffective($phpcsFile, $tokens[$elsePointer]['scope_opener'] + 1); if ($tokens[$firstPointerInIf]['code'] === T_RETURN && $tokens[$firstPointerInElse]['code'] === T_RETURN) { $this->checkIfWithReturns($phpcsFile, $ifPointer, $elsePointer, $firstPointerInIf, $firstPointerInElse); return; } $this->checkIfWithAssignments($phpcsFile, $ifPointer, $elsePointer, $firstPointerInIf, $firstPointerInElse); } private function checkIfWithReturns(File $phpcsFile, int $ifPointer, int $elsePointer, int $returnInIf, int $returnInElse): void { $ifContainsComment = $this->containsComment($phpcsFile, $ifPointer); $elseContainsComment = $this->containsComment($phpcsFile, $elsePointer); $conditionContainsLogicalOperators = $this->containsLogicalOperators($phpcsFile, $ifPointer); $errorParameters = [ 'Use ternary operator.', $ifPointer, self::CODE_TERNARY_OPERATOR_NOT_USED, ]; if ($ifContainsComment || $elseContainsComment || $conditionContainsLogicalOperators) { $phpcsFile->addError(...$errorParameters); return; } $fix = $phpcsFile->addFixableError(...$errorParameters); if (!$fix) { return; } $tokens = $phpcsFile->getTokens(); $pointerAfterReturnInIf = TokenHelper::findNextEffective($phpcsFile, $returnInIf + 1); /** @var int $semicolonAfterReturnInIf */ $semicolonAfterReturnInIf = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $pointerAfterReturnInIf + 1); $pointerAfterReturnInElse = TokenHelper::findNextEffective($phpcsFile, $returnInElse + 1); $semicolonAfterReturnInElse = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $pointerAfterReturnInElse + 1); $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $ifPointer, 'return'); if ($ifPointer + 1 === $tokens[$ifPointer]['parenthesis_opener']) { FixerHelper::add($phpcsFile, $ifPointer, ' '); } FixerHelper::replace($phpcsFile, $tokens[$ifPointer]['parenthesis_opener'], ''); FixerHelper::replace($phpcsFile, $tokens[$ifPointer]['parenthesis_closer'], ' ? '); FixerHelper::removeBetween($phpcsFile, $tokens[$ifPointer]['parenthesis_closer'], $pointerAfterReturnInIf); FixerHelper::change($phpcsFile, $semicolonAfterReturnInIf, $pointerAfterReturnInElse - 1, ' : '); FixerHelper::removeBetweenIncluding($phpcsFile, $semicolonAfterReturnInElse + 1, $tokens[$elsePointer]['scope_closer']); $phpcsFile->fixer->endChangeset(); } private function checkIfWithAssignments( File $phpcsFile, int $ifPointer, int $elsePointer, int $firstPointerInIf, int $firstPointerInElse ): void { $tokens = $phpcsFile->getTokens(); $identificatorEndPointerInIf = IdentificatorHelper::findEndPointer($phpcsFile, $firstPointerInIf); $identificatorEndPointerInElse = IdentificatorHelper::findEndPointer($phpcsFile, $firstPointerInElse); if ($identificatorEndPointerInIf === null || $identificatorEndPointerInElse === null) { return; } $identificatorInIf = TokenHelper::getContent($phpcsFile, $firstPointerInIf, $identificatorEndPointerInIf); $identificatorInElse = TokenHelper::getContent($phpcsFile, $firstPointerInElse, $identificatorEndPointerInElse); if ($identificatorInIf !== $identificatorInElse) { return; } $assignmentPointerInIf = TokenHelper::findNextEffective($phpcsFile, $identificatorEndPointerInIf + 1); $assignmentPointerInElse = TokenHelper::findNextEffective($phpcsFile, $identificatorEndPointerInElse + 1); if ( $tokens[$assignmentPointerInIf]['code'] !== T_EQUAL || $tokens[$assignmentPointerInElse]['code'] !== T_EQUAL ) { return; } $pointerAfterAssignmentInIf = TokenHelper::findNextEffective($phpcsFile, $assignmentPointerInIf + 1); $pointerAfterAssignmentInElse = TokenHelper::findNextEffective($phpcsFile, $assignmentPointerInElse + 1); if ( $tokens[$pointerAfterAssignmentInIf]['code'] === T_BITWISE_AND || $tokens[$pointerAfterAssignmentInElse]['code'] === T_BITWISE_AND ) { return; } $ifContainsComment = $this->containsComment($phpcsFile, $ifPointer); $elseContainsComment = $this->containsComment($phpcsFile, $elsePointer); $conditionContainsLogicalOperators = $this->containsLogicalOperators($phpcsFile, $ifPointer); $errorParameters = [ 'Use ternary operator.', $ifPointer, self::CODE_TERNARY_OPERATOR_NOT_USED, ]; if ($ifContainsComment || $elseContainsComment || $conditionContainsLogicalOperators) { $phpcsFile->addError(...$errorParameters); return; } $fix = $phpcsFile->addFixableError(...$errorParameters); if (!$fix) { return; } /** @var int $semicolonAfterAssignmentInIf */ $semicolonAfterAssignmentInIf = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $pointerAfterAssignmentInIf + 1); $semicolonAfterAssignmentInElse = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $pointerAfterAssignmentInElse + 1); $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $ifPointer, $tokens[$ifPointer]['parenthesis_opener'], sprintf('%s = ', $identificatorInIf)); FixerHelper::change($phpcsFile, $tokens[$ifPointer]['parenthesis_closer'], $pointerAfterAssignmentInIf - 1, ' ? '); FixerHelper::change($phpcsFile, $semicolonAfterAssignmentInIf, $pointerAfterAssignmentInElse - 1, ' : '); FixerHelper::removeBetweenIncluding($phpcsFile, $semicolonAfterAssignmentInElse + 1, $tokens[$elsePointer]['scope_closer']); $phpcsFile->fixer->endChangeset(); } private function isCompatibleScope(File $phpcsFile, int $scopeOpenerPointer, int $scopeCloserPointer): bool { $semicolonPointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $scopeOpenerPointer + 1, $scopeCloserPointer); if ($semicolonPointer === null) { return false; } if (TokenHelper::findNext($phpcsFile, T_INLINE_THEN, $scopeOpenerPointer + 1, $semicolonPointer) !== null) { return false; } if ($this->ignoreMultiLine) { $firstContentPointer = TokenHelper::findNextEffective($phpcsFile, $scopeOpenerPointer + 1); if (TokenHelper::findNextContent( $phpcsFile, T_WHITESPACE, $phpcsFile->eolChar, $firstContentPointer + 1, $semicolonPointer, ) !== null) { return false; } } $pointerAfterSemicolon = TokenHelper::findNextEffective($phpcsFile, $semicolonPointer + 1); return $pointerAfterSemicolon === $scopeCloserPointer; } private function containsComment(File $phpcsFile, int $scopeOwnerPointer): bool { $tokens = $phpcsFile->getTokens(); return TokenHelper::findNext( $phpcsFile, Tokens::$commentTokens, $tokens[$scopeOwnerPointer]['scope_opener'] + 1, $tokens[$scopeOwnerPointer]['scope_closer'], ) !== null; } private function containsLogicalOperators(File $phpcsFile, int $scopeOwnerPointer): bool { $tokens = $phpcsFile->getTokens(); return TokenHelper::findNext( $phpcsFile, [T_LOGICAL_AND, T_LOGICAL_OR, T_LOGICAL_XOR], $tokens[$scopeOwnerPointer]['parenthesis_opener'] + 1, $tokens[$scopeOwnerPointer]['parenthesis_closer'], ) !== null; } } PK41]D ^coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/AssignmentInConditionSniff.phpnu[ */ public function register(): array { return [ T_IF, T_ELSEIF, T_DO, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $conditionStartPointer */ public function process(File $phpcsFile, $conditionStartPointer): void { $tokens = $phpcsFile->getTokens(); $token = $tokens[$conditionStartPointer]; if ($token['code'] === T_DO) { $whilePointer = TokenHelper::findNext($phpcsFile, T_WHILE, $token['scope_closer'] + 1); $whileToken = $tokens[$whilePointer]; $parenthesisOpener = $whileToken['parenthesis_opener']; $parenthesisCloser = $whileToken['parenthesis_closer']; $type = 'do-while'; } else { $parenthesisOpener = $token['parenthesis_opener']; $parenthesisCloser = $token['parenthesis_closer']; $type = $token['code'] === T_IF ? 'if' : 'elseif'; } if ( $parenthesisOpener === null || $parenthesisCloser === null ) { return; } $this->processCondition($phpcsFile, $parenthesisOpener, $parenthesisCloser, $type); } private function processCondition(File $phpcsFile, int $parenthesisOpener, int $parenthesisCloser, string $conditionType): void { $equalsTokenPointers = TokenHelper::findNextAll($phpcsFile, T_EQUAL, $parenthesisOpener + 1, $parenthesisCloser); if ($equalsTokenPointers === []) { return; } if (!$this->ignoreAssignmentsInsideFunctionCalls) { $this->error($phpcsFile, $conditionType, $equalsTokenPointers[0]); return; } $tokens = $phpcsFile->getTokens(); foreach ($equalsTokenPointers as $equalsTokenPointer) { /** @var non-empty-list $parenthesisStarts */ $parenthesisStarts = array_keys($tokens[$equalsTokenPointer]['nested_parenthesis']); $insideParenthesis = max($parenthesisStarts); if ($insideParenthesis === $parenthesisOpener) { $this->error($phpcsFile, $conditionType, $equalsTokenPointer); continue; } $functionCall = TokenHelper::findPrevious( $phpcsFile, TokenHelper::ONLY_NAME_TOKEN_CODES, $insideParenthesis, $parenthesisOpener, ); if ($functionCall !== null) { continue; } $this->error($phpcsFile, $conditionType, $equalsTokenPointer); } } private function error(File $phpcsFile, string $conditionType, int $equalsTokenPointer): void { $phpcsFile->addError( sprintf('Assignment in %s condition is not allowed.', $conditionType), $equalsTokenPointer, self::CODE_ASSIGNMENT_IN_CONDITION, ); } } PK41]0))ecoding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/UselessIfConditionWithReturnSniff.phpnu[ */ public function register(): array { return [ T_IF, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $ifPointer */ public function process(File $phpcsFile, $ifPointer): void { $tokens = $phpcsFile->getTokens(); if (!array_key_exists('scope_closer', $tokens[$ifPointer])) { // If without curly braces is not supported. return; } $ifBooleanPointer = $this->findBooleanAfterReturnInScope($phpcsFile, $tokens[$ifPointer]['scope_opener']); if ($ifBooleanPointer === null) { return; } $newCondition = static fn (): string => strtolower($tokens[$ifBooleanPointer]['content']) === 'true' ? TokenHelper::getContent( $phpcsFile, $tokens[$ifPointer]['parenthesis_opener'] + 1, $tokens[$ifPointer]['parenthesis_closer'] - 1, ) : ConditionHelper::getNegativeCondition( $phpcsFile, $tokens[$ifPointer]['parenthesis_opener'] + 1, $tokens[$ifPointer]['parenthesis_closer'] - 1, ); $elsePointer = TokenHelper::findNextEffective($phpcsFile, $tokens[$ifPointer]['scope_closer'] + 1); $errorParameters = [ 'Useless condition.', $ifPointer, self::CODE_USELESS_IF_CONDITION, ]; if ( $elsePointer !== null && $tokens[$elsePointer]['code'] === T_ELSE ) { if (!array_key_exists('scope_closer', $tokens[$elsePointer])) { // Else without curly braces is not supported. return; } $elseBooleanPointer = $this->findBooleanAfterReturnInScope($phpcsFile, $tokens[$elsePointer]['scope_opener']); if ($elseBooleanPointer === null) { return; } if (!$this->isFixable($phpcsFile, $ifPointer, $tokens[$elsePointer]['scope_closer'])) { $phpcsFile->addError(...$errorParameters); return; } $fix = $phpcsFile->addFixableError(...$errorParameters); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $ifPointer, $tokens[$elsePointer]['scope_closer'], sprintf('return %s;', $newCondition())); $phpcsFile->fixer->endChangeset(); } else { $returnPointer = TokenHelper::findNextEffective($phpcsFile, $tokens[$ifPointer]['scope_closer'] + 1); if ($returnPointer === null) { return; } if ($tokens[$returnPointer]['code'] !== T_RETURN) { return; } $semicolonPointer = $this->findSemicolonAfterReturnWithBoolean($phpcsFile, $returnPointer); if ($semicolonPointer === null) { return; } if (!$this->isFixable($phpcsFile, $ifPointer, $semicolonPointer)) { $phpcsFile->addError(...$errorParameters); return; } $fix = $phpcsFile->addFixableError(...$errorParameters); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $ifPointer, $semicolonPointer, sprintf('return %s;', $newCondition())); $phpcsFile->fixer->endChangeset(); } } private function isFixable(File $phpcsFile, int $ifPointer, int $endPointer): bool { $tokens = $phpcsFile->getTokens(); if (TokenHelper::findNext($phpcsFile, Tokens::$commentTokens, $ifPointer + 1, $endPointer) !== null) { return false; } if ($this->assumeAllConditionExpressionsAreAlreadyBoolean) { return true; } return ConditionHelper::conditionReturnsBoolean( $phpcsFile, $tokens[$ifPointer]['parenthesis_opener'] + 1, $tokens[$ifPointer]['parenthesis_closer'] - 1, ); } private function findBooleanAfterReturnInScope(File $phpcsFile, int $scopeOpenerPointer): ?int { $tokens = $phpcsFile->getTokens(); /** @var int $returnPointer */ $returnPointer = TokenHelper::findNextEffective($phpcsFile, $scopeOpenerPointer + 1); if ($tokens[$returnPointer]['code'] !== T_RETURN) { return null; } $booleanPointer = $this->findBooleanAfterReturn($phpcsFile, $returnPointer); if ($booleanPointer === null) { return null; } $semicolonPointer = TokenHelper::findNextEffective($phpcsFile, $booleanPointer + 1); if ($tokens[$semicolonPointer]['code'] !== T_SEMICOLON) { return null; } return $booleanPointer; } private function findBooleanAfterReturn(File $phpcsFile, int $returnPointer): ?int { $tokens = $phpcsFile->getTokens(); $booleanPointer = TokenHelper::findNextEffective($phpcsFile, $returnPointer + 1); if (in_array($tokens[$booleanPointer]['code'], [T_TRUE, T_FALSE], true)) { return $booleanPointer; } return null; } private function findSemicolonAfterReturnWithBoolean(File $phpcsFile, int $returnPointer): ?int { $tokens = $phpcsFile->getTokens(); $booleanPointer = $this->findBooleanAfterReturn($phpcsFile, $returnPointer); if ($booleanPointer === null) { return null; } $semicolonPointer = TokenHelper::findNextEffective($phpcsFile, $booleanPointer + 1); if ($tokens[$semicolonPointer]['code'] !== T_SEMICOLON) { return null; } return $semicolonPointer; } } PK41]3X[6 ecoding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/BlockControlStructureSpacingSniff.phpnu[ */ public array $controlStructures = []; /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $controlStructurePointer */ public function process(File $phpcsFile, $controlStructurePointer): void { $this->linesCountBefore = SniffSettingsHelper::normalizeInteger($this->linesCountBefore); $this->linesCountBeforeFirst = SniffSettingsHelper::normalizeInteger($this->linesCountBeforeFirst); $this->linesCountAfter = SniffSettingsHelper::normalizeInteger($this->linesCountAfter); $this->linesCountAfterLast = SniffSettingsHelper::normalizeInteger($this->linesCountAfterLast); if ($this->isWhilePartOfDo($phpcsFile, $controlStructurePointer)) { return; } parent::process($phpcsFile, $controlStructurePointer); } /** * @return list */ protected function getSupportedKeywords(): array { return [ self::KEYWORD_IF, self::KEYWORD_DO, self::KEYWORD_WHILE, self::KEYWORD_FOR, self::KEYWORD_FOREACH, self::KEYWORD_SWITCH, self::KEYWORD_TRY, self::KEYWORD_CASE, self::KEYWORD_DEFAULT, ]; } /** * @return list */ protected function getKeywordsToCheck(): array { return $this->controlStructures; } protected function getLinesCountBefore(): int { return $this->linesCountBefore; } /** * @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter */ protected function getLinesCountBeforeFirst(File $phpcsFile, int $controlStructurePointer): int { return $this->linesCountBeforeFirst; } protected function getLinesCountAfter(): int { return $this->linesCountAfter; } /** * @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter */ protected function getLinesCountAfterLast(File $phpcsFile, int $controlStructurePointer, int $controlStructureEndPointer): int { return $this->linesCountAfterLast; } private function isWhilePartOfDo(File $phpcsFile, int $controlStructurePointer): bool { $tokens = $phpcsFile->getTokens(); $pointerBefore = TokenHelper::findPreviousEffective($phpcsFile, $controlStructurePointer - 1); return $tokens[$controlStructurePointer]['code'] === T_WHILE && $tokens[$pointerBefore]['code'] === T_CLOSE_CURLY_BRACKET && array_key_exists('scope_condition', $tokens[$pointerBefore]) && $tokens[$tokens[$pointerBefore]['scope_condition']]['code'] === T_DO; } } PK41]bcoding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/RequireMultiLineConditionSniff.phpnu[minLineLength = SniffSettingsHelper::normalizeInteger($this->minLineLength); if ($this->shouldBeSkipped($phpcsFile, $controlStructurePointer)) { return; } $tokens = $phpcsFile->getTokens(); $parenthesisOpenerPointer = $tokens[$controlStructurePointer]['parenthesis_opener']; $parenthesisCloserPointer = $tokens[$controlStructurePointer]['parenthesis_closer']; $booleanOperatorPointers = TokenHelper::findNextAll( $phpcsFile, Tokens::$booleanOperators, $parenthesisOpenerPointer + 1, $parenthesisCloserPointer, ); if ($booleanOperatorPointers === []) { return; } $conditionStartPointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisOpenerPointer + 1); $conditionEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisCloserPointer - 1); $conditionStartsOnNewLine = $tokens[$parenthesisOpenerPointer]['line'] !== $tokens[$conditionStartPointer]['line']; $conditionEndsOnNewLine = $tokens[$parenthesisCloserPointer]['line'] !== $tokens[$conditionEndPointer]['line']; $lineStart = $this->getLineStart($phpcsFile, $conditionStartsOnNewLine ? $conditionStartPointer - 1 : $parenthesisOpenerPointer); $lineEnd = $this->getLineEnd($phpcsFile, $conditionEndsOnNewLine ? $conditionEndPointer + 1 : $parenthesisCloserPointer); $condition = $this->getCondition($phpcsFile, $parenthesisOpenerPointer, $parenthesisCloserPointer); $lineLength = strlen($lineStart . $condition . $lineEnd); $conditionLinesCount = $tokens[$conditionEndPointer]['line'] - $tokens[$conditionStartPointer]['line'] + 1; if (!$this->shouldReportError($lineLength, $conditionLinesCount, count($booleanOperatorPointers))) { return; } $fix = $phpcsFile->addFixableError( sprintf( 'Condition of "%s" should be split to more lines so each condition part is on its own line.', $this->getControlStructureName($phpcsFile, $controlStructurePointer), ), $controlStructurePointer, self::CODE_REQUIRED_MULTI_LINE_CONDITION, ); if (!$fix) { return; } $controlStructureIndentation = IndentationHelper::getIndentation( $phpcsFile, $conditionStartsOnNewLine ? $conditionStartPointer : TokenHelper::findFirstNonWhitespaceOnLine($phpcsFile, $parenthesisOpenerPointer), ); $conditionIndentation = $conditionStartsOnNewLine ? $controlStructureIndentation : IndentationHelper::addIndentation($phpcsFile, $controlStructureIndentation); $innerConditionLevel = 0; $phpcsFile->fixer->beginChangeset(); if (!$conditionStartsOnNewLine) { FixerHelper::removeWhitespaceBefore($phpcsFile, $conditionStartPointer); FixerHelper::addBefore($phpcsFile, $conditionStartPointer, $phpcsFile->eolChar . $conditionIndentation); } for ($i = $conditionStartPointer; $i <= $conditionEndPointer; $i++) { if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS) { $containsBooleanOperator = TokenHelper::findNext( $phpcsFile, Tokens::$booleanOperators, $i + 1, $tokens[$i]['parenthesis_closer'], ) !== null; $innerConditionLevel++; if ($containsBooleanOperator) { FixerHelper::removeWhitespaceAfter($phpcsFile, $i); FixerHelper::add( $phpcsFile, $i, $phpcsFile->eolChar . IndentationHelper::addIndentation($phpcsFile, $conditionIndentation, $innerConditionLevel), ); FixerHelper::removeWhitespaceBefore($phpcsFile, $tokens[$i]['parenthesis_closer']); FixerHelper::addBefore( $phpcsFile, $tokens[$i]['parenthesis_closer'], $phpcsFile->eolChar . IndentationHelper::addIndentation( $phpcsFile, $conditionIndentation, $innerConditionLevel - 1, ), ); } continue; } if ($tokens[$i]['code'] === T_CLOSE_PARENTHESIS) { $innerConditionLevel--; continue; } if (!in_array($tokens[$i]['code'], Tokens::$booleanOperators, true)) { continue; } $innerConditionIndentation = $conditionIndentation; if ($innerConditionLevel > 0) { $innerConditionIndentation = IndentationHelper::addIndentation( $phpcsFile, $innerConditionIndentation, $innerConditionLevel, ); } if ($this->booleanOperatorOnPreviousLine) { FixerHelper::add($phpcsFile, $i, $phpcsFile->eolChar . $innerConditionIndentation); FixerHelper::removeWhitespaceAfter($phpcsFile, $i); continue; } FixerHelper::removeWhitespaceBefore($phpcsFile, $i); FixerHelper::addBefore($phpcsFile, $i, $phpcsFile->eolChar . $innerConditionIndentation); } if (!$conditionEndsOnNewLine) { FixerHelper::removeWhitespaceAfter($phpcsFile, $conditionEndPointer); FixerHelper::add($phpcsFile, $conditionEndPointer, $phpcsFile->eolChar . $controlStructureIndentation); } $phpcsFile->fixer->endChangeset(); } private function shouldReportError(int $lineLength, int $conditionLinesCount, int $booleanOperatorPointersCount): bool { if ($conditionLinesCount === 1) { return $this->minLineLength === 0 || $lineLength >= $this->minLineLength; } return $this->alwaysSplitAllConditionParts ? $conditionLinesCount < $booleanOperatorPointersCount + 1 : false; } } PK41]*^coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/RequireYodaComparisonSniff.phpnu[ (Foo::BAR, BAR) * > (true, false, null, 1, 1.0, arrays, 'foo') */ class RequireYodaComparisonSniff implements Sniff { public const CODE_REQUIRED_YODA_COMPARISON = 'RequiredYodaComparison'; public bool $alwaysVariableOnRight = false; /** * @return array */ public function register(): array { return [ T_IS_IDENTICAL, T_IS_NOT_IDENTICAL, T_IS_EQUAL, T_IS_NOT_EQUAL, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $comparisonTokenPointer */ public function process(File $phpcsFile, $comparisonTokenPointer): void { $tokens = $phpcsFile->getTokens(); $leftSideTokens = YodaHelper::getLeftSideTokens($tokens, $comparisonTokenPointer); $rightSideTokens = YodaHelper::getRightSideTokens($tokens, $comparisonTokenPointer); $leftDynamism = YodaHelper::getDynamismForTokens($tokens, $leftSideTokens); $rightDynamism = YodaHelper::getDynamismForTokens($tokens, $rightSideTokens); if ($leftDynamism === null || $rightDynamism === null) { return; } if ($leftDynamism <= $rightDynamism) { return; } if (!$this->alwaysVariableOnRight && $leftDynamism >= 900 && $rightDynamism >= 900) { return; } $fix = $phpcsFile->addFixableError('Yoda comparison is required.', $comparisonTokenPointer, self::CODE_REQUIRED_YODA_COMPARISON); if (!$fix || count($leftSideTokens) === 0 || count($rightSideTokens) === 0) { return; } YodaHelper::fix($phpcsFile, $leftSideTokens, $rightSideTokens); } } PK41]X [coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/NewWithParenthesesSniff.phpnu[ */ public function register(): array { return [ T_NEW, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $newPointer */ public function process(File $phpcsFile, $newPointer): void { $tokens = $phpcsFile->getTokens(); /** @var int $nextPointer */ $nextPointer = TokenHelper::findNextEffective($phpcsFile, $newPointer + 1); if ($tokens[$nextPointer]['code'] === T_ATTRIBUTE) { $nextPointer = AttributeHelper::getAttributeTarget($phpcsFile, $nextPointer); } if ($tokens[$nextPointer]['code'] === T_ANON_CLASS || $tokens[$nextPointer]['code'] === T_READONLY) { return; } if ($tokens[$nextPointer]['code'] === T_OPEN_PARENTHESIS) { $nextPointer = $tokens[$nextPointer]['parenthesis_closer']; } $shouldBeOpenParenthesisPointer = $nextPointer + 1; do { $shouldBeOpenParenthesisPointer = TokenHelper::findNext( $phpcsFile, [ T_OPEN_PARENTHESIS, T_SEMICOLON, T_COMMA, T_INLINE_THEN, T_INLINE_ELSE, T_COALESCE, T_CLOSE_SHORT_ARRAY, T_CLOSE_SQUARE_BRACKET, T_CLOSE_PARENTHESIS, T_DOUBLE_ARROW, ], $shouldBeOpenParenthesisPointer, ); if ( $shouldBeOpenParenthesisPointer === null || $tokens[$shouldBeOpenParenthesisPointer]['code'] !== T_CLOSE_SQUARE_BRACKET || $tokens[$shouldBeOpenParenthesisPointer]['bracket_opener'] <= $newPointer ) { break; } $shouldBeOpenParenthesisPointer++; } while (true); if ( $shouldBeOpenParenthesisPointer !== null && $tokens[$shouldBeOpenParenthesisPointer]['code'] === T_OPEN_PARENTHESIS ) { return; } $fix = $phpcsFile->addFixableError( 'Usage of "new" without parentheses is disallowed.', $newPointer, self::CODE_MISSING_PARENTHESES, ); if (!$fix) { return; } /** @var int $classNameEndPointer */ $classNameEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $shouldBeOpenParenthesisPointer - 1); $phpcsFile->fixer->beginChangeset(); FixerHelper::add($phpcsFile, $classNameEndPointer, '()'); $phpcsFile->fixer->endChangeset(); } } PK41]]hcoding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/RequireMultiLineTernaryOperatorSniff.phpnu[ */ public function register(): array { return [ T_INLINE_THEN, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $inlineThenPointer */ public function process(File $phpcsFile, $inlineThenPointer): void { $this->lineLengthLimit = SniffSettingsHelper::normalizeInteger($this->lineLengthLimit); $this->minExpressionsLength = SniffSettingsHelper::normalizeNullableInteger($this->minExpressionsLength); $tokens = $phpcsFile->getTokens(); $nextPointer = TokenHelper::findNextEffective($phpcsFile, $inlineThenPointer + 1); if ($tokens[$nextPointer]['code'] === T_INLINE_ELSE) { return; } $inlineElsePointer = TernaryOperatorHelper::getElsePointer($phpcsFile, $inlineThenPointer); if ($tokens[$inlineThenPointer]['line'] !== $tokens[$inlineElsePointer]['line']) { return; } $inlineElseEndPointer = TernaryOperatorHelper::getEndPointer($phpcsFile, $inlineThenPointer, $inlineElsePointer); $pointerAfterInlineElseEnd = TokenHelper::findNextEffective($phpcsFile, $inlineElseEndPointer + 1); if ($pointerAfterInlineElseEnd === null || $tokens[$pointerAfterInlineElseEnd]['code'] !== T_SEMICOLON) { return; } $endOfLineBeforeInlineThenPointer = $this->getEndOfLineBefore($phpcsFile, $inlineThenPointer); $actualLineLength = strlen(TokenHelper::getContent($phpcsFile, $endOfLineBeforeInlineThenPointer + 1, $pointerAfterInlineElseEnd)); if ($actualLineLength <= $this->lineLengthLimit) { return; } $expressionsLength = strlen(TokenHelper::getContent($phpcsFile, $inlineThenPointer + 1, $pointerAfterInlineElseEnd - 1)); if ( $this->minExpressionsLength !== null && $this->minExpressionsLength >= $expressionsLength ) { return; } $fix = $phpcsFile->addFixableError( 'Ternary operator should be reformatted to more lines.', $inlineThenPointer, self::CODE_MULTI_LINE_TERNARY_OPERATOR_NOT_USED, ); if (!$fix) { return; } $indentation = $this->getIndentation($phpcsFile, $endOfLineBeforeInlineThenPointer); $pointerBeforeInlineThen = TokenHelper::findPreviousEffective($phpcsFile, $inlineThenPointer - 1); $pointerBeforeInlineElse = TokenHelper::findPreviousEffective($phpcsFile, $inlineElsePointer - 1); $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetween($phpcsFile, $pointerBeforeInlineThen, $inlineThenPointer); FixerHelper::addBefore($phpcsFile, $inlineThenPointer, $phpcsFile->eolChar . $indentation); FixerHelper::removeBetween($phpcsFile, $pointerBeforeInlineElse, $inlineElsePointer); FixerHelper::addBefore($phpcsFile, $inlineElsePointer, $phpcsFile->eolChar . $indentation); $phpcsFile->fixer->endChangeset(); } private function getEndOfLineBefore(File $phpcsFile, int $pointer): int { $tokens = $phpcsFile->getTokens(); $endOfLineBefore = null; $startPointer = $pointer - 1; while (true) { $possibleEndOfLinePointer = TokenHelper::findPrevious( $phpcsFile, [T_WHITESPACE, T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO, ...TokenHelper::INLINE_COMMENT_TOKEN_CODES], $startPointer, ); if ( $tokens[$possibleEndOfLinePointer]['code'] === T_WHITESPACE && $tokens[$possibleEndOfLinePointer]['content'] === $phpcsFile->eolChar ) { $endOfLineBefore = $possibleEndOfLinePointer; break; } if ( $tokens[$possibleEndOfLinePointer]['code'] === T_OPEN_TAG || $tokens[$possibleEndOfLinePointer]['code'] === T_OPEN_TAG_WITH_ECHO ) { $endOfLineBefore = $possibleEndOfLinePointer; break; } if ( in_array($tokens[$possibleEndOfLinePointer]['code'], TokenHelper::INLINE_COMMENT_TOKEN_CODES, true) && substr($tokens[$possibleEndOfLinePointer]['content'], -1) === $phpcsFile->eolChar ) { $endOfLineBefore = $possibleEndOfLinePointer; break; } $startPointer = $possibleEndOfLinePointer - 1; } /** @var int $endOfLineBefore */ $endOfLineBefore = $endOfLineBefore; return $endOfLineBefore; } private function getIndentation(File $phpcsFile, int $endOfLinePointer): string { $pointerAfterWhitespace = TokenHelper::findNextNonWhitespace($phpcsFile, $endOfLinePointer + 1); $actualIndentation = TokenHelper::getContent($phpcsFile, $endOfLinePointer + 1, $pointerAfterWhitespace - 1); return IndentationHelper::addIndentation($phpcsFile, $actualIndentation); } } PK41]3qqvcoding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/DisallowContinueWithoutIntegerOperandInSwitchSniff.phpnu[ */ public function register(): array { return [ T_CONTINUE, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $continuePointer */ public function process(File $phpcsFile, $continuePointer): void { $tokens = $phpcsFile->getTokens(); $operandPointer = TokenHelper::findNextEffective($phpcsFile, $continuePointer + 1); if ($tokens[$operandPointer]['code'] === T_LNUMBER) { return; } $conditionTokenCode = current(array_reverse($tokens[$continuePointer]['conditions'])); if ($conditionTokenCode !== T_SWITCH) { return; } $fix = $phpcsFile->addFixableError( 'Usage of "continue" without integer operand in "switch" is disallowed, use "break" instead.', $continuePointer, self::CODE_DISALLOWED_CONTINUE_WITHOUT_INTEGER_OPERAND_IN_SWITCH, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $continuePointer, 'break'); $phpcsFile->fixer->endChangeset(); } } PK41] @dcoding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/RequireNullCoalesceOperatorSniff.phpnu[ */ public function register(): array { return [ T_ISSET, T_IS_IDENTICAL, T_IS_NOT_IDENTICAL, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { $tokens = $phpcsFile->getTokens(); if ($tokens[$pointer]['code'] === T_ISSET) { $this->checkIsset($phpcsFile, $pointer); } else { $this->checkIdenticalOperator($phpcsFile, $pointer); } } public function checkIsset(File $phpcsFile, int $issetPointer): void { $tokens = $phpcsFile->getTokens(); $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $issetPointer - 1); if ($tokens[$previousPointer]['code'] === T_BOOLEAN_NOT) { return; } if (in_array($tokens[$previousPointer]['code'], Tokens::$booleanOperators, true)) { return; } $openParenthesisPointer = TokenHelper::findNextEffective($phpcsFile, $issetPointer + 1); $closeParenthesisPointer = $tokens[$openParenthesisPointer]['parenthesis_closer']; /** @var int $inlineThenPointer */ $inlineThenPointer = TokenHelper::findNextEffective($phpcsFile, $closeParenthesisPointer + 1); if ($tokens[$inlineThenPointer]['code'] !== T_INLINE_THEN) { return; } $commaPointer = TokenHelper::findNext($phpcsFile, T_COMMA, $openParenthesisPointer + 1, $closeParenthesisPointer); if ($commaPointer !== null) { return; } $inlineElsePointer = TernaryOperatorHelper::getElsePointer($phpcsFile, $inlineThenPointer); $variableContent = IdentificatorHelper::getContent($phpcsFile, $openParenthesisPointer + 1, $closeParenthesisPointer - 1); $thenContent = IdentificatorHelper::getContent($phpcsFile, $inlineThenPointer + 1, $inlineElsePointer - 1); if ($variableContent !== $thenContent) { return; } $fix = $phpcsFile->addFixableError( 'Use null coalesce operator instead of ternary operator.', $inlineThenPointer, self::CODE_NULL_COALESCE_OPERATOR_NOT_USED, ); if (!$fix) { return; } $startPointer = $issetPointer; if (in_array($tokens[$previousPointer]['code'], Tokens::$castTokens, true)) { $startPointer = $previousPointer; } $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $startPointer, $inlineElsePointer, sprintf('%s ??', $variableContent)); $phpcsFile->fixer->endChangeset(); } public function checkIdenticalOperator(File $phpcsFile, int $identicalOperator): void { $tokens = $phpcsFile->getTokens(); /** @var int $pointerBeforeIdenticalOperator */ $pointerBeforeIdenticalOperator = TokenHelper::findPreviousEffective($phpcsFile, $identicalOperator - 1); /** @var int $pointerAfterIdenticalOperator */ $pointerAfterIdenticalOperator = TokenHelper::findNextEffective($phpcsFile, $identicalOperator + 1); if ( $tokens[$pointerBeforeIdenticalOperator]['code'] !== T_NULL && $tokens[$pointerAfterIdenticalOperator]['code'] !== T_NULL ) { return; } $isYodaCondition = $tokens[$pointerBeforeIdenticalOperator]['code'] === T_NULL; $variableEndPointer = $isYodaCondition ? $pointerAfterIdenticalOperator : $pointerBeforeIdenticalOperator; $tmpPointer = $variableEndPointer; while ($tokens[$tmpPointer]['code'] === T_CLOSE_PARENTHESIS) { /** @var int $tmpPointer */ $tmpPointer = TokenHelper::findPreviousEffective($phpcsFile, $tokens[$tmpPointer]['parenthesis_opener'] - 1); } $variableStartPointer = IdentificatorHelper::findStartPointer($phpcsFile, $tmpPointer); if ($variableStartPointer === null) { return; } $pointerBeforeCondition = TokenHelper::findPreviousEffective( $phpcsFile, ($isYodaCondition ? $pointerBeforeIdenticalOperator : $variableStartPointer) - 1, ); if (in_array($tokens[$pointerBeforeCondition]['code'], Tokens::$booleanOperators, true)) { return; } /** @var int $inlineThenPointer */ $inlineThenPointer = TokenHelper::findNextEffective( $phpcsFile, ($isYodaCondition ? $variableEndPointer : $pointerAfterIdenticalOperator) + 1, ); if ($tokens[$inlineThenPointer]['code'] !== T_INLINE_THEN) { return; } $inlineElsePointer = TernaryOperatorHelper::getElsePointer($phpcsFile, $inlineThenPointer); $inlineElseEndPointer = TernaryOperatorHelper::getEndPointer($phpcsFile, $inlineThenPointer, $inlineElsePointer); $pointerAfterInlineElseEnd = TokenHelper::findNextEffective($phpcsFile, $inlineElseEndPointer + 1); $variableContent = IdentificatorHelper::getContent($phpcsFile, $variableStartPointer, $variableEndPointer); /** @var int $compareToStartPointer */ $compareToStartPointer = TokenHelper::findNextEffective( $phpcsFile, ($tokens[$identicalOperator]['code'] === T_IS_IDENTICAL ? $inlineElsePointer : $inlineThenPointer) + 1, ); /** @var int $compareToEndPointer */ $compareToEndPointer = TokenHelper::findPreviousEffective( $phpcsFile, ($tokens[$identicalOperator]['code'] === T_IS_IDENTICAL ? $pointerAfterInlineElseEnd : $inlineElsePointer) - 1, ); $compareToContent = IdentificatorHelper::getContent($phpcsFile, $compareToStartPointer, $compareToEndPointer); if ($compareToContent !== $variableContent) { return; } $fix = $phpcsFile->addFixableError( 'Use null coalesce operator instead of ternary operator.', $inlineThenPointer, self::CODE_NULL_COALESCE_OPERATOR_NOT_USED, ); if (!$fix) { return; } /** @var int $conditionStart */ $conditionStart = $isYodaCondition ? $pointerBeforeIdenticalOperator : $variableStartPointer; $variableContent = trim(TokenHelper::getContent($phpcsFile, $variableStartPointer, $variableEndPointer)); $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $conditionStart, sprintf('%s ??', $variableContent)); if ($tokens[$identicalOperator]['code'] === T_IS_IDENTICAL) { FixerHelper::removeBetweenIncluding($phpcsFile, $conditionStart + 1, $inlineThenPointer); $pointerBeforeInlineElse = TokenHelper::findPreviousEffective($phpcsFile, $inlineElsePointer - 1); FixerHelper::removeBetweenIncluding($phpcsFile, $pointerBeforeInlineElse + 1, $inlineElseEndPointer); } else { FixerHelper::removeBetweenIncluding($phpcsFile, $conditionStart + 1, $inlineElsePointer); } $phpcsFile->fixer->endChangeset(); } } PK41]>RAAfcoding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/RequireNullSafeObjectOperatorSniff.phpnu[|\?->)~'; public ?bool $enable = null; /** * @return array */ public function register(): array { return [ T_IS_IDENTICAL, T_IS_NOT_IDENTICAL, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $identicalPointer */ public function process(File $phpcsFile, $identicalPointer): int { $this->enable = SniffSettingsHelper::isEnabledByPhpVersion($this->enable, 80000); if (!$this->enable) { return $identicalPointer + 1; } $tokens = $phpcsFile->getTokens(); [$pointerBeforeIdentical, $pointerAfterIdentical] = $this->getIdenticalData($phpcsFile, $identicalPointer); if ($tokens[$pointerBeforeIdentical]['code'] !== T_NULL && $tokens[$pointerAfterIdentical]['code'] !== T_NULL) { return $identicalPointer + 1; } [$identificatorStartPointer, $identificatorEndPointer, $conditionStartPointer] = $this->getConditionData( $phpcsFile, $pointerBeforeIdentical, $pointerAfterIdentical, ); if ($identificatorStartPointer === null || $identificatorEndPointer === null) { return $identicalPointer + 1; } $isYoda = $tokens[$pointerBeforeIdentical]['code'] === T_NULL; $identificator = IdentificatorHelper::getContent($phpcsFile, $identificatorStartPointer, $identificatorEndPointer); $pointerAfterCondition = TokenHelper::findNextEffective( $phpcsFile, ($isYoda ? $identificatorEndPointer : $pointerAfterIdentical) + 1, ); $allowedBooleanCondition = $tokens[$identicalPointer]['code'] === T_IS_NOT_IDENTICAL ? T_BOOLEAN_AND : T_BOOLEAN_OR; if ($tokens[$pointerAfterCondition]['code'] === $allowedBooleanCondition) { return $this->checkNextCondition($phpcsFile, $identicalPointer, $conditionStartPointer, $identificator, $pointerAfterCondition); } if ($tokens[$pointerAfterCondition]['code'] === T_INLINE_THEN) { $this->checkTernaryOperator($phpcsFile, $identicalPointer, $conditionStartPointer, $identificator, $pointerAfterCondition); return $pointerAfterCondition + 1; } return $identicalPointer + 1; } private function checkTernaryOperator( File $phpcsFile, int $identicalPointer, int $conditionStartPointer, string $identificator, int $inlineThenPointer ): void { $tokens = $phpcsFile->getTokens(); $ternaryOperatorStartPointer = TernaryOperatorHelper::getStartPointer($phpcsFile, $inlineThenPointer); $searchStartPointer = $ternaryOperatorStartPointer; do { $booleanOperatorPointer = TokenHelper::findNext($phpcsFile, Tokens::$booleanOperators, $searchStartPointer, $inlineThenPointer); if ($booleanOperatorPointer === null) { break; } $identicalPointer = TokenHelper::findNext( $phpcsFile, [T_IS_IDENTICAL, T_IS_NOT_IDENTICAL], $searchStartPointer, $booleanOperatorPointer, ); if ($identicalPointer === null) { return; } $pointerAfterIdentical = TokenHelper::findNextEffective($phpcsFile, $identicalPointer + 1); if ($tokens[$pointerAfterIdentical]['code'] !== T_NULL) { return; } $searchStartPointer = $booleanOperatorPointer + 1; } while (true); $pointerBeforeCondition = TokenHelper::findPreviousEffective($phpcsFile, $conditionStartPointer - 1); if (in_array($tokens[$pointerBeforeCondition]['code'], [T_BOOLEAN_AND, T_BOOLEAN_OR], true)) { $previousIdenticalPointer = TokenHelper::findPreviousLocal( $phpcsFile, [T_IS_IDENTICAL, T_IS_NOT_IDENTICAL], $pointerBeforeCondition, ); if ($previousIdenticalPointer !== null) { [$pointerBeforePreviousIdentical, $pointerAfterPreviousIdentical] = $this->getIdenticalData( $phpcsFile, $previousIdenticalPointer, ); [$previousIdentificatorStartPointer, $previousIdentificatorEndPointer] = $this->getConditionData( $phpcsFile, $pointerBeforePreviousIdentical, $pointerAfterPreviousIdentical, ); if ($previousIdentificatorStartPointer !== null && $previousIdentificatorEndPointer !== null) { $previousIdentificator = IdentificatorHelper::getContent( $phpcsFile, $previousIdentificatorStartPointer, $previousIdentificatorEndPointer, ); if (!self::areIdentificatorsCompatible($previousIdentificator, $identificator)) { return; } } } } $defaultInElse = $tokens[$identicalPointer]['code'] === T_IS_NOT_IDENTICAL; $inlineElsePointer = TernaryOperatorHelper::getElsePointer($phpcsFile, $inlineThenPointer); $inlineElseEndPointer = TernaryOperatorHelper::getEndPointer($phpcsFile, $inlineThenPointer, $inlineElsePointer); if ($defaultInElse) { $nextIdentificatorPointers = $this->getNextIdentificator($phpcsFile, $inlineThenPointer); if ($nextIdentificatorPointers === null) { return; } [$nextIdentificatorStartPointer, $nextIdentificatorEndPointer] = $nextIdentificatorPointers; $nextIdentificator = IdentificatorHelper::getContent($phpcsFile, $nextIdentificatorStartPointer, $nextIdentificatorEndPointer); if (!$this->areIdentificatorsCompatible($identificator, $nextIdentificator)) { return; } if (TokenHelper::findNextEffective($phpcsFile, $nextIdentificatorEndPointer + 1) !== $inlineElsePointer) { return; } $identificatorDifference = $this->getIdentificatorDifference( $phpcsFile, $identificator, $nextIdentificatorStartPointer, $nextIdentificatorEndPointer, ); $firstPointerInElse = TokenHelper::findNextEffective($phpcsFile, $inlineElsePointer + 1); $defaultContent = TokenHelper::getContent($phpcsFile, $firstPointerInElse, $inlineElseEndPointer); $conditionEndPointer = $inlineElseEndPointer; } else { $nullPointer = TokenHelper::findNextEffective($phpcsFile, $inlineThenPointer + 1); if ($tokens[$nullPointer]['code'] !== T_NULL) { return; } if (TokenHelper::findNextEffective($phpcsFile, $nullPointer + 1) !== $inlineElsePointer) { return; } $nextIdentificatorPointers = $this->getNextIdentificator($phpcsFile, $inlineElsePointer); if ($nextIdentificatorPointers === null) { return; } [$nextIdentificatorStartPointer, $nextIdentificatorEndPointer] = $nextIdentificatorPointers; if ($nextIdentificatorEndPointer !== $inlineElseEndPointer) { return; } $nextIdentificator = IdentificatorHelper::getContent($phpcsFile, $nextIdentificatorStartPointer, $nextIdentificatorEndPointer); if (!$this->areIdentificatorsCompatible($identificator, $nextIdentificator)) { return; } $identificatorDifference = $this->getIdentificatorDifference( $phpcsFile, $identificator, $nextIdentificatorStartPointer, $nextIdentificatorEndPointer, ); $defaultContent = trim(TokenHelper::getContent($phpcsFile, $inlineThenPointer + 1, $inlineElsePointer - 1)); $conditionEndPointer = $nextIdentificatorEndPointer; } $fix = $phpcsFile->addFixableError('Operator ?-> is required.', $identicalPointer, self::CODE_REQUIRED_NULL_SAFE_OBJECT_OPERATOR); if (!$fix) { return; } $conditionContent = sprintf('%s?%s', $identificator, $identificatorDifference); if (strtolower($defaultContent) !== 'null') { $conditionContent .= sprintf(' ?? %s', $defaultContent); } $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $conditionStartPointer, $conditionEndPointer, $conditionContent); $phpcsFile->fixer->endChangeset(); } private function checkNextCondition( File $phpcsFile, int $identicalPointer, int $conditionStartPointer, string $identificator, int $nextConditionBooleanPointer ): int { $nextIdentificatorPointers = $this->getNextIdentificator($phpcsFile, $nextConditionBooleanPointer); if ($nextIdentificatorPointers === null) { return $nextConditionBooleanPointer; } [$nextIdentificatorStartPointer, $nextIdentificatorEndPointer] = $nextIdentificatorPointers; $nextIdentificator = IdentificatorHelper::getContent($phpcsFile, $nextIdentificatorStartPointer, $nextIdentificatorEndPointer); if (!$this->areIdentificatorsCompatible($identificator, $nextIdentificator)) { return $nextIdentificatorEndPointer; } $pointerAfterNexIdentificator = TokenHelper::findNextEffective($phpcsFile, $nextIdentificatorEndPointer + 1); $tokens = $phpcsFile->getTokens(); if ( $tokens[$pointerAfterNexIdentificator]['code'] !== $tokens[$identicalPointer]['code'] && !in_array($tokens[$pointerAfterNexIdentificator]['code'], [T_INLINE_THEN, T_SEMICOLON], true) ) { return $pointerAfterNexIdentificator; } if (!in_array($tokens[$pointerAfterNexIdentificator]['code'], [T_IS_IDENTICAL, T_IS_NOT_IDENTICAL], true)) { return $pointerAfterNexIdentificator; } $pointerAfterIdentical = TokenHelper::findNextEffective($phpcsFile, $pointerAfterNexIdentificator + 1); if ($tokens[$pointerAfterIdentical]['code'] !== T_NULL) { return $pointerAfterNexIdentificator; } $identificatorDifference = $this->getIdentificatorDifference( $phpcsFile, $identificator, $nextIdentificatorStartPointer, $nextIdentificatorEndPointer, ); $fix = $phpcsFile->addFixableError('Operator ?-> is required.', $identicalPointer, self::CODE_REQUIRED_NULL_SAFE_OBJECT_OPERATOR); if (!$fix) { return $pointerAfterNexIdentificator; } $isConditionOfTernaryOperator = TernaryOperatorHelper::isConditionOfTernaryOperator($phpcsFile, $identicalPointer); $phpcsFile->fixer->beginChangeset(); FixerHelper::change( $phpcsFile, $conditionStartPointer, $nextIdentificatorEndPointer, sprintf('%s?%s', $identificator, $identificatorDifference), ); $phpcsFile->fixer->endChangeset(); if ($isConditionOfTernaryOperator) { return TokenHelper::findNext($phpcsFile, T_INLINE_THEN, $identicalPointer + 1); } return $pointerAfterNexIdentificator; } /** * @return array|null */ private function getNextIdentificator(File $phpcsFile, int $pointerBefore): ?array { /** @var int $nextIdentificatorStartPointer */ $nextIdentificatorStartPointer = TokenHelper::findNextEffective($phpcsFile, $pointerBefore + 1); $nextIdentificatorEndPointer = $this->findIdentificatorEnd($phpcsFile, $nextIdentificatorStartPointer); if ($nextIdentificatorEndPointer === null) { return null; } return [$nextIdentificatorStartPointer, $nextIdentificatorEndPointer]; } private function findIdentificatorStart(File $phpcsFile, int $identificatorEndPointer): ?int { $tokens = $phpcsFile->getTokens(); if ($tokens[$identificatorEndPointer]['code'] === T_CLOSE_PARENTHESIS) { $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective( $phpcsFile, $tokens[$identificatorEndPointer]['parenthesis_opener'] - 1, ); $identificatorStartPointer = IdentificatorHelper::findStartPointer($phpcsFile, $pointerBeforeParenthesisOpener); } else { $identificatorStartPointer = IdentificatorHelper::findStartPointer($phpcsFile, $identificatorEndPointer); } if ($identificatorStartPointer !== null) { $pointerBeforeIdentificatorStart = TokenHelper::findPreviousEffective($phpcsFile, $identificatorStartPointer - 1); if (in_array( $tokens[$pointerBeforeIdentificatorStart]['code'], [T_DOUBLE_COLON, T_OBJECT_OPERATOR, T_NULLSAFE_OBJECT_OPERATOR], true, )) { $pointerBeforeOperator = TokenHelper::findPreviousEffective($phpcsFile, $pointerBeforeIdentificatorStart - 1); return $this->findIdentificatorStart($phpcsFile, $pointerBeforeOperator); } } return $identificatorStartPointer; } private function findIdentificatorEnd(File $phpcsFile, int $identificatorStartPointer): ?int { $tokens = $phpcsFile->getTokens(); $identificatorEndPointer = $tokens[$identificatorStartPointer]['code'] === T_STRING ? $identificatorStartPointer : IdentificatorHelper::findEndPointer($phpcsFile, $identificatorStartPointer); if ($identificatorEndPointer !== null) { $pointerAfterIdentificatorEnd = TokenHelper::findNextEffective($phpcsFile, $identificatorEndPointer + 1); if ($tokens[$pointerAfterIdentificatorEnd]['code'] === T_OPEN_PARENTHESIS) { $identificatorEndPointer = $tokens[$pointerAfterIdentificatorEnd]['parenthesis_closer']; $pointerAfterIdentificatorEnd = TokenHelper::findNextEffective($phpcsFile, $identificatorEndPointer + 1); } if (in_array( $tokens[$pointerAfterIdentificatorEnd]['code'], [T_DOUBLE_COLON, T_OBJECT_OPERATOR, T_NULLSAFE_OBJECT_OPERATOR], true, )) { $pointerAfterOperator = TokenHelper::findNextEffective($phpcsFile, $pointerAfterIdentificatorEnd + 1); return $this->findIdentificatorEnd($phpcsFile, $pointerAfterOperator); } } return $identificatorEndPointer; } private function areIdentificatorsCompatible(string $first, string $second): bool { /** @var list $firstParts */ $firstParts = preg_split(self::OPERATOR_REGEXP, $first, -1, PREG_SPLIT_DELIM_CAPTURE); /** @var list $secondParts */ $secondParts = preg_split(self::OPERATOR_REGEXP, $second, -1, PREG_SPLIT_DELIM_CAPTURE); $minPartsCount = min(count($firstParts), count($secondParts)); for ($i = 0; $i < $minPartsCount; $i++) { if ($firstParts[$i] === '?->' && $secondParts[$i] === '->') { continue; } if ($firstParts[$i] !== $secondParts[$i]) { return false; } } return array_key_exists($minPartsCount, $secondParts) && $secondParts[$minPartsCount] === '->'; } private function getIdentificatorDifference( File $phpcsFile, string $identificator, int $nextIdentificatorStartPointer, int $nextIdentificatorEndPointer ): string { $objectOperatorsCountInIdentificator = substr_count($identificator, '->'); $tokens = $phpcsFile->getTokens(); $objectOperatorsCountInNextIdentificator = 0; $differencePointer = $nextIdentificatorStartPointer; for ($i = $nextIdentificatorStartPointer; $i <= $nextIdentificatorEndPointer; $i++) { if (in_array($tokens[$i]['code'], [T_OBJECT_OPERATOR, T_NULLSAFE_OBJECT_OPERATOR], true)) { $objectOperatorsCountInNextIdentificator++; } if ($objectOperatorsCountInNextIdentificator > $objectOperatorsCountInIdentificator) { $differencePointer = $i; break; } } return TokenHelper::getContent($phpcsFile, $differencePointer, $nextIdentificatorEndPointer); } /** * @return array{0: int, 1: int} */ private function getIdenticalData(File $phpcsFile, int $identicalPointer): array { /** @var int $pointerBeforeIdentical */ $pointerBeforeIdentical = TokenHelper::findPreviousEffective($phpcsFile, $identicalPointer - 1); /** @var int $pointerAfterIdentical */ $pointerAfterIdentical = TokenHelper::findNextEffective($phpcsFile, $identicalPointer + 1); return [$pointerBeforeIdentical, $pointerAfterIdentical]; } /** * @return array{0: int|null, 1: int|null, 2: int|null} */ private function getConditionData(File $phpcsFile, int $pointerBeforeIdentical, int $pointerAfterIdentical): array { $tokens = $phpcsFile->getTokens(); $isYoda = $tokens[$pointerBeforeIdentical]['code'] === T_NULL; if ($isYoda) { $identificatorStartPointer = $pointerAfterIdentical; $identificatorEndPointer = $this->findIdentificatorEnd($phpcsFile, $identificatorStartPointer); $conditionStartPointer = $pointerBeforeIdentical; } else { $identificatorEndPointer = $pointerBeforeIdentical; $identificatorStartPointer = $this->findIdentificatorStart($phpcsFile, $identificatorEndPointer); $conditionStartPointer = $identificatorStartPointer; } return [$identificatorStartPointer, $identificatorEndPointer, $conditionStartPointer]; } } PK41]o22Vcoding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/DisallowEmptySniff.phpnu[ */ public function register(): array { return [ T_EMPTY, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $emptyPointer */ public function process(File $phpcsFile, $emptyPointer): void { $phpcsFile->addError('Use of empty() is disallowed.', $emptyPointer, self::CODE_DISALLOWED_EMPTY); } } PK41]BFicoding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/RequireNullCoalesceEqualOperatorSniff.phpnu[ */ public function register(): array { return [ T_EQUAL, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $equalPointer */ public function process(File $phpcsFile, $equalPointer): void { $this->enable = SniffSettingsHelper::isEnabledByPhpVersion($this->enable, 70400); if (!$this->enable) { return; } $this->checkCoalesce($phpcsFile, $equalPointer); $this->checkIf($phpcsFile, $equalPointer); } private function checkCoalesce(File $phpcsFile, int $equalPointer): void { /** @var int $variableStartPointer */ $variableStartPointer = TokenHelper::findNextEffective($phpcsFile, $equalPointer + 1); $variableEndPointer = IdentificatorHelper::findEndPointer($phpcsFile, $variableStartPointer); if ($variableEndPointer === null) { return; } $nullCoalescePointer = TokenHelper::findNextEffective($phpcsFile, $variableEndPointer + 1); $tokens = $phpcsFile->getTokens(); if ($tokens[$nullCoalescePointer]['code'] !== T_COALESCE) { return; } $variableContent = IdentificatorHelper::getContent($phpcsFile, $variableStartPointer, $variableEndPointer); /** @var int $beforeEqualEndPointer */ $beforeEqualEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $equalPointer - 1); $beforeEqualStartPointer = IdentificatorHelper::findStartPointer($phpcsFile, $beforeEqualEndPointer); if ($beforeEqualStartPointer === null) { return; } $beforeEqualVariableContent = IdentificatorHelper::getContent($phpcsFile, $beforeEqualStartPointer, $beforeEqualEndPointer); if ($beforeEqualVariableContent !== $variableContent) { return; } $semicolonPointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $equalPointer + 1); if (TokenHelper::findNext($phpcsFile, Tokens::$operators, $nullCoalescePointer + 1, $semicolonPointer) !== null) { return; } $fix = $phpcsFile->addFixableError( 'Use "??=" operator instead of "=" and "??".', $equalPointer, self::CODE_REQUIRED_NULL_COALESCE_EQUAL_OPERATOR, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $equalPointer, $nullCoalescePointer, '??='); $phpcsFile->fixer->endChangeset(); } private function checkIf(File $phpcsFile, int $equalPointer): void { if (!$this->checkIfConditions) { return; } $tokens = $phpcsFile->getTokens(); $conditionsCount = count($tokens[$equalPointer]['conditions']); if ($conditionsCount === 0) { return; } $ifPointer = array_keys($tokens[$equalPointer]['conditions'])[$conditionsCount - 1]; if ($tokens[$ifPointer]['code'] !== T_IF) { return; } $pointerAfterIfCondition = TokenHelper::findNextEffective($phpcsFile, $tokens[$ifPointer]['scope_closer'] + 1); if ($pointerAfterIfCondition !== null && in_array($tokens[$pointerAfterIfCondition]['code'], [T_ELSEIF, T_ELSE], true)) { return; } $ifVariableStartPointer = TokenHelper::findNextEffective($phpcsFile, $tokens[$ifPointer]['parenthesis_opener'] + 1); $ifVariableEndPointer = IdentificatorHelper::findEndPointer($phpcsFile, $ifVariableStartPointer); if ($ifVariableEndPointer === null) { return; } $nextIfPointer = TokenHelper::findNextEffective($phpcsFile, $ifVariableEndPointer + 1); if ($tokens[$nextIfPointer]['code'] !== T_IS_IDENTICAL) { return; } $nextIfPointer = TokenHelper::findNextEffective($phpcsFile, $nextIfPointer + 1); if ($tokens[$nextIfPointer]['code'] !== T_NULL) { return; } if (TokenHelper::findNextEffective($phpcsFile, $nextIfPointer + 1) !== $tokens[$ifPointer]['parenthesis_closer']) { return; } $beforeEqualVariableStartPointer = TokenHelper::findNextEffective($phpcsFile, $tokens[$ifPointer]['scope_opener'] + 1); $beforeEqualVariableEndPointer = IdentificatorHelper::findEndPointer($phpcsFile, $beforeEqualVariableStartPointer); if ($beforeEqualVariableEndPointer === null) { return; } if (TokenHelper::findNextEffective($phpcsFile, $beforeEqualVariableEndPointer + 1) !== $equalPointer) { return; } $variableName = IdentificatorHelper::getContent($phpcsFile, $ifVariableStartPointer, $ifVariableEndPointer); if ($variableName !== IdentificatorHelper::getContent( $phpcsFile, $beforeEqualVariableStartPointer, $beforeEqualVariableEndPointer, )) { return; } $semicolonPointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $equalPointer + 1); if (TokenHelper::findNextEffective($phpcsFile, $semicolonPointer + 1) !== $tokens[$ifPointer]['scope_closer']) { return; } $fix = $phpcsFile->addFixableError( 'Use "??=" operator instead of if condition and "=".', $ifPointer, self::CODE_REQUIRED_NULL_COALESCE_EQUAL_OPERATOR, ); if (!$fix) { return; } $codeStartPointer = TokenHelper::findNextEffective($phpcsFile, $equalPointer + 1); $afterNullCoalesceEqualCode = IndentationHelper::removeIndentation( $phpcsFile, range($codeStartPointer, $semicolonPointer), IndentationHelper::getIndentation($phpcsFile, $ifPointer), ); $phpcsFile->fixer->beginChangeset(); FixerHelper::change( $phpcsFile, $ifPointer, $tokens[$ifPointer]['scope_closer'], sprintf('%s ??= %s', $variableName, trim($afterNullCoalesceEqualCode)), ); $phpcsFile->fixer->endChangeset(); } } PK41]00Ycoding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/AbstractLineCondition.phpnu[ */ public array $checkedControlStructures = [ self::IF_CONTROL_STRUCTURE, self::WHILE_CONTROL_STRUCTURE, self::DO_CONTROL_STRUCTURE, ]; /** * @return array */ public function register(): array { $this->checkedControlStructures = SniffSettingsHelper::normalizeArray($this->checkedControlStructures); $register = []; if (in_array(self::IF_CONTROL_STRUCTURE, $this->checkedControlStructures, true)) { $register[] = T_IF; $register[] = T_ELSEIF; } if (in_array(self::WHILE_CONTROL_STRUCTURE, $this->checkedControlStructures, true)) { $register[] = T_WHILE; } if (in_array(self::DO_CONTROL_STRUCTURE, $this->checkedControlStructures, true)) { $register[] = T_WHILE; } return $register; } protected function shouldBeSkipped(File $phpcsFile, int $controlStructurePointer): bool { $tokens = $phpcsFile->getTokens(); if ( !array_key_exists('parenthesis_opener', $tokens[$controlStructurePointer]) || $tokens[$controlStructurePointer]['parenthesis_opener'] === null || !array_key_exists('parenthesis_closer', $tokens[$controlStructurePointer]) || $tokens[$controlStructurePointer]['parenthesis_closer'] === null ) { return true; } if ($tokens[$controlStructurePointer]['code'] === T_WHILE) { $isPartOfDo = $this->isPartOfDo($phpcsFile, $controlStructurePointer); if ($isPartOfDo && !in_array(self::DO_CONTROL_STRUCTURE, $this->checkedControlStructures, true)) { return true; } if (!$isPartOfDo && !in_array(self::WHILE_CONTROL_STRUCTURE, $this->checkedControlStructures, true)) { return true; } } return false; } protected function getControlStructureName(File $phpcsFile, int $controlStructurePointer): string { $tokens = $phpcsFile->getTokens(); return $tokens[$controlStructurePointer]['code'] === T_WHILE && $this->isPartOfDo($phpcsFile, $controlStructurePointer) ? 'do-while' : $tokens[$controlStructurePointer]['content']; } protected function isPartOfDo(File $phpcsFile, int $whilePointer): bool { $tokens = $phpcsFile->getTokens(); $parenthesisCloserPointer = $tokens[$whilePointer]['parenthesis_closer']; $pointerAfterParenthesisCloser = TokenHelper::findNextEffective($phpcsFile, $parenthesisCloserPointer + 1); return $tokens[$pointerAfterParenthesisCloser]['code'] !== T_OPEN_CURLY_BRACKET; } protected function getLineStart(File $phpcsFile, int $pointer): string { $firstPointerOnLine = TokenHelper::findFirstTokenOnLine($phpcsFile, $pointer); return TokenHelper::getContent($phpcsFile, $firstPointerOnLine, $pointer); } protected function getCondition(File $phpcsFile, int $parenthesisOpenerPointer, int $parenthesisCloserPointer): string { $condition = TokenHelper::getContent($phpcsFile, $parenthesisOpenerPointer + 1, $parenthesisCloserPointer - 1); return trim(preg_replace(sprintf('~%s[ \t]*~', $phpcsFile->eolChar), ' ', $condition)); } protected function getLineEnd(File $phpcsFile, int $pointer): string { $lastPointerOnLine = TokenHelper::findLastTokenOnLine($phpcsFile, $pointer); return rtrim(TokenHelper::getContent($phpcsFile, $pointer, $lastPointerOnLine)); } } PK41]\ */ public function register(): array { return [ T_INLINE_THEN, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $inlineThenPointer */ public function process(File $phpcsFile, $inlineThenPointer): void { $tokens = $phpcsFile->getTokens(); $nextPointer = TokenHelper::findNextEffective($phpcsFile, $inlineThenPointer + 1); if ($tokens[$nextPointer]['code'] === T_INLINE_ELSE) { return; } $conditionStartPointer = TernaryOperatorHelper::getStartPointer($phpcsFile, $inlineThenPointer); $inlineElsePointer = TernaryOperatorHelper::getElsePointer($phpcsFile, $inlineThenPointer); $inlineElseEndPointer = TernaryOperatorHelper::getEndPointer($phpcsFile, $inlineThenPointer, $inlineElsePointer); $thenContent = trim(TokenHelper::getContent($phpcsFile, $inlineThenPointer + 1, $inlineElsePointer - 1)); $elseContent = trim(TokenHelper::getContent($phpcsFile, $inlineElsePointer + 1, $inlineElseEndPointer)); $conditionEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $inlineThenPointer - 1); $condition = TokenHelper::getContent($phpcsFile, $conditionStartPointer, $conditionEndPointer); if ($tokens[$conditionStartPointer]['code'] === T_BOOLEAN_NOT) { if ($elseContent !== ltrim($condition, '!')) { return; } } else { if ($thenContent !== $condition) { return; } } $fix = $phpcsFile->addFixableError('Use short ternary operator.', $inlineThenPointer, self::CODE_REQUIRED_SHORT_TERNARY_OPERATOR); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); if ($tokens[$conditionStartPointer]['code'] === T_BOOLEAN_NOT) { FixerHelper::replace($phpcsFile, $conditionStartPointer, ''); FixerHelper::change($phpcsFile, $inlineThenPointer, $inlineElseEndPointer, sprintf('?: %s', $thenContent)); } else { FixerHelper::removeBetween($phpcsFile, $inlineThenPointer, $inlineElsePointer); } $phpcsFile->fixer->endChangeset(); } } PK41]XJ77gcoding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/DisallowNullSafeObjectOperatorSniff.phpnu[ */ public function register(): array { return [ T_NULLSAFE_OBJECT_OPERATOR, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $operatorPointer */ public function process(File $phpcsFile, $operatorPointer): void { $phpcsFile->addError('Operator ?-> is disallowed.', $operatorPointer, self::CODE_DISALLOWED_NULL_SAFE_OBJECT_OPERATOR); } } PK41]ndX X Kcoding-standard/SlevomatCodingStandard/Sniffs/Complexity/CognitiveSniff.phpnu[ T_CATCH, T_DO => T_DO, T_ELSE => T_ELSE, T_ELSEIF => T_ELSEIF, T_FOR => T_FOR, T_FOREACH => T_FOREACH, T_IF => T_IF, T_SWITCH => T_SWITCH, T_WHILE => T_WHILE, ]; private const BOOLEAN_OPERATORS = [ T_BOOLEAN_AND => T_BOOLEAN_AND, T_BOOLEAN_OR => T_BOOLEAN_OR, ]; private const OPERATOR_CHAIN_BREAKS = [ T_OPEN_PARENTHESIS => T_OPEN_PARENTHESIS, T_CLOSE_PARENTHESIS => T_CLOSE_PARENTHESIS, T_SEMICOLON => T_SEMICOLON, T_INLINE_THEN => T_INLINE_THEN, T_INLINE_ELSE => T_INLINE_ELSE, ]; /** * B3. Nesting increments */ private const NESTING_INCREMENTS = [ T_CLOSURE => T_CLOSURE, // increments, but does not receive T_ELSEIF => T_ELSEIF, // increments, but does not receive T_ELSE => T_ELSE, T_IF => T_IF, T_INLINE_THEN => T_INLINE_THEN, T_SWITCH => T_SWITCH, T_FOR => T_FOR, T_FOREACH => T_FOREACH, T_WHILE => T_WHILE, T_DO => T_DO, T_CATCH => T_CATCH, ]; /** * B1. Increments */ private const BREAKING_TOKENS = [ T_CONTINUE => T_CONTINUE, T_GOTO => T_GOTO, T_BREAK => T_BREAK, ]; /** * @deprecated * @var ?int maximum allowed complexity */ public ?int $maxComplexity = null; /** @var int complexity which will raise warning */ public int $warningThreshold = 6; /** @var int complexity which will raise error */ public int $errorThreshold = 6; private int $cognitiveComplexity = 0; /** @var int|string */ private $lastBooleanOperator = 0; private File $phpcsFile; /** * @return array */ public function register(): array { return [ T_CLOSURE, T_FUNCTION, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $stackPtr */ public function process(File $phpcsFile, $stackPtr): void { $this->phpcsFile = $phpcsFile; if ($phpcsFile->getCondition($stackPtr, T_FUNCTION) !== false) { return; } if ($this->maxComplexity !== null) { // maxComplexity is deprecated... if set use it $this->warningThreshold = $this->maxComplexity + 1; $this->errorThreshold = $this->maxComplexity + 1; } $cognitiveComplexity = $this->computeForFunctionFromTokensAndPosition($stackPtr); if ($cognitiveComplexity < $this->warningThreshold) { return; } $name = $phpcsFile->getDeclarationName($stackPtr); $errorParameters = [ 'Cognitive complexity for "%s" is %d but has to be less than or equal to %d.', $stackPtr, self::CODE_COMPLEXITY, [ $name, $cognitiveComplexity, $this->warningThreshold - 1, ], ]; $cognitiveComplexity >= $this->errorThreshold ? $phpcsFile->addError(...$errorParameters) : $phpcsFile->addWarning(...$errorParameters); } public function computeForFunctionFromTokensAndPosition(int $position): int { if (FunctionHelper::isAbstract($this->phpcsFile, $position)) { return 0; } $tokens = $this->phpcsFile->getTokens(); // Detect start and end of this function definition $functionStartPosition = $tokens[$position]['scope_opener']; $functionEndPosition = $tokens[$position]['scope_closer']; $this->lastBooleanOperator = 0; $this->cognitiveComplexity = 0; /* Keep track of parser's level stack We push to this stak whenever we encounter a Tokens::$scopeOpeners */ $levelStack = []; /* We look for changes in token[level] to know when to remove from the stack however ['level'] only increases when there are tokens inside {} after pushing to the stack watch for a level change */ $levelIncreased = false; for ($i = $functionStartPosition + 1; $i < $functionEndPosition; $i++) { $currentToken = $tokens[$i]; $isNestingToken = false; if (in_array($currentToken['code'], Tokens::$scopeOpeners, true)) { $isNestingToken = true; if ($levelIncreased === false && count($levelStack) > 0) { // parser's level never increased // caused by empty condition such as `if ($x) { }` array_pop($levelStack); } $levelStack[] = $currentToken; $levelIncreased = false; } elseif (isset($tokens[$i - 1]) && $currentToken['level'] < $tokens[$i - 1]['level']) { $diff = $tokens[$i - 1]['level'] - $currentToken['level']; array_splice($levelStack, 0 - $diff); } elseif (isset($tokens[$i - 1]) && $currentToken['level'] > $tokens[$i - 1]['level']) { $levelIncreased = true; } $this->resolveBooleanOperatorChain($currentToken); if (!$this->isIncrementingToken($currentToken, $tokens, $i)) { continue; } $this->cognitiveComplexity++; $addNestingIncrement = isset(self::NESTING_INCREMENTS[$currentToken['code']]) && in_array($currentToken['code'], [T_ELSEIF, T_ELSE], true) === false; if (!$addNestingIncrement) { continue; } $measuredNestingLevel = count( array_filter($levelStack, static fn (array $token) => in_array($token['code'], self::NESTING_INCREMENTS, true)), ); if ($isNestingToken) { $measuredNestingLevel--; } // B3. Nesting increment if ($measuredNestingLevel > 0) { $this->cognitiveComplexity += $measuredNestingLevel; } } return $this->cognitiveComplexity; } protected function isPartOfDo(File $phpcsFile, int $whilePointer): bool { $tokens = $phpcsFile->getTokens(); $parenthesisCloserPointer = $tokens[$whilePointer]['parenthesis_closer']; $pointerAfterParenthesisCloser = TokenHelper::findNextEffective($phpcsFile, $parenthesisCloserPointer + 1); return $tokens[$pointerAfterParenthesisCloser]['code'] !== T_OPEN_CURLY_BRACKET; } /** * Keep track of consecutive matching boolean operators, that don't receive increment. * * @param array{code:int|string} $token */ private function resolveBooleanOperatorChain(array $token): void { $code = $token['code']; // Whenever we cross anything that interrupts possible condition we reset chain. if ($this->lastBooleanOperator > 0 && isset(self::OPERATOR_CHAIN_BREAKS[$code])) { $this->lastBooleanOperator = 0; return; } if (isset(self::BOOLEAN_OPERATORS[$code]) === false) { return; } // If we match last operator, there is no increment added for current one. if ($this->lastBooleanOperator === $code) { return; } $this->cognitiveComplexity++; $this->lastBooleanOperator = $code; } /** * @param array{code:int|string} $token * @param array|int|string>> $tokens */ private function isIncrementingToken(array $token, array $tokens, int $position): bool { $code = $token['code']; if (isset(self::INCREMENTS[$code])) { return $token['code'] === T_WHILE ? !$this->isPartOfDo($this->phpcsFile, $position) : true; } // B1. ternary operator if ($code === T_INLINE_THEN) { return true; } // B1. goto LABEL, break LABEL, continue LABEL if (isset(self::BREAKING_TOKENS[$code])) { $nextToken = $this->phpcsFile->findNext(Tokens::$emptyTokens, $position + 1, null, true); if ($nextToken === false || $tokens[$nextToken]['code'] !== T_SEMICOLON) { return true; } } return false; } } PK41]Z}JddIcoding-standard/SlevomatCodingStandard/Sniffs/Arrays/ArrayAccessSniff.phpnu[ */ public function register(): array { return [T_OPEN_SQUARE_BRACKET]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $stackPointer */ public function process(File $phpcsFile, $stackPointer): void { $tokens = $phpcsFile->getTokens(); $previousToken = TokenHelper::findPreviousNonWhitespace($phpcsFile, $stackPointer - 1); if ( $previousToken === null || $previousToken === $stackPointer - 1) { return; } if ($tokens[$previousToken]['code'] === T_VARIABLE) { $this->addError( $phpcsFile, $stackPointer, 'There should be no space between array variable and array access operator.', self::CODE_NO_SPACE_BEFORE_BRACKETS, ); } if ($tokens[$previousToken]['code'] !== T_CLOSE_SQUARE_BRACKET) { return; } $this->addError( $phpcsFile, $stackPointer, 'There should be no space between array access operators.', self::CODE_NO_SPACE_BETWEEN_BRACKETS, ); } private function addError(File $phpcsFile, int $stackPointer, string $error, string $code): void { $fix = $phpcsFile->addFixableError($error, $stackPointer, $code); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $stackPointer - 1, ''); $phpcsFile->fixer->endChangeset(); } } PK41]}Wcoding-standard/SlevomatCodingStandard/Sniffs/Arrays/SingleLineArrayWhitespaceSniff.phpnu[ */ public function register(): array { return TokenHelper::ARRAY_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $stackPointer */ public function process(File $phpcsFile, $stackPointer): int { $this->spacesAroundBrackets = SniffSettingsHelper::normalizeInteger($this->spacesAroundBrackets); $tokens = $phpcsFile->getTokens(); [$arrayOpenerPointer, $arrayCloserPointer] = ArrayHelper::openClosePointers($tokens[$stackPointer]); // Check only single-line arrays. if ($tokens[$arrayOpenerPointer]['line'] !== $tokens[$arrayCloserPointer]['line']) { return $arrayCloserPointer; } $pointerContent = TokenHelper::findNextNonWhitespace($phpcsFile, $arrayOpenerPointer + 1, $arrayCloserPointer + 1); if ($pointerContent === $arrayCloserPointer) { // Empty array, but if the brackets aren't together, there's a problem. if ($this->enableEmptyArrayCheck) { $this->checkWhitespaceInEmptyArray($phpcsFile, $arrayOpenerPointer, $arrayCloserPointer); } // We can return here because there is nothing else to check. // All code below can assume that the array is not empty. return $arrayCloserPointer + 1; } $this->checkWhitespaceAfterOpeningBracket($phpcsFile, $arrayOpenerPointer); $this->checkWhitespaceBeforeClosingBracket($phpcsFile, $arrayCloserPointer); for ($i = $arrayOpenerPointer + 1; $i < $arrayCloserPointer; $i++) { // Skip bracketed statements, like function calls. if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS) { $i = $tokens[$i]['parenthesis_closer']; continue; } // Skip nested arrays as they will be processed separately if (in_array($tokens[$i]['code'], TokenHelper::ARRAY_TOKEN_CODES, true)) { $i = ArrayHelper::openClosePointers($tokens[$i])[1]; continue; } if ($tokens[$i]['code'] !== T_COMMA) { continue; } // Before checking this comma, make sure we are not at the end of the array. $next = TokenHelper::findNextNonWhitespace($phpcsFile, $i + 1, $arrayCloserPointer); if ($next === null) { return $arrayOpenerPointer + 1; } $this->checkWhitespaceBeforeComma($phpcsFile, $i); $this->checkWhitespaceAfterComma($phpcsFile, $i); } return $arrayOpenerPointer + 1; } private function checkWhitespaceInEmptyArray(File $phpcsFile, int $arrayStart, int $arrayEnd): void { if ($arrayEnd - $arrayStart === 1) { return; } $error = 'Empty array declaration must have no space between the parentheses.'; $fix = $phpcsFile->addFixableError($error, $arrayStart, self::CODE_SPACE_IN_EMPTY_ARRAY); if (!$fix) { return; } FixerHelper::replace($phpcsFile, $arrayStart + 1, ''); } private function checkWhitespaceAfterOpeningBracket(File $phpcsFile, int $arrayStart): void { $tokens = $phpcsFile->getTokens(); $whitespacePointer = $arrayStart + 1; $spaceLength = 0; if ($tokens[$whitespacePointer]['code'] === T_WHITESPACE) { $spaceLength = $tokens[$whitespacePointer]['length']; } if ($spaceLength === $this->spacesAroundBrackets) { return; } $error = sprintf('Expected %d spaces after array opening bracket, %d found.', $this->spacesAroundBrackets, $spaceLength); $fix = $phpcsFile->addFixableError($error, $arrayStart, self::CODE_SPACE_AFTER_ARRAY_OPEN); if (!$fix) { return; } if ($spaceLength === 0) { FixerHelper::add($phpcsFile, $arrayStart, str_repeat(' ', $this->spacesAroundBrackets)); } else { FixerHelper::replace( $phpcsFile, $whitespacePointer, str_repeat(' ', $this->spacesAroundBrackets), ); } } private function checkWhitespaceBeforeClosingBracket(File $phpcsFile, int $arrayEnd): void { $tokens = $phpcsFile->getTokens(); $whitespacePointer = $arrayEnd - 1; $spaceLength = 0; if ($tokens[$whitespacePointer]['code'] === T_WHITESPACE) { $spaceLength = $tokens[$whitespacePointer]['length']; } if ($spaceLength === $this->spacesAroundBrackets) { return; } $error = sprintf('Expected %d spaces before array closing bracket, %d found.', $this->spacesAroundBrackets, $spaceLength); $fix = $phpcsFile->addFixableError($error, $arrayEnd, self::CODE_SPACE_BEFORE_ARRAY_CLOSE); if (!$fix) { return; } if ($spaceLength === 0) { FixerHelper::addBefore($phpcsFile, $arrayEnd, str_repeat(' ', $this->spacesAroundBrackets)); } else { FixerHelper::replace( $phpcsFile, $whitespacePointer, str_repeat(' ', $this->spacesAroundBrackets), ); } } private function checkWhitespaceBeforeComma(File $phpcsFile, int $comma): void { $tokens = $phpcsFile->getTokens(); if ($tokens[$comma - 1]['code'] !== T_WHITESPACE) { return; } if ($tokens[$comma - 2]['code'] === T_COMMA) { return; } $error = sprintf( 'Expected 0 spaces between "%s" and comma, %d found.', $tokens[$comma - 2]['content'], $tokens[$comma - 1]['length'], ); $fix = $phpcsFile->addFixableError($error, $comma, self::CODE_SPACE_BEFORE_COMMA); if (!$fix) { return; } FixerHelper::replace($phpcsFile, $comma - 1, ''); } private function checkWhitespaceAfterComma(File $phpcsFile, int $comma): void { $tokens = $phpcsFile->getTokens(); if ($tokens[$comma + 1]['code'] !== T_WHITESPACE) { $error = sprintf('Expected 1 space between comma and "%s", 0 found.', $tokens[$comma + 1]['content']); $fix = $phpcsFile->addFixableError($error, $comma, self::CODE_SPACE_AFTER_COMMA); if ($fix) { FixerHelper::add($phpcsFile, $comma, ' '); } return; } $spaceLength = $tokens[$comma + 1]['length']; if ($spaceLength === 1) { return; } $error = sprintf('Expected 1 space between comma and "%s", %d found.', $tokens[$comma + 2]['content'], $spaceLength); $fix = $phpcsFile->addFixableError($error, $comma, self::CODE_SPACE_AFTER_COMMA); if (!$fix) { return; } FixerHelper::replace($phpcsFile, $comma + 1, ' '); } } PK41]M`5Tcoding-standard/SlevomatCodingStandard/Sniffs/Arrays/DisallowPartiallyKeyedSniff.phpnu[ */ public function register(): array { return TokenHelper::ARRAY_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $stackPointer */ public function process(File $phpcsFile, $stackPointer): void { $keyValues = ArrayHelper::parse($phpcsFile, $stackPointer); if (!ArrayHelper::isKeyed($keyValues)) { return; } if (ArrayHelper::isKeyedAll($keyValues)) { return; } $phpcsFile->addError('Partially keyed array disallowed.', $stackPointer, self::CODE_DISALLOWED_PARTIALLY_KEYED); } } PK41]<Xcoding-standard/SlevomatCodingStandard/Sniffs/Arrays/AlphabeticallySortedByKeysSniff.phpnu[ */ public function register(): array { return TokenHelper::ARRAY_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $stackPointer */ public function process(File $phpcsFile, $stackPointer): void { if (ArrayHelper::isMultiLine($phpcsFile, $stackPointer) === false) { return; } // "Parse" the array... get info for each key/value pair $keyValues = ArrayHelper::parse($phpcsFile, $stackPointer); if (ArrayHelper::isKeyedAll($keyValues) === false) { return; } if (ArrayHelper::isSortedByKey($keyValues)) { return; } $fix = $phpcsFile->addFixableError( 'Keyed multi-line arrays must be sorted alphabetically.', $stackPointer, self::CODE_INCORRECT_KEY_ORDER, ); if ($fix) { $this->fix($phpcsFile, $keyValues); } } /** * @param list $keyValues */ private function fix(File $phpcsFile, array $keyValues): void { $pointerStart = $keyValues[0]->getPointerStart(); $pointerEnd = $keyValues[count($keyValues) - 1]->getPointerEnd(); // Determine indent to use $indent = ArrayHelper::getIndentation($keyValues); usort($keyValues, static fn ($a1, $a2) => strnatcasecmp((string) $a1->getKey(), (string) $a2->getKey())); $content = implode( '', array_map( static fn (ArrayKeyValue $keyValue) => $keyValue->getContent($phpcsFile, true, $indent) . $phpcsFile->eolChar, $keyValues, ), ); $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $pointerStart, $pointerEnd, $content); $phpcsFile->fixer->endChangeset(); } } PK41]6y=Pcoding-standard/SlevomatCodingStandard/Sniffs/Arrays/TrailingArrayCommaSniff.phpnu[ */ public function register(): array { return TokenHelper::ARRAY_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $stackPointer */ public function process(File $phpcsFile, $stackPointer): void { $this->enableAfterHeredoc = SniffSettingsHelper::isEnabledByPhpVersion($this->enableAfterHeredoc, 70300); $tokens = $phpcsFile->getTokens(); [$arrayOpenerPointer, $arrayCloserPointer] = ArrayHelper::openClosePointers($tokens[$stackPointer]); if ($tokens[$arrayOpenerPointer]['line'] === $tokens[$arrayCloserPointer]['line']) { return; } /** @var int $pointerPreviousToClose */ $pointerPreviousToClose = TokenHelper::findPreviousEffective($phpcsFile, $arrayCloserPointer - 1); $tokenPreviousToClose = $tokens[$pointerPreviousToClose]; if ( $pointerPreviousToClose === $arrayOpenerPointer || $tokenPreviousToClose['code'] === T_COMMA || $tokens[$arrayCloserPointer]['line'] === $tokenPreviousToClose['line'] ) { return; } if ( !$this->enableAfterHeredoc && in_array($tokenPreviousToClose['code'], [T_END_HEREDOC, T_END_NOWDOC], true) ) { return; } $fix = $phpcsFile->addFixableError( 'Multi-line arrays must have a trailing comma after the last element.', $pointerPreviousToClose, self::CODE_MISSING_TRAILING_COMMA, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::add($phpcsFile, $pointerPreviousToClose, ','); $phpcsFile->fixer->endChangeset(); } } PK41]ñ  _coding-standard/SlevomatCodingStandard/Sniffs/Arrays/MultiLineArrayEndBracketPlacementSniff.phpnu[ */ public function register(): array { return TokenHelper::ARRAY_TOKEN_CODES; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $stackPointer */ public function process(File $phpcsFile, $stackPointer): void { $tokens = $phpcsFile->getTokens(); if (ArrayHelper::isMultiLine($phpcsFile, $stackPointer) === false) { return; } [$arrayOpenerPointer, $arrayCloserPointer] = ArrayHelper::openClosePointers($tokens[$stackPointer]); $nextEffective = TokenHelper::findNextEffective($phpcsFile, $arrayOpenerPointer + 1, $arrayCloserPointer); if ($nextEffective === null || in_array($tokens[$nextEffective]['code'], TokenHelper::ARRAY_TOKEN_CODES, true) === false) { return; } [$nextPointerOpener, $nextPointerCloser] = ArrayHelper::openClosePointers($tokens[$nextEffective]); $arraysStartAtSameLine = $tokens[$arrayOpenerPointer]['line'] === $tokens[$nextPointerOpener]['line']; $arraysEndAtSameLine = $tokens[$arrayCloserPointer]['line'] === $tokens[$nextPointerCloser]['line']; if (!$arraysStartAtSameLine || $arraysEndAtSameLine) { return; } $error = "Expected nested array to end at the same line as it's parent. Either put the nested array's end at the same line as the parent's end, or put the nested array start on it's own line."; $fix = $phpcsFile->addFixableError($error, $arrayOpenerPointer, self::CODE_ARRAY_END_WRONG_PLACEMENT); if (!$fix) { return; } FixerHelper::add($phpcsFile, $arrayOpenerPointer, $phpcsFile->eolChar); } } PK41]H k k [coding-standard/SlevomatCodingStandard/Sniffs/Arrays/DisallowImplicitArrayCreationSniff.phpnu[ */ 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::FUNCTION_TOKEN_CODES, true)) { continue; } $scopeOwnerPointer = $conditionPointer; break; } $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::FUNCTION_TOKEN_CODES, 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; } } PK41]A},33Bcoding-standard/SlevomatCodingStandard/Helpers/NamespaceHelper.phpnu[ */ public static function getAllNamespacesPointers(File $phpcsFile): array { $tokens = $phpcsFile->getTokens(); $lazyValue = static function () use ($phpcsFile, $tokens): array { $all = TokenHelper::findNextAll($phpcsFile, T_NAMESPACE, 0); $all = array_filter( $all, static function ($pointer) use ($phpcsFile, $tokens) { $next = TokenHelper::findNextEffective($phpcsFile, $pointer + 1); return $next === null || $tokens[$next]['code'] !== T_NS_SEPARATOR; }, ); return array_values($all); }; return SniffLocalCache::getAndSetIfNotCached($phpcsFile, 'namespacePointers', $lazyValue); } public static function isFullyQualifiedName(string $typeName): bool { return StringHelper::startsWith($typeName, self::NAMESPACE_SEPARATOR); } public static function isFullyQualifiedPointer(File $phpcsFile, int $pointer): bool { return in_array($phpcsFile->getTokens()[$pointer]['code'], [T_NS_SEPARATOR, T_NAME_FULLY_QUALIFIED], true); } public static function getFullyQualifiedTypeName(string $typeName): string { if (self::isFullyQualifiedName($typeName)) { return $typeName; } return sprintf('%s%s', self::NAMESPACE_SEPARATOR, $typeName); } public static function hasNamespace(string $typeName): bool { $parts = self::getNameParts($typeName); return count($parts) > 1; } /** * @return list */ public static function getNameParts(string $name): array { $name = self::normalizeToCanonicalName($name); return explode(self::NAMESPACE_SEPARATOR, $name); } public static function getLastNamePart(string $name): string { return array_slice(self::getNameParts($name), -1)[0]; } public static function getName(File $phpcsFile, int $namespacePointer): string { /** @var int $namespaceNameStartPointer */ $namespaceNameStartPointer = TokenHelper::findNextEffective($phpcsFile, $namespacePointer + 1); $namespaceNameEndPointer = TokenHelper::findNextExcluding( $phpcsFile, TokenHelper::NAME_TOKEN_CODES, $namespaceNameStartPointer + 1, ) - 1; return TokenHelper::getContent($phpcsFile, $namespaceNameStartPointer, $namespaceNameEndPointer); } public static function findCurrentNamespacePointer(File $phpcsFile, int $pointer): ?int { $allNamespacesPointers = array_reverse(self::getAllNamespacesPointers($phpcsFile)); foreach ($allNamespacesPointers as $namespacesPointer) { if ($namespacesPointer < $pointer) { return $namespacesPointer; } } return null; } public static function findCurrentNamespaceName(File $phpcsFile, int $anyPointer): ?string { $namespacePointer = self::findCurrentNamespacePointer($phpcsFile, $anyPointer); if ($namespacePointer === null) { return null; } return self::getName($phpcsFile, $namespacePointer); } public static function getUnqualifiedNameFromFullyQualifiedName(string $name): string { $parts = self::getNameParts($name); return $parts[count($parts) - 1]; } public static function isQualifiedName(string $name): bool { return strpos($name, self::NAMESPACE_SEPARATOR) !== false; } public static function normalizeToCanonicalName(string $fullyQualifiedName): string { return ltrim($fullyQualifiedName, self::NAMESPACE_SEPARATOR); } public static function isTypeInNamespace(string $typeName, string $namespace): bool { return StringHelper::startsWith( self::normalizeToCanonicalName($typeName) . '\\', $namespace . '\\', ); } public static function resolveClassName(File $phpcsFile, string $nameAsReferencedInFile, int $currentPointer): string { return self::resolveName($phpcsFile, $nameAsReferencedInFile, ReferencedName::TYPE_CLASS, $currentPointer); } public static function resolveName(File $phpcsFile, string $nameAsReferencedInFile, string $type, int $currentPointer): string { if (self::isFullyQualifiedName($nameAsReferencedInFile)) { return $nameAsReferencedInFile; } $useStatements = UseStatementHelper::getUseStatementsForPointer($phpcsFile, $currentPointer); $uniqueId = UseStatement::getUniqueId($type, self::normalizeToCanonicalName($nameAsReferencedInFile)); if (isset($useStatements[$uniqueId])) { return sprintf('%s%s', self::NAMESPACE_SEPARATOR, $useStatements[$uniqueId]->getFullyQualifiedTypeName()); } $nameParts = self::getNameParts($nameAsReferencedInFile); $firstPartUniqueId = UseStatement::getUniqueId($type, $nameParts[0]); if (count($nameParts) > 1 && isset($useStatements[$firstPartUniqueId])) { return sprintf( '%s%s%s%s', self::NAMESPACE_SEPARATOR, $useStatements[$firstPartUniqueId]->getFullyQualifiedTypeName(), self::NAMESPACE_SEPARATOR, implode(self::NAMESPACE_SEPARATOR, array_slice($nameParts, 1)), ); } $name = sprintf('%s%s', self::NAMESPACE_SEPARATOR, $nameAsReferencedInFile); if ($type === ReferencedName::TYPE_CONSTANT && defined($name)) { return $name; } $namespaceName = self::findCurrentNamespaceName($phpcsFile, $currentPointer); if ($namespaceName !== null) { $name = sprintf('%s%s%s', self::NAMESPACE_SEPARATOR, $namespaceName, $name); } return $name; } } PK41]ۭAcoding-standard/SlevomatCodingStandard/Helpers/VariableHelper.phpnu[getTokens(); if ($tokens[$variablePointer]['content'] !== $tokens[$variableToCheckPointer]['content']) { return false; } if ($tokens[$variableToCheckPointer - 1]['code'] === T_DOUBLE_COLON) { $pointerAfterVariable = TokenHelper::findNextEffective($phpcsFile, $variableToCheckPointer + 1); return $tokens[$pointerAfterVariable]['code'] === T_OPEN_PARENTHESIS; } return !ParameterHelper::isParameter($phpcsFile, $variableToCheckPointer); } public static function isUsedInCompactFunction(File $phpcsFile, int $variablePointer, int $stringPointer): bool { $tokens = $phpcsFile->getTokens(); $stringContent = $tokens[$stringPointer]['content']; if (strtolower($stringContent) !== 'compact') { return false; } $parenthesisOpenerPointer = TokenHelper::findNextEffective($phpcsFile, $stringPointer + 1); if ($tokens[$parenthesisOpenerPointer]['code'] !== T_OPEN_PARENTHESIS) { return false; } $variableNameWithoutDollar = substr($tokens[$variablePointer]['content'], 1); for ($i = $parenthesisOpenerPointer + 1; $i < $tokens[$parenthesisOpenerPointer]['parenthesis_closer']; $i++) { if (preg_match('~^([\'"])' . $variableNameWithoutDollar . '\\1$~', $tokens[$i]['content']) !== 0) { return true; } } return false; } public static function isUsedInScopeInString(File $phpcsFile, string $variableName, int $stringPointer): bool { $tokens = $phpcsFile->getTokens(); $stringContent = $tokens[$stringPointer]['content']; if (preg_match('~(\\\\)?(' . preg_quote($variableName, '~') . ')\b~', $stringContent, $matches) === 1) { if ($matches[1] === '') { return true; } /** @phpstan-ignore-next-line */ if (strlen($matches[1]) % 2 === 1) { return true; } } $variableNameWithoutDollar = substr($variableName, 1); return preg_match('~\$\{' . preg_quote($variableNameWithoutDollar, '~') . '(<=\}|\b)~', $stringContent) !== 0; } private static function isUsedInScopeInternal( File $phpcsFile, int $scopeOwnerPointer, int $variablePointer, ?int $startCheckPointer ): bool { $tokens = $phpcsFile->getTokens(); if ($tokens[$scopeOwnerPointer]['code'] === T_OPEN_TAG) { $scopeCloserPointer = count($tokens) - 1; } elseif ($tokens[$scopeOwnerPointer]['code'] === T_FN) { $scopeCloserPointer = $tokens[$scopeOwnerPointer]['scope_closer']; } else { $scopeCloserPointer = $tokens[$scopeOwnerPointer]['scope_closer'] - 1; } if ($tokens[$scopeOwnerPointer]['code'] === T_OPEN_TAG) { $firstPointerInScope = $scopeOwnerPointer + 1; } elseif ($tokens[$scopeOwnerPointer]['code'] === T_FN) { $firstPointerInScope = $tokens[$scopeOwnerPointer]['scope_opener']; } else { $firstPointerInScope = $tokens[$scopeOwnerPointer]['scope_opener'] + 1; } $startCheckPointer ??= $firstPointerInScope; for ($i = $startCheckPointer; $i <= $scopeCloserPointer; $i++) { if (!ScopeHelper::isInSameScope($phpcsFile, $i, $firstPointerInScope)) { continue; } if ( $tokens[$i]['code'] === T_VARIABLE && self::isUsedAsVariable($phpcsFile, $variablePointer, $i) ) { return true; } if ($tokens[$i]['code'] === T_STRING) { if (self::isGetDefinedVarsCall($phpcsFile, $i)) { return true; } if (self::isUsedInCompactFunction($phpcsFile, $variablePointer, $i)) { return true; } } if ( in_array($tokens[$i]['code'], [T_DOUBLE_QUOTED_STRING, T_HEREDOC], true) && self::isUsedInScopeInString($phpcsFile, $tokens[$variablePointer]['content'], $i) ) { return true; } } return false; } private static function isGetDefinedVarsCall(File $phpcsFile, int $stringPointer): bool { $tokens = $phpcsFile->getTokens(); $stringContent = $tokens[$stringPointer]['content']; if (strtolower($stringContent) !== 'get_defined_vars') { return false; } $parenthesisOpenerPointer = TokenHelper::findNextEffective($phpcsFile, $stringPointer + 1); return $tokens[$parenthesisOpenerPointer]['code'] === T_OPEN_PARENTHESIS; } } PK41]**Acoding-standard/SlevomatCodingStandard/Helpers/TypeHintHelper.phpnu[ 'int', 'boolean' => 'bool', ]; return array_key_exists($typeHint, $longToShort) ? $longToShort[$typeHint] : $typeHint; } public static function isUnofficialUnionTypeHint(string $typeHint): bool { return in_array($typeHint, ['scalar', 'numeric', 'array-key'], true); } public static function isVoidTypeHint(string $typeHint): bool { return $typeHint === 'void'; } public static function isNeverTypeHint(string $typeHint): bool { return in_array($typeHint, ['never', 'never-return', 'never-returns', 'no-return'], true); } /** * @return list */ public static function convertUnofficialUnionTypeHintToOfficialTypeHints(string $typeHint): array { $conversion = [ 'scalar' => ['string', 'int', 'float', 'bool'], 'numeric' => ['int', 'float', 'string'], 'array-key' => ['int', 'string'], ]; return $conversion[$typeHint]; } public static function isTypeDefinedInAnnotation(File $phpcsFile, int $pointer, string $typeHint): bool { $docCommentOpenPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $pointer); if ($docCommentOpenPointer === null) { return false; } return self::isTemplate($phpcsFile, $docCommentOpenPointer, $typeHint) || self::isAlias($phpcsFile, $docCommentOpenPointer, $typeHint); } public static function getFullyQualifiedTypeHint(File $phpcsFile, int $pointer, string $typeHint): string { if (self::isSimpleTypeHint($typeHint)) { return self::convertLongSimpleTypeHintToShort($typeHint); } return NamespaceHelper::resolveClassName($phpcsFile, $typeHint, $pointer); } /** * @return list */ public static function getSimpleTypeHints(): array { static $simpleTypeHints; $simpleTypeHints ??= [ 'int', 'integer', 'false', 'float', 'string', 'bool', 'boolean', 'callable', 'self', 'array', 'iterable', 'void', 'never', ]; return $simpleTypeHints; } /** * @return list */ public static function getSimpleIterableTypeHints(): array { return [ 'array', 'iterable', ]; } public static function isSimpleUnofficialTypeHints(string $typeHint): bool { static $simpleUnofficialTypeHints; // See https://psalm.dev/docs/annotating_code/type_syntax/atomic_types/ $simpleUnofficialTypeHints ??= [ 'null', 'mixed', 'scalar', 'numeric', 'true', 'object', 'resource', 'static', '$this', 'array-key', 'list', 'non-empty-array', 'non-empty-list', 'empty', 'positive-int', 'non-positive-int', 'negative-int', 'non-negative-int', 'literal-int', 'int-mask', 'min', 'max', 'callable-array', 'callable-string', ]; return in_array($typeHint, $simpleUnofficialTypeHints, true) || preg_match('~-string$~i', $typeHint) === 1; } /** * @param list $traversableTypeHints */ public static function isTraversableType(string $type, array $traversableTypeHints): bool { return self::isSimpleIterableTypeHint($type) || in_array($type, $traversableTypeHints, true); } public static function typeHintEqualsAnnotation( File $phpcsFile, int $functionPointer, string $typeHint, string $typeHintInAnnotation ): bool { /** @var list $typeHintParts */ $typeHintParts = preg_split('~([&|])~', self::normalize($typeHint), -1, PREG_SPLIT_DELIM_CAPTURE); /** @var list $typeHintInAnnotationParts */ $typeHintInAnnotationParts = preg_split('~([&|])~', self::normalize($typeHintInAnnotation), -1, PREG_SPLIT_DELIM_CAPTURE); if (count($typeHintParts) !== count($typeHintInAnnotationParts)) { return false; } for ($i = 0; $i < count($typeHintParts); $i++) { if ( ( $typeHintParts[$i] === '|' || $typeHintParts[$i] === '&' ) && $typeHintParts[$i] !== $typeHintInAnnotationParts[$i] ) { return false; } if (self::getFullyQualifiedTypeHint($phpcsFile, $functionPointer, $typeHintParts[$i]) !== self::getFullyQualifiedTypeHint( $phpcsFile, $functionPointer, $typeHintInAnnotationParts[$i], )) { return false; } } return true; } public static function getStartPointer(File $phpcsFile, int $endPointer): int { $previousPointer = TokenHelper::findPreviousExcluding( $phpcsFile, [T_WHITESPACE, ...TokenHelper::TYPE_HINT_TOKEN_CODES], $endPointer - 1, ); return TokenHelper::findNextNonWhitespace($phpcsFile, $previousPointer + 1); } private static function isTemplate(File $phpcsFile, int $docCommentOpenPointer, string $typeHint): bool { static $templateAnnotationNames = null; if ($templateAnnotationNames === null) { foreach (['template', 'template-covariant'] as $annotationName) { $templateAnnotationNames[] = sprintf('@%s', $annotationName); foreach (AnnotationHelper::STATIC_ANALYSIS_PREFIXES as $prefixAnnotationName) { $templateAnnotationNames[] = sprintf('@%s-%s', $prefixAnnotationName, $annotationName); } } } $containsTypeHintInTemplateAnnotation = static function (int $docCommentOpenPointer) use ($phpcsFile, $templateAnnotationNames, $typeHint): bool { foreach ($templateAnnotationNames as $templateAnnotationName) { /** @var list> $annotations */ $annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenPointer, $templateAnnotationName); foreach ($annotations as $templateAnnotation) { if ($templateAnnotation->isInvalid()) { continue; } if ($templateAnnotation->getValue()->name === $typeHint) { return true; } } } return false; }; $tokens = $phpcsFile->getTokens(); $docCommentOwnerPointer = DocCommentHelper::findDocCommentOwnerPointer($phpcsFile, $docCommentOpenPointer); if ($docCommentOwnerPointer !== null) { if (in_array($tokens[$docCommentOwnerPointer]['code'], TokenHelper::CLASS_TYPE_TOKEN_CODES, true)) { return $containsTypeHintInTemplateAnnotation($docCommentOpenPointer); } if ($tokens[$docCommentOwnerPointer]['code'] === T_FUNCTION && $containsTypeHintInTemplateAnnotation($docCommentOpenPointer)) { return true; } } $classPointer = ClassHelper::getClassPointer($phpcsFile, $docCommentOpenPointer); if ($classPointer === null) { return false; } $classDocCommentOpenPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $classPointer); if ($classDocCommentOpenPointer === null) { return false; } return $containsTypeHintInTemplateAnnotation($classDocCommentOpenPointer); } private static function isAlias(File $phpcsFile, int $docCommentOpenPointer, string $typeHint): bool { static $aliasAnnotationNames = null; if ($aliasAnnotationNames === null) { foreach (['type', 'import-type'] as $annotationName) { foreach (AnnotationHelper::STATIC_ANALYSIS_PREFIXES as $prefixAnnotationName) { $aliasAnnotationNames[] = sprintf('@%s-%s', $prefixAnnotationName, $annotationName); } } } $classPointer = ClassHelper::getClassPointer($phpcsFile, $docCommentOpenPointer); if ($classPointer === null) { return false; } $classDocCommentOpenPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $classPointer); if ($classDocCommentOpenPointer === null) { return false; } foreach ($aliasAnnotationNames as $aliasAnnotationName) { $annotations = AnnotationHelper::getAnnotations($phpcsFile, $classDocCommentOpenPointer, $aliasAnnotationName); foreach ($annotations as $aliasAnnotation) { $aliasAnnotationValue = $aliasAnnotation->getValue(); if ($aliasAnnotationValue instanceof TypeAliasTagValueNode && $aliasAnnotationValue->alias === $typeHint) { return true; } if (!($aliasAnnotationValue instanceof TypeAliasImportTagValueNode)) { continue; } if ($aliasAnnotationValue->importedAs === $typeHint) { return true; } if ($aliasAnnotationValue->importedAlias === $typeHint) { return true; } } } return false; } private static function normalize(string $typeHint): string { if (StringHelper::startsWith($typeHint, '?')) { $typeHint = substr($typeHint, 1) . '|null'; } if (self::isNeverTypeHint($typeHint)) { return 'never'; } /** @var list $parts */ $parts = preg_split('~([&|])~', $typeHint, -1, PREG_SPLIT_DELIM_CAPTURE); $hints = []; $delimiter = '|'; foreach ($parts as $part) { if ($part === '|' || $part === '&') { $delimiter = $part; continue; } $hints[] = $part; } if (in_array('mixed', $hints, true)) { return 'mixed'; } $convertedHints = []; foreach ($hints as $hint) { if (self::isUnofficialUnionTypeHint($hint) && $delimiter !== '&') { $convertedHints = array_merge($convertedHints, self::convertUnofficialUnionTypeHintToOfficialTypeHints($hint)); } else { $convertedHints[] = $hint; } } $convertedHints = array_unique($convertedHints); if (count($convertedHints) > 1) { $convertedHints = array_map(static fn (string $part): string => self::isVoidTypeHint($part) ? 'null' : $part, $convertedHints); } sort($convertedHints); return implode($delimiter, $convertedHints); } } PK41]!3JJEcoding-standard/SlevomatCodingStandard/Helpers/UseStatementHelper.phpnu[getTokens(); $nextPointer = TokenHelper::findNextEffective($phpcsFile, $usePointer + 1); // Anonymous function use if ($tokens[$nextPointer]['code'] === T_OPEN_PARENTHESIS) { return false; } if ( $tokens[$nextPointer]['code'] === T_STRING && in_array(strtolower($tokens[$nextPointer]['content']), ['function', 'const'], true) ) { return true; } $previousPointer = TokenHelper::findPrevious( $phpcsFile, [T_OPEN_TAG, T_DECLARE, T_NAMESPACE, T_OPEN_CURLY_BRACKET, T_CLOSE_CURLY_BRACKET], $usePointer, ); if (in_array($tokens[$previousPointer]['code'], [T_OPEN_TAG, T_DECLARE, T_NAMESPACE], true)) { return true; } if (array_key_exists('scope_condition', $tokens[$previousPointer])) { $scopeConditionPointer = $tokens[$previousPointer]['scope_condition']; if ( $tokens[$previousPointer]['code'] === T_OPEN_CURLY_BRACKET && in_array($tokens[$scopeConditionPointer]['code'], TokenHelper::CLASS_TYPE_WITH_ANONYMOUS_CLASS_TOKEN_CODES, true) ) { return false; } // Trait use after another trait use if ($tokens[$scopeConditionPointer]['code'] === T_USE) { return false; } // Trait use after method or import use after function if ($tokens[$scopeConditionPointer]['code'] === T_FUNCTION) { return ClassHelper::getClassPointer($phpcsFile, $usePointer) === null; } } return true; } public static function isTraitUse(File $phpcsFile, int $usePointer): bool { $tokens = $phpcsFile->getTokens(); $nextPointer = TokenHelper::findNextEffective($phpcsFile, $usePointer + 1); // Anonymous function use if ($tokens[$nextPointer]['code'] === T_OPEN_PARENTHESIS) { return false; } return !self::isImportUse($phpcsFile, $usePointer); } public static function getAlias(File $phpcsFile, int $usePointer): ?string { $endPointer = TokenHelper::findNext($phpcsFile, [T_SEMICOLON, T_COMMA], $usePointer + 1); $asPointer = TokenHelper::findNext($phpcsFile, T_AS, $usePointer + 1, $endPointer); if ($asPointer === null) { return null; } $tokens = $phpcsFile->getTokens(); return $tokens[TokenHelper::findNext($phpcsFile, T_STRING, $asPointer + 1)]['content']; } public static function getNameAsReferencedInClassFromUse(File $phpcsFile, int $usePointer): string { $alias = self::getAlias($phpcsFile, $usePointer); if ($alias !== null) { return $alias; } $name = self::getFullyQualifiedTypeNameFromUse($phpcsFile, $usePointer); return NamespaceHelper::getUnqualifiedNameFromFullyQualifiedName($name); } public static function getFullyQualifiedTypeNameFromUse(File $phpcsFile, int $usePointer): string { $tokens = $phpcsFile->getTokens(); $nameEndPointer = TokenHelper::findNext($phpcsFile, [T_SEMICOLON, T_AS, T_COMMA], $usePointer + 1) - 1; if (in_array($tokens[$nameEndPointer]['code'], TokenHelper::INEFFECTIVE_TOKEN_CODES, true)) { $nameEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $nameEndPointer); } $nameStartPointer = TokenHelper::findPreviousExcluding($phpcsFile, TokenHelper::NAME_TOKEN_CODES, $nameEndPointer - 1) + 1; $name = TokenHelper::getContent($phpcsFile, $nameStartPointer, $nameEndPointer); return NamespaceHelper::normalizeToCanonicalName($name); } /** * @return array */ public static function getUseStatementsForPointer(File $phpcsFile, int $pointer): array { $allUseStatements = self::getFileUseStatements($phpcsFile); if (count($allUseStatements) === 1) { return current($allUseStatements); } foreach (array_reverse($allUseStatements, true) as $pointerBeforeUseStatements => $useStatements) { if ($pointerBeforeUseStatements < $pointer) { return $useStatements; } } return []; } /** * @return array> */ public static function getFileUseStatements(File $phpcsFile): array { $lazyValue = static function () use ($phpcsFile): array { $useStatements = []; $tokens = $phpcsFile->getTokens(); $namespaceAndOpenTagPointers = TokenHelper::findNextAll($phpcsFile, [T_OPEN_TAG, T_NAMESPACE], 0); $openTagPointer = $namespaceAndOpenTagPointers[0]; foreach (self::getUseStatementPointers($phpcsFile, $openTagPointer) as $usePointer) { $pointerBeforeUseStatements = $openTagPointer; if (count($namespaceAndOpenTagPointers) > 1) { foreach (array_reverse($namespaceAndOpenTagPointers) as $namespaceAndOpenTagPointer) { if ($namespaceAndOpenTagPointer < $usePointer) { $pointerBeforeUseStatements = $namespaceAndOpenTagPointer; break; } } } $nextTokenFromUsePointer = TokenHelper::findNextEffective($phpcsFile, $usePointer + 1); $type = UseStatement::TYPE_CLASS; if ($tokens[$nextTokenFromUsePointer]['code'] === T_STRING) { if ($tokens[$nextTokenFromUsePointer]['content'] === 'const') { $type = UseStatement::TYPE_CONSTANT; } elseif ($tokens[$nextTokenFromUsePointer]['content'] === 'function') { $type = UseStatement::TYPE_FUNCTION; } } $name = self::getNameAsReferencedInClassFromUse($phpcsFile, $usePointer); $useStatement = new UseStatement( $name, self::getFullyQualifiedTypeNameFromUse($phpcsFile, $usePointer), $usePointer, $type, self::getAlias($phpcsFile, $usePointer), ); $useStatements[$pointerBeforeUseStatements][UseStatement::getUniqueId($type, $name)] = $useStatement; } return $useStatements; }; return SniffLocalCache::getAndSetIfNotCached($phpcsFile, 'useStatements', $lazyValue); } public static function getUseStatementPointer(File $phpcsFile, int $pointer): ?int { $pointers = self::getUseStatementPointers($phpcsFile, 0); foreach (array_reverse($pointers) as $pointerBeforeUseStatements) { if ($pointerBeforeUseStatements < $pointer) { return $pointerBeforeUseStatements; } } return null; } /** * Searches for all use statements in a file, skips bodies of classes and traits. * * @return list */ private static function getUseStatementPointers(File $phpcsFile, int $openTagPointer): array { $lazy = static function () use ($phpcsFile, $openTagPointer): array { $tokens = $phpcsFile->getTokens(); $pointer = $openTagPointer + 1; $pointers = []; while (true) { $pointer = TokenHelper::findNext($phpcsFile, [T_USE, ...TokenHelper::CLASS_TYPE_TOKEN_CODES], $pointer); if ($pointer === null) { break; } $token = $tokens[$pointer]; if (in_array($token['code'], TokenHelper::CLASS_TYPE_TOKEN_CODES, true)) { $pointer = $token['scope_closer'] + 1; continue; } if (self::isGroupUse($phpcsFile, $pointer)) { $pointer++; continue; } if (!self::isImportUse($phpcsFile, $pointer)) { $pointer++; continue; } $pointers[] = $pointer; $pointer++; } return $pointers; }; return SniffLocalCache::getAndSetIfNotCached($phpcsFile, 'useStatementPointers', $lazy); } private static function isGroupUse(File $phpcsFile, int $usePointer): bool { $tokens = $phpcsFile->getTokens(); $semicolonOrGroupUsePointer = TokenHelper::findNext($phpcsFile, [T_SEMICOLON, T_OPEN_USE_GROUP], $usePointer + 1); return $tokens[$semicolonOrGroupUsePointer]['code'] === T_OPEN_USE_GROUP; } } PK41]ːScoding-standard/SlevomatCodingStandard/Helpers/TokenPointerOutOfBoundsException.phpnu[pointer = $pointer; $this->lastTokenPointer = $lastTokenPointer; } public function getPointer(): int { return $this->pointer; } public function getLastTokenPointer(): int { return $this->lastTokenPointer; } } PK41]?k;:coding-standard/SlevomatCodingStandard/Helpers/Comment.phpnu[pointer = $pointer; $this->content = $content; } public function getPointer(): int { return $this->pointer; } public function getContent(): string { return $this->content; } } PK41]f<<Gcoding-standard/SlevomatCodingStandard/Helpers/ReferencedNameHelper.phpnu[ */ public static function getAllReferencedNames(File $phpcsFile, int $openTagPointer): array { $lazyValue = static fn (): array => self::createAllReferencedNames($phpcsFile, $openTagPointer); return SniffLocalCache::getAndSetIfNotCached($phpcsFile, 'references', $lazyValue); } /** * @return list */ public static function getAllReferencedNamesInAttributes(File $phpcsFile, int $openTagPointer): array { $lazyValue = static fn (): array => self::createAllReferencedNamesInAttributes($phpcsFile, $openTagPointer); return SniffLocalCache::getAndSetIfNotCached($phpcsFile, 'referencesFromAttributes', $lazyValue); } public static function getReferenceName(File $phpcsFile, int $nameStartPointer, int $nameEndPointer): string { $tokens = $phpcsFile->getTokens(); $referencedName = ''; for ($i = $nameStartPointer; $i <= $nameEndPointer; $i++) { if (in_array($tokens[$i]['code'], Tokens::$emptyTokens, true)) { continue; } $referencedName .= $tokens[$i]['content']; } return $referencedName; } public static function getReferencedNameEndPointer(File $phpcsFile, int $startPointer): int { $tokens = $phpcsFile->getTokens(); $nameTokenCodesWithWhitespace = [...TokenHelper::NAME_TOKEN_CODES, ...TokenHelper::INEFFECTIVE_TOKEN_CODES]; $lastNamePointer = $startPointer; for ($i = $startPointer + 1; $i < count($tokens); $i++) { if (!in_array($tokens[$i]['code'], $nameTokenCodesWithWhitespace, true)) { break; } if (!in_array($tokens[$i]['code'], TokenHelper::NAME_TOKEN_CODES, true)) { continue; } $lastNamePointer = $i; } return $lastNamePointer; } /** * @return list */ private static function createAllReferencedNames(File $phpcsFile, int $openTagPointer): array { $referencedNames = []; $beginSearchAtPointer = $openTagPointer + 1; $nameTokenCodes = TokenHelper::NAME_TOKEN_CODES; $nameTokenCodes[] = T_DOUBLE_QUOTED_STRING; $nameTokenCodes[] = T_HEREDOC; $tokens = $phpcsFile->getTokens(); while (true) { $nameStartPointer = TokenHelper::findNext($phpcsFile, $nameTokenCodes, $beginSearchAtPointer); if ($nameStartPointer === null) { break; } // Find referenced names inside double quotes string if (self::isNeedParsedContent($tokens[$nameStartPointer]['code'])) { $content = $tokens[$nameStartPointer]['content']; $currentPointer = $nameStartPointer + 1; while (self::isNeedParsedContent($tokens[$currentPointer]['code'])) { $content .= $tokens[$currentPointer]['content']; $currentPointer++; } $names = self::getReferencedNamesFromString($content); foreach ($names as $name) { $referencedNames[] = new ReferencedName($name, $nameStartPointer, $nameStartPointer, ReferencedName::TYPE_CLASS); } $beginSearchAtPointer = $currentPointer; continue; } // Attributes are parsed in specific method $attributeStartPointerBefore = TokenHelper::findPrevious($phpcsFile, T_ATTRIBUTE, $nameStartPointer - 1, $beginSearchAtPointer); if ($attributeStartPointerBefore !== null) { if ($tokens[$attributeStartPointerBefore]['attribute_closer'] > $nameStartPointer) { $beginSearchAtPointer = $tokens[$attributeStartPointerBefore]['attribute_closer'] + 1; continue; } } if (!self::isReferencedName($phpcsFile, $nameStartPointer)) { /** @var int $beginSearchAtPointer */ $beginSearchAtPointer = TokenHelper::findNextExcluding( $phpcsFile, [...TokenHelper::INEFFECTIVE_TOKEN_CODES, ...$nameTokenCodes], $nameStartPointer + 1, ); continue; } $nameEndPointer = self::getReferencedNameEndPointer($phpcsFile, $nameStartPointer); $referencedNames[] = new ReferencedName( self::getReferenceName($phpcsFile, $nameStartPointer, $nameEndPointer), $nameStartPointer, $nameEndPointer, self::getReferenceType($phpcsFile, $nameStartPointer, $nameEndPointer), ); $beginSearchAtPointer = $nameEndPointer + 1; } return $referencedNames; } private static function getReferenceType(File $phpcsFile, int $nameStartPointer, int $nameEndPointer): string { $tokens = $phpcsFile->getTokens(); $nextTokenAfterEndPointer = TokenHelper::findNextEffective($phpcsFile, $nameEndPointer + 1); $previousTokenBeforeStartPointer = TokenHelper::findPreviousEffective($phpcsFile, $nameStartPointer - 1); if ($tokens[$nextTokenAfterEndPointer]['code'] === T_OPEN_PARENTHESIS) { return $tokens[$previousTokenBeforeStartPointer]['code'] === T_NEW ? ReferencedName::TYPE_CLASS : ReferencedName::TYPE_FUNCTION; } if ( $tokens[$previousTokenBeforeStartPointer]['code'] === T_TYPE_UNION || $tokens[$nextTokenAfterEndPointer]['code'] === T_TYPE_UNION ) { return ReferencedName::TYPE_CLASS; } if ( $tokens[$previousTokenBeforeStartPointer]['code'] === T_TYPE_INTERSECTION || $tokens[$nextTokenAfterEndPointer]['code'] === T_TYPE_INTERSECTION ) { return ReferencedName::TYPE_CLASS; } if ($tokens[$nextTokenAfterEndPointer]['code'] === T_BITWISE_AND) { $tokenAfterNextToken = TokenHelper::findNextEffective($phpcsFile, $nextTokenAfterEndPointer + 1); return in_array($tokens[$tokenAfterNextToken]['code'], [T_VARIABLE, T_ELLIPSIS], true) ? ReferencedName::TYPE_CLASS : ReferencedName::TYPE_CONSTANT; } if ( in_array($tokens[$nextTokenAfterEndPointer]['code'], [ T_VARIABLE, // Variadic parameter T_ELLIPSIS, ], true) ) { return ReferencedName::TYPE_CLASS; } if ($tokens[$previousTokenBeforeStartPointer]['code'] === T_COLON) { $previousTokenPointer = TokenHelper::findPreviousEffective($phpcsFile, $previousTokenBeforeStartPointer - 1); if ( $tokens[$previousTokenPointer]['code'] === T_PARAM_NAME && $tokens[$nextTokenAfterEndPointer]['code'] !== T_DOUBLE_COLON ) { return ReferencedName::TYPE_CONSTANT; } // Return type hint return ReferencedName::TYPE_CLASS; } if ( in_array($tokens[$previousTokenBeforeStartPointer]['code'], [ T_EXTENDS, T_IMPLEMENTS, T_INSTANCEOF, // Trait T_USE, T_NEW, // Nullable type hint T_NULLABLE, ], true) || $tokens[$nextTokenAfterEndPointer]['code'] === T_DOUBLE_COLON ) { return ReferencedName::TYPE_CLASS; } if ($tokens[$previousTokenBeforeStartPointer]['code'] === T_COMMA) { $previousTokenPointer = TokenHelper::findPreviousExcluding( $phpcsFile, [T_COMMA, ...TokenHelper::NAME_TOKEN_CODES, ...TokenHelper::INEFFECTIVE_TOKEN_CODES], $previousTokenBeforeStartPointer - 1, ); return in_array($tokens[$previousTokenPointer]['code'], [ T_IMPLEMENTS, T_EXTENDS, T_USE, ], true) ? ReferencedName::TYPE_CLASS : ReferencedName::TYPE_CONSTANT; } if (in_array($tokens[$previousTokenBeforeStartPointer]['code'], [T_BITWISE_OR, T_OPEN_PARENTHESIS], true)) { $catchPointer = TokenHelper::findPreviousExcluding( $phpcsFile, [T_BITWISE_OR, T_OPEN_PARENTHESIS, ...TokenHelper::NAME_TOKEN_CODES, ...TokenHelper::INEFFECTIVE_TOKEN_CODES], $previousTokenBeforeStartPointer - 1, ); if ($tokens[$catchPointer]['code'] === T_CATCH) { return ReferencedName::TYPE_CLASS; } } return ReferencedName::TYPE_CONSTANT; } private static function isReferencedName(File $phpcsFile, int $startPointer): bool { $tokens = $phpcsFile->getTokens(); $nextPointer = TokenHelper::findNextEffective($phpcsFile, $startPointer + 1); $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $startPointer - 1); if ($nextPointer !== null && $tokens[$nextPointer]['code'] === T_DOUBLE_COLON) { return !in_array($tokens[$previousPointer]['code'], [T_OBJECT_OPERATOR, T_NULLSAFE_OBJECT_OPERATOR], true); } if ( count($tokens[$startPointer]['conditions']) > 0 && array_values(array_reverse($tokens[$startPointer]['conditions']))[0] === T_USE ) { // Method imported from trait return false; } $previousToken = $tokens[$previousPointer]; $skipTokenCodes = [ T_FUNCTION, T_DOUBLE_COLON, T_OBJECT_OPERATOR, T_NULLSAFE_OBJECT_OPERATOR, T_NAMESPACE, T_CONST, T_ENUM_CASE, ]; if ($previousToken['code'] === T_USE) { $classPointer = TokenHelper::findPrevious($phpcsFile, [T_CLASS, T_TRAIT, T_ANON_CLASS, T_ENUM], $startPointer - 1); if ($classPointer !== null) { $classToken = $tokens[$classPointer]; return $startPointer > $classToken['scope_opener'] && $startPointer < $classToken['scope_closer']; } return false; } if ( $previousToken['code'] === T_OPEN_PARENTHESIS && isset($previousToken['parenthesis_owner']) && $tokens[$previousToken['parenthesis_owner']]['code'] === T_DECLARE ) { return false; } if ( $previousToken['code'] === T_COMMA && TokenHelper::findPreviousLocal($phpcsFile, T_DECLARE, $previousPointer - 1) !== null ) { return false; } if ($previousToken['code'] === T_COMMA) { $constPointer = TokenHelper::findPreviousLocal($phpcsFile, T_CONST, $previousPointer - 1); if ( $constPointer !== null && TokenHelper::findNext($phpcsFile, [T_OPEN_SHORT_ARRAY, T_ARRAY], $constPointer + 1, $startPointer) === null ) { return false; } } elseif ($previousToken['code'] === T_BITWISE_AND) { $pointerBefore = TokenHelper::findPreviousEffective($phpcsFile, $previousPointer - 1); $isFunctionPointerBefore = TokenHelper::findPreviousLocal($phpcsFile, T_FUNCTION, $previousPointer - 1) !== null; if ($tokens[$pointerBefore]['code'] !== T_VARIABLE && $isFunctionPointerBefore) { return false; } } elseif ($previousToken['code'] === T_GOTO) { return false; } $isProbablyReferencedName = !in_array( $previousToken['code'], [...$skipTokenCodes, ...TokenHelper::CLASS_TYPE_TOKEN_CODES], true, ); if (!$isProbablyReferencedName) { return false; } if ($previousToken['code'] === T_AS && !array_key_exists('nested_parenthesis', $previousToken)) { // "as" in "use" statement return false; } $endPointer = self::getReferencedNameEndPointer($phpcsFile, $startPointer); $referencedName = self::getReferenceName($phpcsFile, $startPointer, $endPointer); if (TypeHintHelper::isSimpleTypeHint($referencedName) || $referencedName === 'object') { return $tokens[$nextPointer]['code'] === T_OPEN_PARENTHESIS; } return true; } /** * @return list */ private static function createAllReferencedNamesInAttributes(File $phpcsFile, int $openTagPointer): array { $referencedNames = []; $tokens = $phpcsFile->getTokens(); $attributePointers = TokenHelper::findNextAll($phpcsFile, T_ATTRIBUTE, $openTagPointer + 1); foreach ($attributePointers as $attributeStartPointer) { $searchStartPointer = $attributeStartPointer + 1; $searchEndPointer = $tokens[$attributeStartPointer]['attribute_closer']; $searchPointer = $searchStartPointer; $searchTokens = [...TokenHelper::NAME_TOKEN_CODES, T_OPEN_PARENTHESIS, T_CLOSE_PARENTHESIS]; $level = 0; do { $pointer = TokenHelper::findNext($phpcsFile, $searchTokens, $searchPointer, $searchEndPointer); if ($pointer === null) { break; } if ($tokens[$pointer]['code'] === T_OPEN_PARENTHESIS) { $level++; $searchPointer = $pointer + 1; continue; } if ($tokens[$pointer]['code'] === T_CLOSE_PARENTHESIS) { $level--; $searchPointer = $pointer + 1; continue; } $referencedNameEndPointer = self::getReferencedNameEndPointer($phpcsFile, $pointer); $pointerBefore = TokenHelper::findPreviousEffective($phpcsFile, $pointer - 1); if (in_array($tokens[$pointerBefore]['code'], [T_OPEN_TAG, T_ATTRIBUTE], true)) { $referenceType = ReferencedName::TYPE_CLASS; } elseif ($tokens[$pointerBefore]['code'] === T_COMMA && $level === 0) { $referenceType = ReferencedName::TYPE_CLASS; } elseif (self::isReferencedName($phpcsFile, $pointer)) { $referenceType = self::getReferenceType($phpcsFile, $pointer, $referencedNameEndPointer); } else { $searchPointer = $pointer + 1; continue; } $referencedName = self::getReferenceName($phpcsFile, $pointer, $referencedNameEndPointer); $referencedNames[] = new ReferencedName( $referencedName, $attributeStartPointer, $tokens[$attributeStartPointer]['attribute_closer'], $referenceType, ); $searchPointer = $referencedNameEndPointer + 1; } while (true); } return $referencedNames; } /** * @param int|string $code */ private static function isNeedParsedContent($code): bool { return in_array($code, [T_DOUBLE_QUOTED_STRING, T_HEREDOC], true); } /** * @return list */ private static function getReferencedNamesFromString(string $content): array { $referencedNames = []; $subTokens = token_get_all(' $token) { if (is_array($token) && $token[0] === T_DOUBLE_COLON) { $referencedName = ''; $tmpPosition = $position - 1; while (true) { if (!is_array($subTokens[$tmpPosition]) || !in_array($subTokens[$tmpPosition][0], [T_NS_SEPARATOR, T_STRING], true)) { break; } $referencedName = $subTokens[$tmpPosition][1] . $referencedName; $tmpPosition--; } $referencedNames[] = $referencedName; } elseif (is_array($token) && $token[0] === T_NEW) { $referencedName = ''; $tmpPosition = $position + 1; while (true) { if (!is_array($subTokens[$tmpPosition])) { break; } if ($subTokens[$tmpPosition][0] === T_WHITESPACE) { $tmpPosition++; continue; } if (!in_array( $subTokens[$tmpPosition][0], [T_STRING, T_NS_SEPARATOR, T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED, T_NAME_RELATIVE], true, )) { break; } $referencedName .= $subTokens[$tmpPosition][1]; $tmpPosition++; } if ($referencedName !== '') { $referencedNames[] = $referencedName; } } } return $referencedNames; } } PK41]C\((Bcoding-standard/SlevomatCodingStandard/Helpers/ConditionHelper.phpnu[getTokens(); $conditionContent = strtolower( trim(TokenHelper::getContent($phpcsFile, $conditionBoundaryStartPointer, $conditionBoundaryEndPointer)), ); if ($conditionContent === 'false' || $conditionContent === 'true') { return true; } $actualPointer = $conditionBoundaryStartPointer; do { $actualPointer = TokenHelper::findNext( $phpcsFile, array_merge( [T_OPEN_PARENTHESIS, T_LESS_THAN, T_GREATER_THAN], Tokens::$booleanOperators, Tokens::$equalityTokens, ), $actualPointer, $conditionBoundaryEndPointer + 1, ); if ($actualPointer === null) { break; } if ($tokens[$actualPointer]['code'] === T_OPEN_PARENTHESIS) { $actualPointer = $tokens[$actualPointer]['parenthesis_closer']; continue; } return true; } while (true); return false; } public static function getNegativeCondition( File $phpcsFile, int $conditionBoundaryStartPointer, int $conditionBoundaryEndPointer, bool $nested = false ): string { /** @var int $conditionStartPointer */ $conditionStartPointer = TokenHelper::findNextEffective($phpcsFile, $conditionBoundaryStartPointer); /** @var int $conditionEndPointer */ $conditionEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $conditionBoundaryEndPointer); $tokens = $phpcsFile->getTokens(); if ( $tokens[$conditionStartPointer]['code'] === T_OPEN_PARENTHESIS && $tokens[$conditionStartPointer]['parenthesis_closer'] === $conditionEndPointer ) { /** @var int $conditionStartPointer */ $conditionStartPointer = TokenHelper::findNextEffective($phpcsFile, $conditionStartPointer + 1); /** @var int $conditionEndPointer */ $conditionEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $conditionEndPointer - 1); } return sprintf( '%s%s%s', $conditionBoundaryStartPointer !== $conditionStartPointer ? TokenHelper::getContent( $phpcsFile, $conditionBoundaryStartPointer, $conditionStartPointer - 1, ) : '', self::getNegativeConditionPart($phpcsFile, $conditionStartPointer, $conditionEndPointer, $nested), $conditionBoundaryEndPointer !== $conditionEndPointer ? TokenHelper::getContent( $phpcsFile, $conditionEndPointer + 1, $conditionBoundaryEndPointer, ) : '', ); } private static function getNegativeConditionPart( File $phpcsFile, int $conditionBoundaryStartPointer, int $conditionBoundaryEndPointer, bool $nested ): string { $tokens = $phpcsFile->getTokens(); $condition = TokenHelper::getContent($phpcsFile, $conditionBoundaryStartPointer, $conditionBoundaryEndPointer); if (strtolower($condition) === 'true') { return 'false'; } if (strtolower($condition) === 'false') { return 'true'; } $pointerAfterConditionStart = TokenHelper::findNextEffective($phpcsFile, $conditionBoundaryStartPointer); $booleanPointers = TokenHelper::findNextAll( $phpcsFile, Tokens::$booleanOperators, $conditionBoundaryStartPointer, $conditionBoundaryEndPointer + 1, ); if ($tokens[$pointerAfterConditionStart]['code'] === T_BOOLEAN_NOT) { $pointerAfterBooleanNot = TokenHelper::findNextEffective($phpcsFile, $pointerAfterConditionStart + 1); if ($tokens[$pointerAfterBooleanNot]['code'] === T_OPEN_PARENTHESIS) { if ($nested && $booleanPointers !== []) { return self::removeBooleanNot($condition); } $pointerAfterParenthesisCloser = TokenHelper::findNextEffective( $phpcsFile, $tokens[$pointerAfterBooleanNot]['parenthesis_closer'] + 1, $conditionBoundaryEndPointer + 1, ); if ( $pointerAfterParenthesisCloser === null || $pointerAfterParenthesisCloser === $conditionBoundaryEndPointer ) { return TokenHelper::getContent( $phpcsFile, $pointerAfterBooleanNot + 1, $tokens[$pointerAfterBooleanNot]['parenthesis_closer'] - 1, ); } } } if (count($booleanPointers) > 0) { return self::getNegativeLogicalCondition($phpcsFile, $conditionBoundaryStartPointer, $conditionBoundaryEndPointer); } if ($tokens[$pointerAfterConditionStart]['code'] === T_BOOLEAN_NOT) { return self::removeBooleanNot($condition); } if (TokenHelper::findNext( $phpcsFile, [T_INSTANCEOF, T_BITWISE_AND, T_COALESCE, T_INLINE_THEN], $conditionBoundaryStartPointer, $conditionBoundaryEndPointer + 1, ) !== null) { return sprintf('!(%s)', $condition); } if ($tokens[$pointerAfterConditionStart]['code'] === T_STRING) { $pointerAfterConditionStart = TokenHelper::findNextEffective($phpcsFile, $pointerAfterConditionStart + 1); if ( $tokens[$pointerAfterConditionStart]['code'] === T_OPEN_PARENTHESIS && $tokens[$pointerAfterConditionStart]['parenthesis_closer'] === $conditionBoundaryEndPointer ) { return sprintf('!%s', $condition); } } if (in_array($tokens[$pointerAfterConditionStart]['code'], [T_VARIABLE, T_SELF, T_STATIC, T_PARENT], true)) { $identificatorEndPointer = IdentificatorHelper::findEndPointer($phpcsFile, $pointerAfterConditionStart); $pointerAfterIdentificatorEnd = TokenHelper::findNextEffective($phpcsFile, $identificatorEndPointer + 1); if ( $tokens[$pointerAfterIdentificatorEnd]['code'] === T_OPEN_PARENTHESIS && $tokens[$pointerAfterIdentificatorEnd]['parenthesis_closer'] === $conditionBoundaryEndPointer ) { return sprintf('!%s', $condition); } } $comparisonPointer = TokenHelper::findNext( $phpcsFile, [T_IS_EQUAL, T_IS_NOT_EQUAL, T_IS_IDENTICAL, T_IS_NOT_IDENTICAL, T_IS_SMALLER_OR_EQUAL, T_IS_GREATER_OR_EQUAL, T_LESS_THAN, T_GREATER_THAN], $conditionBoundaryStartPointer, $conditionBoundaryEndPointer + 1, ); if ($comparisonPointer !== null) { $comparisonReplacements = [ T_IS_EQUAL => '!=', T_IS_NOT_EQUAL => '==', T_IS_IDENTICAL => '!==', T_IS_NOT_IDENTICAL => '===', T_IS_GREATER_OR_EQUAL => '<', T_IS_SMALLER_OR_EQUAL => '>', T_GREATER_THAN => '<=', T_LESS_THAN => '>=', ]; $negativeCondition = ''; for ($i = $conditionBoundaryStartPointer; $i <= $conditionBoundaryEndPointer; $i++) { // Skip calls() if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS) { $negativeCondition .= TokenHelper::getContent($phpcsFile, $i, $tokens[$i]['parenthesis_closer']); $i = $tokens[$i]['parenthesis_closer']; continue; } $negativeCondition .= array_key_exists($tokens[$i]['code'], $comparisonReplacements) ? $comparisonReplacements[$tokens[$i]['code']] : $tokens[$i]['content']; } return $negativeCondition; } return sprintf('!%s', $condition); } private static function removeBooleanNot(string $condition): string { return preg_replace('~^!\\s*~', '', $condition); } private static function getNegativeLogicalCondition( File $phpcsFile, int $conditionBoundaryStartPointer, int $conditionBoundaryEndPointer ): string { if (TokenHelper::findNext($phpcsFile, T_LOGICAL_XOR, $conditionBoundaryStartPointer, $conditionBoundaryEndPointer) !== null) { return sprintf('!(%s)', TokenHelper::getContent($phpcsFile, $conditionBoundaryStartPointer, $conditionBoundaryEndPointer)); } $tokens = $phpcsFile->getTokens(); $booleanOperatorReplacements = [ T_BOOLEAN_AND => '||', T_BOOLEAN_OR => '&&', T_LOGICAL_AND => 'or', T_LOGICAL_OR => 'and', ]; $negativeCondition = ''; $nestedConditionStartPointer = $conditionBoundaryStartPointer; $actualPointer = $conditionBoundaryStartPointer; $parenthesesLevel = 0; $operatorsOnLevel = []; do { $actualPointer = TokenHelper::findNext( $phpcsFile, array_merge([T_OPEN_PARENTHESIS, T_CLOSE_PARENTHESIS], Tokens::$booleanOperators), $actualPointer, $conditionBoundaryEndPointer + 1, ); if ($actualPointer === null) { break; } if ($tokens[$actualPointer]['code'] === T_OPEN_PARENTHESIS) { $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $actualPointer - 1); if ($tokens[$pointerBeforeParenthesisOpener]['code'] === T_STRING) { $actualPointer = $tokens[$actualPointer]['parenthesis_closer'] + 1; continue; } $parenthesesLevel++; $actualPointer++; continue; } if ($tokens[$actualPointer]['code'] === T_CLOSE_PARENTHESIS) { $parenthesesLevel--; $actualPointer++; continue; } if ($parenthesesLevel !== 0) { $actualPointer++; continue; } if ( array_key_exists($parenthesesLevel, $operatorsOnLevel) && $operatorsOnLevel[$parenthesesLevel] !== $tokens[$actualPointer]['code'] ) { return sprintf('!(%s)', TokenHelper::getContent($phpcsFile, $conditionBoundaryStartPointer, $conditionBoundaryEndPointer)); } $operatorsOnLevel[$parenthesesLevel] = $tokens[$actualPointer]['code']; $negativeCondition .= self::getNegativeCondition($phpcsFile, $nestedConditionStartPointer, $actualPointer - 1, true); $negativeCondition .= $booleanOperatorReplacements[$tokens[$actualPointer]['code']]; $nestedConditionStartPointer = $actualPointer + 1; $actualPointer++; } while (true); return $negativeCondition . self::getNegativeCondition( $phpcsFile, $nestedConditionStartPointer, $conditionBoundaryEndPointer, true, ); } } PK41] [x33>coding-standard/SlevomatCodingStandard/Helpers/TokenHelper.phpnu[ $types */ public static function findNext(File $phpcsFile, $types, int $startPointer, ?int $endPointer = null): ?int { /** @var int|false $token */ $token = $phpcsFile->findNext($types, $startPointer, $endPointer, false); return $token === false ? null : $token; } /** * @param int|string|array $types * @return list */ public static function findNextAll(File $phpcsFile, $types, int $startPointer, ?int $endPointer = null): array { $pointers = []; $actualStartPointer = $startPointer; while (true) { $pointer = self::findNext($phpcsFile, $types, $actualStartPointer, $endPointer); if ($pointer === null) { break; } $pointers[] = $pointer; $actualStartPointer = $pointer + 1; } return $pointers; } /** * @param int|string|array $types */ public static function findNextContent(File $phpcsFile, $types, string $content, int $startPointer, ?int $endPointer = null): ?int { /** @var int|false $token */ $token = $phpcsFile->findNext($types, $startPointer, $endPointer, false, $content); return $token === false ? null : $token; } /** * @param int $startPointer Search starts at this token, inclusive * @param int|null $endPointer Search ends at this token, exclusive */ public static function findNextEffective(File $phpcsFile, int $startPointer, ?int $endPointer = null): ?int { return self::findNextExcluding($phpcsFile, self::INEFFECTIVE_TOKEN_CODES, $startPointer, $endPointer); } /** * @param int $startPointer Search starts at this token, inclusive * @param int|null $endPointer Search ends at this token, exclusive */ public static function findNextNonWhitespace(File $phpcsFile, int $startPointer, ?int $endPointer = null): ?int { return self::findNextExcluding($phpcsFile, T_WHITESPACE, $startPointer, $endPointer); } /** * @param int|string|array $types * @param int $startPointer Search starts at this token, inclusive * @param int|null $endPointer Search ends at this token, exclusive */ public static function findNextExcluding(File $phpcsFile, $types, int $startPointer, ?int $endPointer = null): ?int { /** @var int|false $token */ $token = $phpcsFile->findNext($types, $startPointer, $endPointer, true); return $token === false ? null : $token; } /** * @param int|string|array $types */ public static function findNextLocal(File $phpcsFile, $types, int $startPointer, ?int $endPointer = null): ?int { /** @var int|false $token */ $token = $phpcsFile->findNext($types, $startPointer, $endPointer, false, null, true); return $token === false ? null : $token; } /** * @param int $startPointer Search starts at this token, inclusive * @param int|null $endPointer Search ends at this token, exclusive */ public static function findNextAnyToken(File $phpcsFile, int $startPointer, ?int $endPointer = null): ?int { return self::findNextExcluding($phpcsFile, [], $startPointer, $endPointer); } /** * @param int|string|array $types * @param int $startPointer Search starts at this token, inclusive * @param int|null $endPointer Search ends at this token, exclusive */ public static function findPrevious(File $phpcsFile, $types, int $startPointer, ?int $endPointer = null): ?int { /** @var int|false $token */ $token = $phpcsFile->findPrevious($types, $startPointer, $endPointer, false); return $token === false ? null : $token; } /** * @param int|string|array $types */ public static function findPreviousContent(File $phpcsFile, $types, string $content, int $startPointer, ?int $endPointer = null): ?int { /** @var int|false $token */ $token = $phpcsFile->findPrevious($types, $startPointer, $endPointer, false, $content); return $token === false ? null : $token; } /** * @param int $startPointer Search starts at this token, inclusive * @param int|null $endPointer Search ends at this token, exclusive */ public static function findPreviousEffective(File $phpcsFile, int $startPointer, ?int $endPointer = null): ?int { return self::findPreviousExcluding($phpcsFile, self::INEFFECTIVE_TOKEN_CODES, $startPointer, $endPointer); } /** * @param int $startPointer Search starts at this token, inclusive * @param int|null $endPointer Search ends at this token, exclusive */ public static function findPreviousNonWhitespace(File $phpcsFile, int $startPointer, ?int $endPointer = null): ?int { return self::findPreviousExcluding($phpcsFile, T_WHITESPACE, $startPointer, $endPointer); } /** * @param int|string|array $types * @param int $startPointer Search starts at this token, inclusive * @param int|null $endPointer Search ends at this token, exclusive */ public static function findPreviousExcluding(File $phpcsFile, $types, int $startPointer, ?int $endPointer = null): ?int { /** @var int|false $token */ $token = $phpcsFile->findPrevious($types, $startPointer, $endPointer, true); return $token === false ? null : $token; } /** * @param int|string|array $types */ public static function findPreviousLocal(File $phpcsFile, $types, int $startPointer, ?int $endPointer = null): ?int { /** @var int|false $token */ $token = $phpcsFile->findPrevious($types, $startPointer, $endPointer, false, null, true); return $token === false ? null : $token; } /** * @param int $pointer Search starts at this token, inclusive */ public static function findFirstTokenOnLine(File $phpcsFile, int $pointer): int { if ($pointer === 0) { return $pointer; } $tokens = $phpcsFile->getTokens(); $line = $tokens[$pointer]['line']; do { $pointer--; } while ($tokens[$pointer]['line'] === $line); return $pointer + 1; } /** * @param int $pointer Search starts at this token, inclusive */ public static function findLastTokenOnLine(File $phpcsFile, int $pointer): int { $tokens = $phpcsFile->getTokens(); $line = $tokens[$pointer]['line']; do { $pointer++; } while (array_key_exists($pointer, $tokens) && $tokens[$pointer]['line'] === $line); return $pointer - 1; } /** * @param int $pointer Search starts at this token, inclusive */ public static function findLastTokenOnPreviousLine(File $phpcsFile, int $pointer): int { $tokens = $phpcsFile->getTokens(); $line = $tokens[$pointer]['line']; do { $pointer--; } while ($tokens[$pointer]['line'] === $line); return $pointer; } /** * @param int $pointer Search starts at this token, inclusive */ public static function findFirstTokenOnNextLine(File $phpcsFile, int $pointer): ?int { $tokens = $phpcsFile->getTokens(); if ($pointer >= count($tokens)) { return null; } $line = $tokens[$pointer]['line']; do { $pointer++; if (!array_key_exists($pointer, $tokens)) { return null; } } while ($tokens[$pointer]['line'] === $line); return $pointer; } /** * @param int $pointer Search starts at this token, inclusive */ public static function findFirstNonWhitespaceOnLine(File $phpcsFile, int $pointer): int { if ($pointer === 0) { return $pointer; } $tokens = $phpcsFile->getTokens(); $line = $tokens[$pointer]['line']; do { $pointer--; } while ($pointer >= 0 && $tokens[$pointer]['line'] === $line); return self::findNextExcluding($phpcsFile, [T_WHITESPACE, T_DOC_COMMENT_WHITESPACE], $pointer + 1); } /** * @param int $pointer Search starts at this token, inclusive */ public static function findFirstNonWhitespaceOnNextLine(File $phpcsFile, int $pointer): ?int { $newLinePointer = self::findNextContent($phpcsFile, [T_WHITESPACE, T_DOC_COMMENT_WHITESPACE], $phpcsFile->eolChar, $pointer); if ($newLinePointer === null) { return null; } $nextPointer = self::findNextExcluding($phpcsFile, [T_WHITESPACE, T_DOC_COMMENT_WHITESPACE], $newLinePointer + 1); $tokens = $phpcsFile->getTokens(); if ($nextPointer !== null && $tokens[$pointer]['line'] === $tokens[$nextPointer]['line'] - 1) { return $nextPointer; } return null; } /** * @param int $pointer Search starts at this token, inclusive */ public static function findFirstNonWhitespaceOnPreviousLine(File $phpcsFile, int $pointer): ?int { $newLinePointerOnPreviousLine = self::findPreviousContent( $phpcsFile, [T_WHITESPACE, T_DOC_COMMENT_WHITESPACE], $phpcsFile->eolChar, $pointer, ); if ($newLinePointerOnPreviousLine === null) { return null; } $newLinePointerBeforePreviousLine = self::findPreviousContent( $phpcsFile, [T_WHITESPACE, T_DOC_COMMENT_WHITESPACE], $phpcsFile->eolChar, $newLinePointerOnPreviousLine - 1, ); if ($newLinePointerBeforePreviousLine === null) { return null; } $nextPointer = self::findNextExcluding($phpcsFile, [T_WHITESPACE, T_DOC_COMMENT_WHITESPACE], $newLinePointerBeforePreviousLine + 1); $tokens = $phpcsFile->getTokens(); if ($nextPointer !== null && $tokens[$pointer]['line'] === $tokens[$nextPointer]['line'] + 1) { return $nextPointer; } return null; } public static function getContent(File $phpcsFile, int $startPointer, ?int $endPointer = null): string { $tokens = $phpcsFile->getTokens(); $endPointer ??= self::getLastTokenPointer($phpcsFile); $content = ''; for ($i = $startPointer; $i <= $endPointer; $i++) { $content .= $tokens[$i]['content']; } return $content; } public static function getLastTokenPointer(File $phpcsFile): int { $tokenCount = count($phpcsFile->getTokens()); if ($tokenCount === 0) { throw new EmptyFileException($phpcsFile->getFilename()); } return $tokenCount - 1; } } PK41]@ JJAcoding-standard/SlevomatCodingStandard/Helpers/ReferencedName.phpnu[nameAsReferencedInFile = $nameAsReferencedInFile; $this->startPointer = $startPointer; $this->endPointer = $endPointer; $this->type = $type; } public function getNameAsReferencedInFile(): string { return $this->nameAsReferencedInFile; } public function getStartPointer(): int { return $this->startPointer; } public function getType(): string { return $this->type; } public function getEndPointer(): int { return $this->endPointer; } public function isClass(): bool { return $this->type === self::TYPE_CLASS; } public function isConstant(): bool { return $this->type === self::TYPE_CONSTANT; } public function isFunction(): bool { return $this->type === self::TYPE_FUNCTION; } public function hasSameUseStatementType(UseStatement $useStatement): bool { return $this->getType() === $useStatement->getType(); } } PK41]ZN;;Ecoding-standard/SlevomatCodingStandard/Helpers/PhpDocParserHelper.phpnu[traverse([$node]); return $cloneNode; } private static function getConfig(): ParserConfig { static $config; $config ??= new ParserConfig(['lines' => true, 'indexes' => true]); return $config; } } PK41]Ԡ7B7BAcoding-standard/SlevomatCodingStandard/Helpers/FunctionHelper.phpnu[getTokens(); return $tokens[TokenHelper::findNext( $phpcsFile, T_STRING, $functionPointer + 1, $tokens[$functionPointer]['parenthesis_opener'], )]['content']; } public static function getFullyQualifiedName(File $phpcsFile, int $functionPointer): string { $name = self::getName($phpcsFile, $functionPointer); $namespace = NamespaceHelper::findCurrentNamespaceName($phpcsFile, $functionPointer); if (self::isMethod($phpcsFile, $functionPointer)) { foreach (array_reverse( $phpcsFile->getTokens()[$functionPointer]['conditions'], true, ) as $conditionPointer => $conditionTokenCode) { if ($conditionTokenCode === T_ANON_CLASS) { return sprintf('class@anonymous::%s', $name); } if (in_array($conditionTokenCode, [T_CLASS, T_INTERFACE, T_TRAIT, T_ENUM], true)) { $name = sprintf( '%s%s::%s', NamespaceHelper::NAMESPACE_SEPARATOR, ClassHelper::getName($phpcsFile, $conditionPointer), $name, ); break; } } return $namespace !== null ? sprintf('%s%s%s', NamespaceHelper::NAMESPACE_SEPARATOR, $namespace, $name) : $name; } return $namespace !== null ? sprintf('%s%s%s%s', NamespaceHelper::NAMESPACE_SEPARATOR, $namespace, NamespaceHelper::NAMESPACE_SEPARATOR, $name) : $name; } public static function isAbstract(File $phpcsFile, int $functionPointer): bool { return !isset($phpcsFile->getTokens()[$functionPointer]['scope_opener']); } public static function isMethod(File $phpcsFile, int $functionPointer): bool { $functionPointerConditions = $phpcsFile->getTokens()[$functionPointer]['conditions']; if ($functionPointerConditions === []) { return false; } $lastFunctionPointerCondition = array_pop($functionPointerConditions); return in_array($lastFunctionPointerCondition, Tokens::$ooScopeTokens, true); } public static function findClassPointer(File $phpcsFile, int $functionPointer): ?int { $tokens = $phpcsFile->getTokens(); if ($tokens[$functionPointer]['code'] === T_CLOSURE) { return null; } foreach (array_reverse($tokens[$functionPointer]['conditions'], true) as $conditionPointer => $conditionTokenCode) { if (!in_array($conditionTokenCode, Tokens::$ooScopeTokens, true)) { continue; } return $conditionPointer; } return null; } /** * @return list */ public static function getParametersNames(File $phpcsFile, int $functionPointer): array { $tokens = $phpcsFile->getTokens(); $parametersNames = []; for ($i = $tokens[$functionPointer]['parenthesis_opener'] + 1; $i < $tokens[$functionPointer]['parenthesis_closer']; $i++) { if ($tokens[$i]['code'] !== T_VARIABLE) { continue; } $parametersNames[] = $tokens[$i]['content']; } return $parametersNames; } /** * @return array */ public static function getParametersTypeHints(File $phpcsFile, int $functionPointer): array { $tokens = $phpcsFile->getTokens(); $parametersTypeHints = []; for ($i = $tokens[$functionPointer]['parenthesis_opener'] + 1; $i < $tokens[$functionPointer]['parenthesis_closer']; $i++) { if ($tokens[$i]['code'] !== T_VARIABLE) { continue; } $parameterName = $tokens[$i]['content']; $pointerBeforeVariable = TokenHelper::findPreviousExcluding( $phpcsFile, [...TokenHelper::INEFFECTIVE_TOKEN_CODES, T_BITWISE_AND, T_ELLIPSIS], $i - 1, ); if (!in_array($tokens[$pointerBeforeVariable]['code'], TokenHelper::TYPE_HINT_TOKEN_CODES, true)) { $parametersTypeHints[$parameterName] = null; continue; } $typeHintEndPointer = $pointerBeforeVariable; $typeHintStartPointer = TypeHintHelper::getStartPointer($phpcsFile, $typeHintEndPointer); $pointerBeforeTypeHint = TokenHelper::findPreviousEffective($phpcsFile, $typeHintStartPointer - 1); $isNullable = $tokens[$pointerBeforeTypeHint]['code'] === T_NULLABLE; if ($isNullable) { $typeHintStartPointer = $pointerBeforeTypeHint; } $typeHint = TokenHelper::getContent($phpcsFile, $typeHintStartPointer, $typeHintEndPointer); /** @var string $typeHint */ $typeHint = preg_replace('~\s+~', '', $typeHint); if (!$isNullable) { $isNullable = preg_match('~(?:^|\|)null(?:\||$)~i', $typeHint) === 1; } $parametersTypeHints[$parameterName] = new TypeHint($typeHint, $isNullable, $typeHintStartPointer, $typeHintEndPointer); } return $parametersTypeHints; } public static function returnsValue(File $phpcsFile, int $functionPointer): bool { $tokens = $phpcsFile->getTokens(); $firstPointerInScope = $tokens[$functionPointer]['scope_opener'] + 1; for ($i = $firstPointerInScope; $i < $tokens[$functionPointer]['scope_closer']; $i++) { if (!in_array($tokens[$i]['code'], [T_YIELD, T_YIELD_FROM], true)) { continue; } if (!ScopeHelper::isInSameScope($phpcsFile, $i, $firstPointerInScope)) { continue; } return true; } for ($i = $firstPointerInScope; $i < $tokens[$functionPointer]['scope_closer']; $i++) { if ($tokens[$i]['code'] !== T_RETURN) { continue; } if (!ScopeHelper::isInSameScope($phpcsFile, $i, $firstPointerInScope)) { continue; } $nextEffectiveTokenPointer = TokenHelper::findNextEffective($phpcsFile, $i + 1); return $tokens[$nextEffectiveTokenPointer]['code'] !== T_SEMICOLON; } return false; } public static function findReturnTypeHint(File $phpcsFile, int $functionPointer): ?TypeHint { $tokens = $phpcsFile->getTokens(); $nextPointer = TokenHelper::findNextEffective($phpcsFile, $tokens[$functionPointer]['parenthesis_closer'] + 1); if ($tokens[$nextPointer]['code'] === T_USE) { $useParenthesisOpener = TokenHelper::findNextEffective($phpcsFile, $nextPointer + 1); $colonPointer = TokenHelper::findNextEffective($phpcsFile, $tokens[$useParenthesisOpener]['parenthesis_closer'] + 1); } else { $colonPointer = $nextPointer; } if ($tokens[$colonPointer]['code'] !== T_COLON) { return null; } $typeHintStartPointer = TokenHelper::findNextEffective($phpcsFile, $colonPointer + 1); $nullable = $tokens[$typeHintStartPointer]['code'] === T_NULLABLE; $pointerAfterTypeHint = self::isAbstract($phpcsFile, $functionPointer) ? TokenHelper::findNext($phpcsFile, T_SEMICOLON, $typeHintStartPointer + 1) : $tokens[$functionPointer]['scope_opener']; $typeHintEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $pointerAfterTypeHint - 1); $typeHint = TokenHelper::getContent($phpcsFile, $typeHintStartPointer, $typeHintEndPointer); /** @var string $typeHint */ $typeHint = preg_replace('~\s+~', '', $typeHint); if (!$nullable) { $nullable = preg_match('~(?:^|\|)null(?:\||$)~i', $typeHint) === 1; } return new TypeHint($typeHint, $nullable, $typeHintStartPointer, $typeHintEndPointer); } public static function hasReturnTypeHint(File $phpcsFile, int $functionPointer): bool { return self::findReturnTypeHint($phpcsFile, $functionPointer) !== null; } /** * @return list|Annotation> */ public static function getParametersAnnotations(File $phpcsFile, int $functionPointer): array { return AnnotationHelper::getAnnotations($phpcsFile, $functionPointer, '@param'); } /** * @return array|Annotation|Annotation> */ public static function getValidParametersAnnotations(File $phpcsFile, int $functionPointer): array { $tokens = $phpcsFile->getTokens(); $parametersAnnotations = []; if (self::getName($phpcsFile, $functionPointer) === '__construct') { for ($i = $tokens[$functionPointer]['parenthesis_opener'] + 1; $i < $tokens[$functionPointer]['parenthesis_closer']; $i++) { if ($tokens[$i]['code'] !== T_VARIABLE) { continue; } $varAnnotations = AnnotationHelper::getAnnotations($phpcsFile, $i, '@var'); if ($varAnnotations === []) { continue; } $parametersAnnotations[$tokens[$i]['content']] = $varAnnotations[0]; } } foreach (self::getParametersAnnotations($phpcsFile, $functionPointer) as $parameterAnnotation) { if ($parameterAnnotation->isInvalid()) { continue; } $parametersAnnotations[$parameterAnnotation->getValue()->parameterName] = $parameterAnnotation; } return $parametersAnnotations; } /** * @return array|Annotation> */ public static function getValidPrefixedParametersAnnotations(File $phpcsFile, int $functionPointer): array { $tokens = $phpcsFile->getTokens(); $parametersAnnotations = []; foreach (AnnotationHelper::STATIC_ANALYSIS_PREFIXES as $prefix) { if (self::getName($phpcsFile, $functionPointer) === '__construct') { for ($i = $tokens[$functionPointer]['parenthesis_opener'] + 1; $i < $tokens[$functionPointer]['parenthesis_closer']; $i++) { if ($tokens[$i]['code'] !== T_VARIABLE) { continue; } /** @var list> $varAnnotations */ $varAnnotations = AnnotationHelper::getAnnotations($phpcsFile, $i, sprintf('@%s-var', $prefix)); if ($varAnnotations === []) { continue; } $parametersAnnotations[$tokens[$i]['content']] = $varAnnotations[0]; } } /** @var list> $annotations */ $annotations = AnnotationHelper::getAnnotations($phpcsFile, $functionPointer, sprintf('@%s-param', $prefix)); foreach ($annotations as $parameterAnnotation) { if ($parameterAnnotation->isInvalid()) { continue; } $parametersAnnotations[$parameterAnnotation->getValue()->parameterName] = $parameterAnnotation; } } return $parametersAnnotations; } /** * @return Annotation|null */ public static function findReturnAnnotation(File $phpcsFile, int $functionPointer): ?Annotation { /** @var list> $returnAnnotations */ $returnAnnotations = AnnotationHelper::getAnnotations($phpcsFile, $functionPointer, '@return'); if ($returnAnnotations === []) { return null; } return $returnAnnotations[0]; } /** * @return list */ public static function getValidPrefixedReturnAnnotations(File $phpcsFile, int $functionPointer): array { $returnAnnotations = []; $annotations = AnnotationHelper::getAnnotations($phpcsFile, $functionPointer); foreach (AnnotationHelper::STATIC_ANALYSIS_PREFIXES as $prefix) { $prefixedAnnotationName = sprintf('@%s-return', $prefix); foreach ($annotations as $annotation) { if ($annotation->isInvalid()) { continue; } if ($annotation->getName() === $prefixedAnnotationName) { $returnAnnotations[] = $annotation; } } } return $returnAnnotations; } /** * @return list */ public static function getAllFunctionNames(File $phpcsFile): array { $previousFunctionPointer = 0; return array_map( static fn (int $functionPointer): string => self::getName($phpcsFile, $functionPointer), array_values(array_filter( iterator_to_array(self::getAllFunctionOrMethodPointers($phpcsFile, $previousFunctionPointer)), static fn (int $functionOrMethodPointer): bool => !self::isMethod($phpcsFile, $functionOrMethodPointer), )), ); } /** * @param int $flags optional bitmask of self::LINE_INCLUDE_* constants */ public static function getFunctionLengthInLines(File $file, int $functionPosition, int $flags = 0): int { if (self::isAbstract($file, $functionPosition)) { return 0; } return self::getLineCount($file, $functionPosition, $flags); } public static function getLineCount(File $file, int $tokenPosition, int $flags = 0): int { $includeWhitespace = ($flags & self::LINE_INCLUDE_WHITESPACE) === self::LINE_INCLUDE_WHITESPACE; $includeComments = ($flags & self::LINE_INCLUDE_COMMENT) === self::LINE_INCLUDE_COMMENT; $tokens = $file->getTokens(); $token = $tokens[$tokenPosition]; $tokenOpenerPosition = $token['scope_opener'] ?? $tokenPosition; $tokenCloserPosition = $token['scope_closer'] ?? $file->numTokens - 1; $tokenOpenerLine = $tokens[$tokenOpenerPosition]['line']; $tokenCloserLine = $tokens[$tokenCloserPosition]['line']; $lineCount = 0; $lastCommentLine = null; $previousIncludedPosition = null; for ($position = $tokenOpenerPosition; $position <= $tokenCloserPosition - 1; $position++) { $token = $tokens[$position]; if ($includeComments === false) { if (in_array($token['code'], Tokens::$commentTokens, true)) { if ( $previousIncludedPosition !== null && substr_count($token['content'], $file->eolChar) > 0 && $token['line'] === $tokens[$previousIncludedPosition]['line'] ) { // Comment with linebreak starting on same line as included Token $lineCount++; } // Don't include comment $lastCommentLine = $token['line']; continue; } if ( $previousIncludedPosition !== null && $token['code'] === T_WHITESPACE && $token['line'] === $lastCommentLine && $token['line'] !== $tokens[$previousIncludedPosition]['line'] ) { // Whitespace after block comment... still on comment line... // Ignore along with the comment continue; } } if ($token['code'] === T_WHITESPACE) { $nextNonWhitespacePosition = $file->findNext(T_WHITESPACE, $position + 1, $tokenCloserPosition + 1, true); if ( $includeWhitespace === false && $token['column'] === 1 && $nextNonWhitespacePosition !== false && $tokens[$nextNonWhitespacePosition]['line'] !== $token['line'] ) { // This line is nothing but whitepace $position = $nextNonWhitespacePosition - 1; continue; } if ($previousIncludedPosition === $tokenOpenerPosition && $token['line'] === $tokenOpenerLine) { // Don't linclude line break after opening "{" // Unless there was code or an (included) comment following the "{" continue; } } if ($token['code'] !== T_WHITESPACE) { $previousIncludedPosition = $position; } $newLineFoundCount = substr_count($token['content'], $file->eolChar); $lineCount += $newLineFoundCount; } if ($tokens[$previousIncludedPosition]['line'] === $tokenCloserLine) { // There is code or comment on the closing "}" line... $lineCount++; } return $lineCount; } /** * @return Generator */ private static function getAllFunctionOrMethodPointers(File $phpcsFile, int &$previousFunctionPointer): Generator { do { $nextFunctionPointer = TokenHelper::findNext($phpcsFile, T_FUNCTION, $previousFunctionPointer + 1); if ($nextFunctionPointer === null) { break; } $previousFunctionPointer = $nextFunctionPointer; yield $nextFunctionPointer; } while (true); } } PK41];""Ccoding-standard/SlevomatCodingStandard/Helpers/AnnotationHelper.phpnu[ */ public static function getAnnotations(File $phpcsFile, int $pointer, ?string $name = null): array { $docCommentOpenPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $pointer); if ($docCommentOpenPointer === null) { return []; } return SniffLocalCache::getAndSetIfNotCached( $phpcsFile, sprintf('annotations-%d-%s', $docCommentOpenPointer, $name ?? 'all'), static function () use ($phpcsFile, $docCommentOpenPointer, $name): array { $annotations = []; if ($name !== null) { foreach (self::getAnnotations($phpcsFile, $docCommentOpenPointer) as $annotation) { if ($annotation->getName() === $name) { $annotations[] = $annotation; } } } else { $parsedDocComment = DocCommentHelper::parseDocComment($phpcsFile, $docCommentOpenPointer); if ($parsedDocComment !== null) { foreach ($parsedDocComment->getNode()->getTags() as $node) { $annotationStartPointer = $parsedDocComment->getNodeStartPointer($phpcsFile, $node); $annotations[] = new Annotation( $node, $annotationStartPointer, $parsedDocComment->getNodeEndPointer($phpcsFile, $node, $annotationStartPointer), ); } } } return $annotations; }, ); } /** * @template T * @param class-string $type * @return list */ public static function getAnnotationNodesByType(Node $node, string $type): array { static $visitor; static $traverser; $visitor ??= new class extends AbstractNodeVisitor { /** @var class-string */ private string $type; /** @var list */ private array $nodes = []; /** @var list */ private array $nodesToIgnore = []; /** * @return Node|list|NodeTraverser::*|null */ public function enterNode(Node $node) { if ($this->type === IdentifierTypeNode::class) { if ($node instanceof ArrayShapeItemNode || $node instanceof ObjectShapeItemNode) { $this->nodesToIgnore[] = $node->keyName; } elseif ($node instanceof DoctrineArgument) { $this->nodesToIgnore[] = $node->key; } } if ($node instanceof $this->type && !in_array($node, $this->nodesToIgnore, true)) { $this->nodes[] = $node; } return null; } /** * @param class-string $type */ public function setType(string $type): void { $this->type = $type; } public function clean(): void { $this->nodes = []; $this->nodesToIgnore = []; } /** * @return list */ public function getNodes(): array { return $this->nodes; } }; $traverser ??= new NodeTraverser([$visitor]); $visitor->setType($type); $visitor->clean(); $traverser->traverse([$node]); return $visitor->getNodes(); } public static function fixAnnotation( ParsedDocComment $parsedDocComment, Annotation $annotation, Node $nodeToFix, Node $fixedNode ): string { $originalNode = $annotation->getNode(); $newPhpDocNode = PhpDocParserHelper::cloneNode($parsedDocComment->getNode()); foreach ($newPhpDocNode->getTags() as $node) { if ($node->getAttribute(Attribute::ORIGINAL_NODE) === $originalNode) { self::changeAnnotationNode($node, $nodeToFix, $fixedNode); break; } } return PhpDocParserHelper::getPrinter()->printFormatPreserving( $newPhpDocNode, $parsedDocComment->getNode(), $parsedDocComment->getTokens(), ); } /** * @param list $traversableTypeHints */ public static function isAnnotationUseless( File $phpcsFile, int $functionPointer, ?TypeHint $typeHint, Annotation $annotation, array $traversableTypeHints, bool $enableUnionTypeHint = false, bool $enableIntersectionTypeHint = false, bool $enableStandaloneNullTrueFalseTypeHints = false ): bool { if ($annotation->isInvalid()) { return false; } if ($typeHint === null) { return false; } /** @var ParamTagValueNode|TypelessParamTagValueNode|ReturnTagValueNode|VarTagValueNode $annotationValue */ $annotationValue = $annotation->getValue(); if ($annotationValue->description !== '') { return false; } if ($annotationValue instanceof TypelessParamTagValueNode) { return true; } $annotationType = $annotationValue->type; if ( TypeHintHelper::isTraversableType( TypeHintHelper::getFullyQualifiedTypeHint($phpcsFile, $functionPointer, $typeHint->getTypeHintWithoutNullabilitySymbol()), $traversableTypeHints, ) && !( $annotationType instanceof IdentifierTypeNode && TypeHintHelper::isSimpleIterableTypeHint(strtolower($annotationType->name)) ) ) { return false; } if (AnnotationTypeHelper::containsStaticOrThisType($annotationType)) { return false; } if ( AnnotationTypeHelper::containsJustTwoTypes($annotationType) || ( $enableUnionTypeHint && ( $annotationType instanceof UnionTypeNode || ( $annotationType instanceof IdentifierTypeNode && TypeHintHelper::isUnofficialUnionTypeHint($annotationType->name) ) ) ) || ( $enableIntersectionTypeHint && $annotationType instanceof IntersectionTypeNode ) ) { $annotationTypeHint = AnnotationTypeHelper::print($annotationType); return TypeHintHelper::typeHintEqualsAnnotation( $phpcsFile, $functionPointer, $typeHint->getTypeHint(), $annotationTypeHint, ); } if ($annotationType instanceof ObjectShapeNode) { return false; } if ($annotationType instanceof ConstTypeNode) { return false; } if ($annotationType instanceof GenericTypeNode) { return false; } if ($annotationType instanceof CallableTypeNode) { return false; } if ($annotationType instanceof ConditionalTypeNode) { return false; } if ($annotationType instanceof ConditionalTypeForParameterNode) { return false; } if ($annotationType instanceof IdentifierTypeNode) { if (in_array( strtolower($annotationType->name), ['true', 'false', 'null'], true, )) { return $enableStandaloneNullTrueFalseTypeHints; } if (TypeHintHelper::isSimpleUnofficialTypeHints( strtolower($annotationType->name), ) && !in_array($annotationType->name, ['object', 'mixed'], true) ) { return false; } } $annotationTypeHint = AnnotationTypeHelper::getTypeHintFromOneType($annotationType); return TypeHintHelper::typeHintEqualsAnnotation( $phpcsFile, $functionPointer, $typeHint->getTypeHintWithoutNullabilitySymbol(), $annotationTypeHint, ); } private static function changeAnnotationNode(PhpDocTagNode $tagNode, Node $nodeToChange, Node $changedNode): PhpDocTagNode { static $visitor; static $traverser; $visitor ??= new class extends AbstractNodeVisitor { private Node $nodeToChange; private Node $changedNode; public function enterNode(Node $node): ?Node { if ($node->getAttribute(Attribute::ORIGINAL_NODE) === $this->nodeToChange) { return $this->changedNode; } return null; } public function setNodeToChange(Node $nodeToChange): void { $this->nodeToChange = $nodeToChange; } public function setChangedNode(Node $changedNode): void { $this->changedNode = $changedNode; } }; $traverser ??= new NodeTraverser([$visitor]); $visitor->setNodeToChange($nodeToChange); $visitor->setChangedNode($changedNode); [$changedTagNode] = $traverser->traverse([$tagNode]); return $changedTagNode; } } PK41]Q>coding-standard/SlevomatCodingStandard/Helpers/ScopeHelper.phpnu[getTokens(); $getScope = static function (int $pointer) use ($tokens): int { $scope = 0; foreach (array_reverse($tokens[$pointer]['conditions'], true) as $conditionPointer => $conditionTokenCode) { if (!in_array($conditionTokenCode, TokenHelper::FUNCTION_TOKEN_CODES, true)) { continue; } $scope = $tokens[$conditionPointer]['level'] + 1; break; } return $scope; }; return $getScope($firstPointer) === $getScope($secondPointer); } public static function getRootPointer(File $phpcsFile, int $pointer): int { $rootPointer = TokenHelper::findNext($phpcsFile, T_OPEN_TAG, 0); $rootPointers = array_reverse(self::getAllRootPointers($phpcsFile)); foreach ($rootPointers as $currentRootPointer) { if ($currentRootPointer < $pointer) { $rootPointer = $currentRootPointer; break; } } return $rootPointer; } /** * @return list */ public static function getAllRootPointers(File $phpcsFile): array { $lazyValue = static fn (): array => TokenHelper::findNextAll($phpcsFile, T_OPEN_TAG, 0); return SniffLocalCache::getAndSetIfNotCached($phpcsFile, 'openTagPointers', $lazyValue); } } PK41]\((Gcoding-standard/SlevomatCodingStandard/Helpers/AnnotationTypeHelper.phpnu[print($typeNode); } public static function containsStaticOrThisType(TypeNode $typeNode): bool { if ($typeNode instanceof ThisTypeNode) { return true; } if ($typeNode instanceof IdentifierTypeNode) { return strtolower($typeNode->name) === 'static'; } if ( $typeNode instanceof UnionTypeNode || $typeNode instanceof IntersectionTypeNode ) { foreach ($typeNode->types as $innerTypeNode) { if (self::containsStaticOrThisType($innerTypeNode)) { return true; } } } return false; } public static function containsOneType(TypeNode $typeNode): bool { if ($typeNode instanceof IdentifierTypeNode) { return true; } if ($typeNode instanceof ThisTypeNode) { return true; } if ($typeNode instanceof GenericTypeNode) { return true; } if ($typeNode instanceof CallableTypeNode) { return true; } if ($typeNode instanceof ObjectShapeNode) { return true; } if ($typeNode instanceof ArrayShapeNode) { return true; } if ($typeNode instanceof ArrayTypeNode) { return true; } if ($typeNode instanceof ConstTypeNode) { if ($typeNode->constExpr instanceof ConstExprIntegerNode) { return true; } if ($typeNode->constExpr instanceof ConstExprFloatNode) { return true; } if ($typeNode->constExpr instanceof ConstExprStringNode) { return true; } } return false; } public static function containsJustTwoTypes(TypeNode $typeNode): bool { if ($typeNode instanceof NullableTypeNode && self::containsOneType($typeNode->type)) { return true; } if ( !$typeNode instanceof UnionTypeNode && !$typeNode instanceof IntersectionTypeNode ) { return false; } return count($typeNode->types) === 2; } /** * @param list $traversableTypeHints */ public static function containsTraversableType(TypeNode $typeNode, File $phpcsFile, int $pointer, array $traversableTypeHints): bool { if ($typeNode instanceof GenericTypeNode) { return true; } if ($typeNode instanceof ObjectShapeNode) { return false; } if ($typeNode instanceof ArrayShapeNode) { return true; } if ($typeNode instanceof ArrayTypeNode) { return true; } if ($typeNode instanceof IdentifierTypeNode) { $fullyQualifiedType = TypeHintHelper::getFullyQualifiedTypeHint($phpcsFile, $pointer, $typeNode->name); return TypeHintHelper::isTraversableType($fullyQualifiedType, $traversableTypeHints); } if ( $typeNode instanceof UnionTypeNode || $typeNode instanceof IntersectionTypeNode ) { foreach ($typeNode->types as $innerTypeNode) { if (self::containsTraversableType($innerTypeNode, $phpcsFile, $pointer, $traversableTypeHints)) { return true; } } } return ( $typeNode instanceof ConditionalTypeNode || $typeNode instanceof ConditionalTypeForParameterNode ) && ( self::containsTraversableType($typeNode->if, $phpcsFile, $pointer, $traversableTypeHints) || self::containsTraversableType($typeNode->else, $phpcsFile, $pointer, $traversableTypeHints) ); } /** * @param list $traversableTypeHints */ public static function containsItemsSpecificationForTraversable( TypeNode $typeNode, File $phpcsFile, int $pointer, array $traversableTypeHints, bool $inTraversable = false ): bool { if ($typeNode instanceof GenericTypeNode) { foreach ($typeNode->genericTypes as $genericType) { if (!self::containsItemsSpecificationForTraversable($genericType, $phpcsFile, $pointer, $traversableTypeHints, true)) { return false; } } return true; } if ($typeNode instanceof ArrayShapeNode || $typeNode instanceof ObjectShapeNode) { foreach ($typeNode->items as $innerItemNode) { if (!self::containsItemsSpecificationForTraversable( $innerItemNode->valueType, $phpcsFile, $pointer, $traversableTypeHints, true, )) { return false; } } return true; } if ($typeNode instanceof NullableTypeNode) { return self::containsItemsSpecificationForTraversable($typeNode->type, $phpcsFile, $pointer, $traversableTypeHints, true); } if ($typeNode instanceof IdentifierTypeNode) { if (TypeHintHelper::isTypeDefinedInAnnotation($phpcsFile, $pointer, $typeNode->name)) { // We can expect it's better type for traversable return true; } if (!$inTraversable) { return false; } return !TypeHintHelper::isTraversableType( TypeHintHelper::getFullyQualifiedTypeHint($phpcsFile, $pointer, $typeNode->name), $traversableTypeHints, ); } if ($typeNode instanceof ConstTypeNode) { return $inTraversable; } if ($typeNode instanceof CallableTypeNode) { return $inTraversable; } if ($typeNode instanceof ArrayTypeNode) { return self::containsItemsSpecificationForTraversable($typeNode->type, $phpcsFile, $pointer, $traversableTypeHints, true); } if ( $typeNode instanceof UnionTypeNode || $typeNode instanceof IntersectionTypeNode ) { foreach ($typeNode->types as $innerTypeNode) { if ( !$inTraversable && $innerTypeNode instanceof IdentifierTypeNode && strtolower($innerTypeNode->name) === 'null' ) { continue; } if (self::containsItemsSpecificationForTraversable( $innerTypeNode, $phpcsFile, $pointer, $traversableTypeHints, $inTraversable, )) { return true; } } } if ($typeNode instanceof ConditionalTypeNode || $typeNode instanceof ConditionalTypeForParameterNode) { return self::containsItemsSpecificationForTraversable($typeNode->if, $phpcsFile, $pointer, $traversableTypeHints, $inTraversable) || self::containsItemsSpecificationForTraversable( $typeNode->else, $phpcsFile, $pointer, $traversableTypeHints, $inTraversable, ); } return false; } public static function getTypeHintFromOneType( TypeNode $typeNode, bool $enableUnionTypeHint = false, bool $enableStandaloneNullTrueFalseTypeHints = false ): string { if ($typeNode instanceof GenericTypeNode) { $genericName = $typeNode->type->name; if (in_array(strtolower($genericName), ['non-empty-array', 'list', 'non-empty-list'], true)) { return 'array'; } return $genericName; } if ($typeNode instanceof IdentifierTypeNode) { if (strtolower($typeNode->name) === 'true') { return $enableStandaloneNullTrueFalseTypeHints ? 'true' : 'bool'; } if (strtolower($typeNode->name) === 'false') { return $enableUnionTypeHint || $enableStandaloneNullTrueFalseTypeHints ? 'false' : 'bool'; } if (in_array( strtolower($typeNode->name), ['positive-int', 'non-positive-int', 'negative-int', 'non-negative-int', 'literal-int', 'int-mask'], true, )) { return 'int'; } if (in_array( strtolower($typeNode->name), ['callable-array', 'callable-string'], true, )) { return 'callable'; } // See https://psalm.dev/docs/annotating_code/type_syntax/scalar_types/#class-string-interface-string if (preg_match('~-string$~i', $typeNode->name) === 1) { return 'string'; } if (in_array( strtolower($typeNode->name), ['non-empty-array', 'list', 'non-empty-list'], true, )) { return 'array'; } return $typeNode->name; } if ($typeNode instanceof CallableTypeNode) { return $typeNode->identifier->name; } if ($typeNode instanceof ArrayTypeNode) { return 'array'; } if ($typeNode instanceof ArrayShapeNode) { return 'array'; } if ($typeNode instanceof ObjectShapeNode) { return 'object'; } if ($typeNode instanceof ConstTypeNode) { if ($typeNode->constExpr instanceof ConstExprIntegerNode) { return 'int'; } if ($typeNode->constExpr instanceof ConstExprFloatNode) { return 'float'; } if ($typeNode->constExpr instanceof ConstExprStringNode) { return 'string'; } } return (string) $typeNode; } /** * @param UnionTypeNode|IntersectionTypeNode $typeNode * @param list $traversableTypeHints * @return list */ public static function getTraversableTypeHintsFromType( TypeNode $typeNode, File $phpcsFile, int $pointer, array $traversableTypeHints, bool $enableUnionTypeHint = false ): array { $typeHints = []; foreach ($typeNode->types as $type) { if ( $type instanceof GenericTypeNode || $type instanceof ThisTypeNode || $type instanceof IdentifierTypeNode ) { $typeHints[] = self::getTypeHintFromOneType($type); } } if (!$enableUnionTypeHint && count($typeHints) > 1) { return []; } foreach ($typeHints as $typeHint) { if (!TypeHintHelper::isTraversableType( TypeHintHelper::getFullyQualifiedTypeHint($phpcsFile, $pointer, $typeHint), $traversableTypeHints, )) { return []; } } return $typeHints; } /** * @param UnionTypeNode|IntersectionTypeNode $typeNode */ public static function getItemsSpecificationTypeFromType(TypeNode $typeNode): ?TypeNode { foreach ($typeNode->types as $type) { if ($type instanceof ArrayTypeNode) { return $type; } } return null; } } PK41]_>coding-standard/SlevomatCodingStandard/Helpers/ArrayHelper.phpnu[ */ public static function parse(File $phpcsFile, int $arrayPointer): array { $tokens = $phpcsFile->getTokens(); $arrayToken = $tokens[$arrayPointer]; [$arrayOpenerPointer, $arrayCloserPointer] = self::openClosePointers($arrayToken); $keyValues = []; $firstPointerOnNextLine = TokenHelper::findFirstTokenOnNextLine($phpcsFile, $arrayOpenerPointer + 1); $firstEffectivePointer = TokenHelper::findNextEffective($phpcsFile, $arrayOpenerPointer + 1); $arrayKeyValueStartPointer = $firstPointerOnNextLine !== null && $firstPointerOnNextLine < $firstEffectivePointer ? $firstPointerOnNextLine : $firstEffectivePointer; $arrayKeyValueEndPointer = $arrayKeyValueStartPointer; $indentation = $tokens[$arrayOpenerPointer]['line'] < $tokens[$firstEffectivePointer]['line'] ? IndentationHelper::getIndentation($phpcsFile, $firstEffectivePointer) : ''; for ($i = $arrayKeyValueStartPointer; $i < $arrayCloserPointer; $i++) { $token = $tokens[$i]; if (in_array($token['code'], TokenHelper::ARRAY_TOKEN_CODES, true)) { $i = self::openClosePointers($token)[1]; continue; } if (array_key_exists('scope_closer', $token) && $token['scope_closer'] > $i) { $i = $token['scope_closer'] - 1; continue; } if (array_key_exists('parenthesis_closer', $token) && $token['parenthesis_closer'] > $i) { $i = $token['parenthesis_closer'] - 1; continue; } $nextEffectivePointer = TokenHelper::findNextEffective($phpcsFile, $i + 1); if ($nextEffectivePointer === $arrayCloserPointer) { $arrayKeyValueEndPointer = self::getValueEndPointer($phpcsFile, $i, $arrayCloserPointer, $indentation); break; } if ($token['code'] !== T_COMMA || !ScopeHelper::isInSameScope($phpcsFile, $arrayOpenerPointer, $i)) { $arrayKeyValueEndPointer = $i; continue; } $arrayKeyValueEndPointer = self::getValueEndPointer($phpcsFile, $i, $arrayCloserPointer, $indentation); $keyValues[] = new ArrayKeyValue($phpcsFile, $arrayKeyValueStartPointer, $arrayKeyValueEndPointer); $arrayKeyValueStartPointer = $arrayKeyValueEndPointer + 1; $i = $arrayKeyValueEndPointer; } $keyValues[] = new ArrayKeyValue($phpcsFile, $arrayKeyValueStartPointer, $arrayKeyValueEndPointer); return $keyValues; } /** * @param list $keyValues */ public static function getIndentation(array $keyValues): ?string { $indents = []; foreach ($keyValues as $keyValue) { $indent = $keyValue->getIndent() ?? 'null'; $indents[$indent] = isset($indents[$indent]) ? $indents[$indent] + 1 : 1; } arsort($indents); $indent = key($indents); return $indent !== 'null' ? (string) $indent : null; } /** * @param list $keyValues */ public static function isKeyed(array $keyValues): bool { foreach ($keyValues as $keyValue) { if ($keyValue->getKey() !== null) { return true; } } return false; } /** * @param list $keyValues */ public static function isKeyedAll(array $keyValues): bool { foreach ($keyValues as $keyValue) { if (!$keyValue->isUnpacking() && $keyValue->getKey() === null) { return false; } } return true; } /** * Test if non-empty array with opening & closing brackets on separate lines */ public static function isMultiLine(File $phpcsFile, int $pointer): bool { $tokens = $phpcsFile->getTokens(); $token = $tokens[$pointer]; [$pointerOpener, $pointerCloser] = self::openClosePointers($token); $tokenOpener = $tokens[$pointerOpener]; $tokenCloser = $tokens[$pointerCloser]; return $tokenOpener['line'] !== $tokenCloser['line']; } /** * Test if effective tokens between open & closing tokens */ public static function isNotEmpty(File $phpcsFile, int $pointer): bool { $tokens = $phpcsFile->getTokens(); $token = $tokens[$pointer]; [$pointerOpener, $pointerCloser] = self::openClosePointers($token); /** @var int $pointerPreviousToClose */ $pointerPreviousToClose = TokenHelper::findPreviousEffective($phpcsFile, $pointerCloser - 1); return $pointerPreviousToClose !== $pointerOpener; } /** * @param list $keyValues */ public static function isSortedByKey(array $keyValues): bool { $previousKey = ''; foreach ($keyValues as $keyValue) { if ($keyValue->isUnpacking()) { continue; } if (strnatcasecmp($previousKey, $keyValue->getKey()) === 1) { return false; } $previousKey = $keyValue->getKey(); } return true; } /** * @param array|int|string> $token * @return array{0: int, 1: int} */ public static function openClosePointers(array $token): array { $isShortArray = $token['code'] === T_OPEN_SHORT_ARRAY; $pointerOpener = $isShortArray ? $token['bracket_opener'] : $token['parenthesis_opener']; $pointerCloser = $isShortArray ? $token['bracket_closer'] : $token['parenthesis_closer']; return [(int) $pointerOpener, (int) $pointerCloser]; } private static function getValueEndPointer(File $phpcsFile, int $endPointer, int $arrayCloserPointer, string $indentation): int { $tokens = $phpcsFile->getTokens(); $nextEffectivePointer = TokenHelper::findNextEffective($phpcsFile, $endPointer + 1, $arrayCloserPointer + 1); if ($tokens[$nextEffectivePointer]['line'] === $tokens[$endPointer]['line']) { return $nextEffectivePointer - 1; } for ($i = $endPointer + 1; $i < $nextEffectivePointer; $i++) { if ($tokens[$i]['line'] === $tokens[$endPointer]['line']) { $endPointer = $i; continue; } $nextNonWhitespacePointer = TokenHelper::findNextNonWhitespace($phpcsFile, $i); if (!in_array($tokens[$nextNonWhitespacePointer]['code'], TokenHelper::INLINE_COMMENT_TOKEN_CODES, true)) { break; } if ($indentation === IndentationHelper::getIndentation($phpcsFile, $nextNonWhitespacePointer)) { $endPointer = $i - 1; break; } $i = TokenHelper::findLastTokenOnLine($phpcsFile, $i); $endPointer = $i; } return $endPointer; } } PK41]7  Ccoding-standard/SlevomatCodingStandard/Helpers/ParsedDocComment.phpnu[openPointer = $openPointer; $this->closePointer = $closePointer; $this->node = $node; $this->tokens = $tokens; } public function getOpenPointer(): int { return $this->openPointer; } public function getClosePointer(): int { return $this->closePointer; } public function getNode(): PhpDocNode { return $this->node; } public function getTokens(): TokenIterator { return $this->tokens; } public function getNodeStartPointer(File $phpcsFile, Node $node): int { $tokens = $phpcsFile->getTokens(); $tagStartLine = $tokens[$this->openPointer]['line'] + $node->getAttribute('startLine') - 1; $searchPointer = $this->openPointer + 1; for ($i = $this->openPointer + 1; $i < $this->closePointer; $i++) { if ($tagStartLine === $tokens[$i]['line']) { $searchPointer = $i; break; } } return TokenHelper::findNext($phpcsFile, [...TokenHelper::ANNOTATION_TOKEN_CODES, T_DOC_COMMENT_STRING], $searchPointer); } public function getNodeEndPointer(File $phpcsFile, Node $node, int $nodeStartPointer): int { $tokens = $phpcsFile->getTokens(); $content = trim($this->tokens->getContentBetween( $node->getAttribute(Attribute::START_INDEX), $node->getAttribute(Attribute::END_INDEX) + 1, )); $length = strlen($content); $searchPointer = $nodeStartPointer; $content = ''; for ($i = $nodeStartPointer; $i < count($tokens); $i++) { $content .= $tokens[$i]['content']; if (strlen($content) >= $length) { $searchPointer = $i; break; } } return TokenHelper::findPrevious( $phpcsFile, [...TokenHelper::ANNOTATION_TOKEN_CODES, T_DOC_COMMENT_STRING], $searchPointer, ); } } PK41] Bcoding-standard/SlevomatCodingStandard/Helpers/AttributeHelper.phpnu[ $name->getFullyQualifiedName(), self::getAttributes($phpcsFile, $pointer), ); return in_array($attributeName, $attributeNames, true); } /** * @return list */ public static function getAttributes(File $phpcsFile, int $pointer): array { $tokens = $phpcsFile->getTokens(); if ($tokens[$pointer]['code'] !== T_ATTRIBUTE) { $attributeOpenerPointer = null; do { $attributeEndPointerCandidate = TokenHelper::findPrevious( $phpcsFile, [T_ATTRIBUTE_END, T_SEMICOLON, T_CLOSE_CURLY_BRACKET, T_OPEN_CURLY_BRACKET], $attributeOpenerPointer ?? $pointer - 1, ); if ( $attributeEndPointerCandidate === null || $tokens[$attributeEndPointerCandidate]['code'] !== T_ATTRIBUTE_END ) { break; } $attributeOpenerPointer = $tokens[$attributeEndPointerCandidate]['attribute_opener']; } while (true); if ($attributeOpenerPointer === null) { return []; } } else { $attributeOpenerPointer = $pointer; } $attributeCloserPointer = $tokens[$attributeOpenerPointer]['attribute_closer']; $actualPointer = $attributeOpenerPointer; $attributes = []; do { $attributeNameStartPointer = TokenHelper::findNextEffective($phpcsFile, $actualPointer + 1, $attributeCloserPointer); if ($attributeNameStartPointer === null) { break; } $attributeNameEndPointer = TokenHelper::findNextExcluding( $phpcsFile, TokenHelper::NAME_TOKEN_CODES, $attributeNameStartPointer + 1, ) - 1; $attributeName = TokenHelper::getContent($phpcsFile, $attributeNameStartPointer, $attributeNameEndPointer); $pointerAfterAttributeName = TokenHelper::findNextEffective($phpcsFile, $attributeNameEndPointer + 1, $attributeCloserPointer); if ($pointerAfterAttributeName === null) { $attributes[] = new Attribute( $attributeOpenerPointer, $attributeName, NamespaceHelper::resolveClassName($phpcsFile, $attributeName, $attributeOpenerPointer), $attributeNameStartPointer, $attributeNameEndPointer, ); break; } if ($tokens[$pointerAfterAttributeName]['code'] === T_COMMA) { $attributes[] = new Attribute( $attributeOpenerPointer, $attributeName, NamespaceHelper::resolveClassName($phpcsFile, $attributeName, $attributeOpenerPointer), $attributeNameStartPointer, $attributeNameEndPointer, ); $actualPointer = $pointerAfterAttributeName; } if ($tokens[$pointerAfterAttributeName]['code'] === T_OPEN_PARENTHESIS) { $attributes[] = new Attribute( $attributeOpenerPointer, $attributeName, NamespaceHelper::resolveClassName($phpcsFile, $attributeName, $attributeOpenerPointer), $attributeNameStartPointer, $tokens[$pointerAfterAttributeName]['parenthesis_closer'], TokenHelper::getContent( $phpcsFile, $pointerAfterAttributeName, $tokens[$pointerAfterAttributeName]['parenthesis_closer'], ), ); $actualPointer = TokenHelper::findNextEffective( $phpcsFile, $tokens[$pointerAfterAttributeName]['parenthesis_closer'] + 1, $attributeCloserPointer, ); continue; } } while ($actualPointer !== null); return $attributes; } /** * Attributes have syntax that when defined incorrectly or in older PHP version, they are treated as comments. * An example of incorrect declaration is variables that are not properties. */ public static function isValidAttribute(File $phpcsFile, int $attributeOpenerPointer): bool { return self::getAttributeTarget($phpcsFile, $attributeOpenerPointer) !== null; } public static function getAttributeTarget(File $phpcsFile, int $attributeOpenerPointer): ?int { $attributeTargetPointer = TokenHelper::findNext($phpcsFile, self::ATTRIBUTE_TARGETS, $attributeOpenerPointer); if ($attributeTargetPointer === null) { return null; } if ( $phpcsFile->getTokens()[$attributeTargetPointer]['code'] === T_VARIABLE && !PropertyHelper::isProperty($phpcsFile, $attributeTargetPointer) && !ParameterHelper::isParameter($phpcsFile, $attributeTargetPointer) ) { return null; } return $attributeTargetPointer; } } PK41]MgAcoding-standard/SlevomatCodingStandard/Helpers/PropertyHelper.phpnu[getTokens(); $previousPointer = TokenHelper::findPreviousExcluding( $phpcsFile, [...TokenHelper::INEFFECTIVE_TOKEN_CODES, ...TokenHelper::TYPE_HINT_TOKEN_CODES, T_NULLABLE], $variablePointer - 1, ); if (in_array($tokens[$previousPointer]['code'], [T_FINAL, T_ABSTRACT], true)) { return true; } if ($tokens[$previousPointer]['code'] === T_STATIC) { $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $previousPointer - 1); } if (in_array( $tokens[$previousPointer]['code'], [...array_values(Tokens::$scopeModifiers), T_READONLY], true, )) { $constructorPointer = TokenHelper::findPrevious($phpcsFile, T_FUNCTION, $previousPointer - 1); if ($constructorPointer === null) { return true; } return $tokens[$constructorPointer]['parenthesis_closer'] < $previousPointer || $promoted; } if ( !array_key_exists('conditions', $tokens[$variablePointer]) || count($tokens[$variablePointer]['conditions']) === 0 ) { return false; } $functionPointer = TokenHelper::findPrevious( $phpcsFile, [...TokenHelper::FUNCTION_TOKEN_CODES, T_SEMICOLON, T_CLOSE_CURLY_BRACKET, T_OPEN_CURLY_BRACKET], $variablePointer - 1, ); if ( $functionPointer !== null && in_array($tokens[$functionPointer]['code'], TokenHelper::FUNCTION_TOKEN_CODES, true) ) { return false; } $previousParenthesisPointer = TokenHelper::findPrevious($phpcsFile, T_OPEN_PARENTHESIS, $variablePointer - 1); if ($previousParenthesisPointer !== null && $tokens[$previousParenthesisPointer]['parenthesis_closer'] > $variablePointer) { $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $previousParenthesisPointer - 1); if ($previousPointer !== null && in_array($tokens[$previousPointer]['content'], ['get', 'set'], true)) { // Parameter of property hook return false; } } $previousCurlyBracketPointer = TokenHelper::findPrevious($phpcsFile, T_OPEN_CURLY_BRACKET, $variablePointer - 1); if ( $previousCurlyBracketPointer !== null && $tokens[$previousCurlyBracketPointer]['bracket_closer'] > $variablePointer ) { // Variable in content of property hook if (!array_key_exists('scope_condition', $tokens[$previousCurlyBracketPointer])) { return false; } } $conditionCode = array_values($tokens[$variablePointer]['conditions'])[count($tokens[$variablePointer]['conditions']) - 1]; return in_array($conditionCode, Tokens::$ooScopeTokens, true); } public static function getStartPointer(File $phpcsFile, int $propertyPointer): int { $previousCodeEndPointer = TokenHelper::findPrevious( $phpcsFile, [ // Previous property or constant T_SEMICOLON, // Previous method or property with hooks T_CLOSE_CURLY_BRACKET, // Start of the class T_OPEN_CURLY_BRACKET, // Start of the constructor T_OPEN_PARENTHESIS, // Previous parameter in the constructor T_COMMA, ], $propertyPointer - 1, ); $startPointer = TokenHelper::findPreviousEffective($phpcsFile, $propertyPointer - 1, $previousCodeEndPointer); do { $possibleStartPointer = TokenHelper::findPrevious( $phpcsFile, TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES, $startPointer - 1, $previousCodeEndPointer, ); if ($possibleStartPointer === null) { return $startPointer; } $startPointer = $possibleStartPointer; } while (true); } public static function getEndPointer(File $phpcsFile, int $propertyPointer): int { $tokens = $phpcsFile->getTokens(); $endPointer = TokenHelper::findNext($phpcsFile, [T_SEMICOLON, T_OPEN_CURLY_BRACKET], $propertyPointer + 1); return $tokens[$endPointer]['code'] === T_OPEN_CURLY_BRACKET ? $tokens[$endPointer]['bracket_closer'] : $endPointer; } public static function findTypeHint(File $phpcsFile, int $propertyPointer): ?TypeHint { $tokens = $phpcsFile->getTokens(); $propertyStartPointer = self::getStartPointer($phpcsFile, $propertyPointer); $typeHintEndPointer = TokenHelper::findPrevious( $phpcsFile, TokenHelper::TYPE_HINT_TOKEN_CODES, $propertyPointer - 1, $propertyStartPointer, ); if ($typeHintEndPointer === null) { return null; } $typeHintStartPointer = TypeHintHelper::getStartPointer($phpcsFile, $typeHintEndPointer); $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $typeHintStartPointer - 1, $propertyStartPointer); $nullable = $previousPointer !== null && $tokens[$previousPointer]['code'] === T_NULLABLE; if ($nullable) { $typeHintStartPointer = $previousPointer; } $typeHint = TokenHelper::getContent($phpcsFile, $typeHintStartPointer, $typeHintEndPointer); if (!$nullable) { $nullable = preg_match('~(?:^|\|\s*)null(?:\s*\||$)~i', $typeHint) === 1; } /** @var string $typeHint */ $typeHint = preg_replace('~\s+~', '', $typeHint); return new TypeHint($typeHint, $nullable, $typeHintStartPointer, $typeHintEndPointer); } public static function getFullyQualifiedName(File $phpcsFile, int $propertyPointer): string { $propertyToken = $phpcsFile->getTokens()[$propertyPointer]; $propertyName = $propertyToken['content']; $classPointer = array_reverse(array_keys($propertyToken['conditions']))[0]; if ($phpcsFile->getTokens()[$classPointer]['code'] === T_ANON_CLASS) { return sprintf('class@anonymous::%s', $propertyName); } $name = sprintf('%s%s::%s', NamespaceHelper::NAMESPACE_SEPARATOR, ClassHelper::getName($phpcsFile, $classPointer), $propertyName); $namespace = NamespaceHelper::findCurrentNamespaceName($phpcsFile, $propertyPointer); return $namespace !== null ? sprintf('%s%s%s', NamespaceHelper::NAMESPACE_SEPARATOR, $namespace, $name) : $name; } } PK41]ߦwwCcoding-standard/SlevomatCodingStandard/Helpers/DocCommentHelper.phpnu[getTokens()[$docCommentOpenToken]['comment_closer'], ), ); } /** * @return list|null */ public static function getDocCommentDescription(File $phpcsFile, int $pointer): ?array { $docCommentOpenPointer = self::findDocCommentOpenPointer($phpcsFile, $pointer); if ($docCommentOpenPointer === null) { return null; } $tokens = $phpcsFile->getTokens(); $descriptionStartPointer = TokenHelper::findNextExcluding( $phpcsFile, [T_DOC_COMMENT_WHITESPACE, T_DOC_COMMENT_STAR], $docCommentOpenPointer + 1, $tokens[$docCommentOpenPointer]['comment_closer'], ); if ($descriptionStartPointer === null) { return null; } if ($tokens[$descriptionStartPointer]['code'] !== T_DOC_COMMENT_STRING) { return null; } $tokenAfterDescriptionPointer = TokenHelper::findNext( $phpcsFile, [T_DOC_COMMENT_TAG, T_DOC_COMMENT_CLOSE_TAG], $descriptionStartPointer + 1, $tokens[$docCommentOpenPointer]['comment_closer'] + 1, ); /** @var list $comments */ $comments = []; for ($i = $descriptionStartPointer; $i < $tokenAfterDescriptionPointer; $i++) { if ($tokens[$i]['code'] !== T_DOC_COMMENT_STRING) { continue; } $comments[] = new Comment($i, trim($tokens[$i]['content'])); } return count($comments) > 0 ? $comments : null; } public static function hasInheritdocAnnotation(File $phpcsFile, int $pointer): bool { $docCommentOpenPointer = self::findDocCommentOpenPointer($phpcsFile, $pointer); if ($docCommentOpenPointer === null) { return false; } $parsedDocComment = self::parseDocComment($phpcsFile, $docCommentOpenPointer); if ($parsedDocComment === null) { return false; } foreach ($parsedDocComment->getNode()->children as $child) { if ($child instanceof PhpDocTagNode) { if (strtolower($child->name) === '@inheritdoc') { return true; } if (stripos((string) $child->value, '{@inheritdoc}') !== false) { return true; } } if ($child instanceof PhpDocTextNode && stripos($child->text, '{@inheritdoc}') !== false) { return true; } } return false; } public static function hasDocCommentDescription(File $phpcsFile, int $pointer): bool { return self::getDocCommentDescription($phpcsFile, $pointer) !== null; } public static function findDocCommentOpenPointer(File $phpcsFile, int $pointer): ?int { return SniffLocalCache::getAndSetIfNotCached( $phpcsFile, sprintf('doc-comment-open-pointer-%d', $pointer), static function () use ($phpcsFile, $pointer): ?int { $tokens = $phpcsFile->getTokens(); if ($tokens[$pointer]['code'] === T_DOC_COMMENT_OPEN_TAG) { return $pointer; } $found = TokenHelper::findPrevious( $phpcsFile, [T_DOC_COMMENT_CLOSE_TAG, T_SEMICOLON, T_CLOSE_CURLY_BRACKET, T_OPEN_CURLY_BRACKET], $pointer - 1, ); if ($found !== null && $tokens[$found]['code'] === T_DOC_COMMENT_CLOSE_TAG) { return $tokens[$found]['comment_opener']; } return null; }, ); } public static function findDocCommentOwnerPointer(File $phpcsFile, int $docCommentOpenPointer): ?int { $tokens = $phpcsFile->getTokens(); $docCommentCloserPointer = $tokens[$docCommentOpenPointer]['comment_closer']; if (self::isInline($phpcsFile, $docCommentOpenPointer)) { return null; } $docCommentOwnerPointer = null; for ($i = $docCommentCloserPointer + 1; $i < count($tokens); $i++) { if ($tokens[$i]['code'] === T_ATTRIBUTE) { $i = $tokens[$i]['attribute_closer']; continue; } if (in_array( $tokens[$i]['code'], [T_PUBLIC, T_PROTECTED, T_PRIVATE, T_VAR, T_READONLY, T_FINAL, T_STATIC, T_ABSTRACT, T_WHITESPACE], true, )) { continue; } if (in_array( $tokens[$i]['code'], [T_FUNCTION, T_VARIABLE, T_CONST, ...TokenHelper::CLASS_TYPE_TOKEN_CODES], true, )) { $docCommentOwnerPointer = $i; } break; } return $docCommentOwnerPointer; } public static function isInline(File $phpcsFile, int $docCommentOpenPointer): bool { $tokens = $phpcsFile->getTokens(); $nextPointer = TokenHelper::findNextNonWhitespace($phpcsFile, $tokens[$docCommentOpenPointer]['comment_closer'] + 1); if ( $nextPointer !== null && in_array( $tokens[$nextPointer]['code'], [T_PUBLIC, T_PROTECTED, T_PRIVATE, T_READONLY, T_FINAL, T_STATIC, T_ABSTRACT, T_CONST, T_CLASS, T_INTERFACE, T_TRAIT, T_ENUM, T_ATTRIBUTE], true, ) ) { return false; } $parsedDocComment = self::parseDocComment($phpcsFile, $docCommentOpenPointer); if ($parsedDocComment === null) { return false; } foreach ($parsedDocComment->getNode()->getTags() as $annotation) { if (preg_match('~^@(?:(?:phpstan|psalm)-)?var~i', $annotation->name) === 1) { return true; } } return false; } public static function parseDocComment(File $phpcsFile, int $docCommentOpenPointer): ?ParsedDocComment { return SniffLocalCache::getAndSetIfNotCached( $phpcsFile, sprintf('parsed-doc-comment-%d', $docCommentOpenPointer), static function () use ($phpcsFile, $docCommentOpenPointer): ?ParsedDocComment { $docComment = self::getDocComment($phpcsFile, $docCommentOpenPointer); $docCommentTokens = new TokenIterator(PhpDocParserHelper::getLexer()->tokenize($docComment)); try { $parsedDocComment = PhpDocParserHelper::getParser()->parse($docCommentTokens); return new ParsedDocComment( $docCommentOpenPointer, $phpcsFile->getTokens()[$docCommentOpenPointer]['comment_closer'], $parsedDocComment, $docCommentTokens, ); } catch (ParserException $e) { return null; } }, ); } } PK41],  @coding-standard/SlevomatCodingStandard/Helpers/CommentHelper.phpnu[getTokens()[$commentPointer]['content']); } public static function getCommentEndPointer(File $phpcsFile, int $commentStartPointer): ?int { $tokens = $phpcsFile->getTokens(); if (array_key_exists('comment_closer', $tokens[$commentStartPointer])) { return $tokens[$commentStartPointer]['comment_closer']; } if (self::isLineComment($phpcsFile, $commentStartPointer)) { return $commentStartPointer; } if (strpos($tokens[$commentStartPointer]['content'], '/*') !== 0) { // Part of block comment return null; } $commentEndPointer = $commentStartPointer; for ($i = $commentStartPointer + 1; $i < $phpcsFile->numTokens; $i++) { if ($tokens[$i]['code'] === T_COMMENT) { $commentEndPointer = $i; continue; } if (in_array($tokens[$i]['code'], Tokens::$phpcsCommentTokens, true)) { $commentEndPointer = $i; continue; } break; } return $commentEndPointer; } public static function getMultilineCommentStartPointer(File $phpcsFile, int $commentEndPointer): int { $tokens = $phpcsFile->getTokens(); $commentStartPointer = $commentEndPointer; do { $commentBefore = TokenHelper::findPrevious($phpcsFile, TokenHelper::INLINE_COMMENT_TOKEN_CODES, $commentStartPointer - 1); if ($commentBefore === null) { break; } if ($tokens[$commentBefore]['line'] + 1 !== $tokens[$commentStartPointer]['line']) { break; } /** @var int $commentStartPointer */ $commentStartPointer = $commentBefore; } while (true); return $commentStartPointer; } public static function getMultilineCommentEndPointer(File $phpcsFile, int $commentStartPointer): int { $tokens = $phpcsFile->getTokens(); $commentEndPointer = $commentStartPointer; do { $commentAfter = TokenHelper::findNext($phpcsFile, TokenHelper::INLINE_COMMENT_TOKEN_CODES, $commentEndPointer + 1); if ($commentAfter === null) { break; } if ($tokens[$commentAfter]['line'] - 1 !== $tokens[$commentEndPointer]['line']) { break; } /** @var int $commentEndPointer */ $commentEndPointer = $commentAfter; } while (true); return $commentEndPointer; } } PK41]^^Fcoding-standard/SlevomatCodingStandard/Helpers/IdentificatorHelper.phpnu[getTokens(); $variableContent = ''; for ($i = $startPointer; $i <= $endPointer; $i++) { if (in_array($tokens[$i]['code'], TokenHelper::INEFFECTIVE_TOKEN_CODES, true)) { continue; } $variableContent .= $tokens[$i]['content']; } return $variableContent; } public static function findStartPointer(File $phpcsFile, int $endPointer): ?int { $tokens = $phpcsFile->getTokens(); if (in_array($tokens[$endPointer]['code'], TokenHelper::NAME_TOKEN_CODES, true)) { /** @var int $previousPointer */ $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $endPointer - 1); if (in_array($tokens[$previousPointer]['code'], [T_OBJECT_OPERATOR, T_NULLSAFE_OBJECT_OPERATOR, T_DOUBLE_COLON], true)) { $pointerBeforeOperator = TokenHelper::findPreviousEffective($phpcsFile, $previousPointer - 1); if ($tokens[$pointerBeforeOperator]['code'] !== T_CLOSE_PARENTHESIS) { return self::getStartPointerBeforeOperator($phpcsFile, $previousPointer); } } return $endPointer; } if (in_array($tokens[$endPointer]['code'], [T_CLOSE_CURLY_BRACKET, T_CLOSE_SQUARE_BRACKET], true)) { return self::getStartPointerBeforeVariablePart($phpcsFile, $tokens[$endPointer]['bracket_opener']); } if ($tokens[$endPointer]['code'] === T_VARIABLE) { return self::getStartPointerBeforeVariablePart($phpcsFile, $endPointer); } return null; } public static function findEndPointer(File $phpcsFile, int $startPointer): ?int { $tokens = $phpcsFile->getTokens(); if (in_array($tokens[$startPointer]['code'], TokenHelper::NAME_TOKEN_CODES, true)) { $startPointer = TokenHelper::findNextExcluding($phpcsFile, TokenHelper::NAME_TOKEN_CODES, $startPointer + 1) - 1; } elseif ($tokens[$startPointer]['code'] === T_DOLLAR) { $startPointer = TokenHelper::findNextEffective($phpcsFile, $startPointer + 1); } /** @var int $nextPointer */ $nextPointer = TokenHelper::findNextEffective($phpcsFile, $startPointer + 1); if ( in_array($tokens[$startPointer]['code'], [T_SELF, T_STATIC, T_PARENT, ...TokenHelper::NAME_TOKEN_CODES], true) && $tokens[$nextPointer]['code'] === T_DOUBLE_COLON ) { return self::getEndPointerAfterOperator($phpcsFile, $nextPointer); } if ($tokens[$startPointer]['code'] === T_VARIABLE) { if (in_array($tokens[$nextPointer]['code'], [T_DOUBLE_COLON, T_OBJECT_OPERATOR, T_NULLSAFE_OBJECT_OPERATOR], true)) { return self::getEndPointerAfterOperator($phpcsFile, $nextPointer); } if ($tokens[$nextPointer]['code'] === T_OPEN_SQUARE_BRACKET) { return self::getEndPointerAfterVariablePart($phpcsFile, $startPointer); } return $startPointer; } return null; } private static function getStartPointerBeforeOperator(File $phpcsFile, int $operatorPointer): int { $tokens = $phpcsFile->getTokens(); /** @var int $previousPointer */ $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $operatorPointer - 1); if (in_array($tokens[$previousPointer]['code'], TokenHelper::NAME_TOKEN_CODES, true)) { $previousPointer = TokenHelper::findPreviousExcluding($phpcsFile, TokenHelper::NAME_TOKEN_CODES, $previousPointer - 1) + 1; } if ( $tokens[$operatorPointer]['code'] === T_DOUBLE_COLON && in_array($tokens[$previousPointer]['code'], [T_SELF, T_STATIC, T_PARENT, ...TokenHelper::NAME_TOKEN_CODES], true) ) { return $previousPointer; } if (in_array($tokens[$previousPointer]['code'], TokenHelper::NAME_TOKEN_CODES, true)) { /** @var int $possibleOperatorPointer */ $possibleOperatorPointer = TokenHelper::findPreviousEffective($phpcsFile, $previousPointer - 1); if (in_array($tokens[$possibleOperatorPointer]['code'], [T_OBJECT_OPERATOR, T_NULLSAFE_OBJECT_OPERATOR], true)) { return self::getStartPointerBeforeOperator($phpcsFile, $possibleOperatorPointer); } } if (in_array($tokens[$previousPointer]['code'], [T_CLOSE_CURLY_BRACKET, T_CLOSE_SQUARE_BRACKET], true)) { return self::getStartPointerBeforeVariablePart($phpcsFile, $tokens[$previousPointer]['bracket_opener']); } return self::getStartPointerBeforeVariablePart($phpcsFile, $previousPointer); } private static function getStartPointerBeforeVariablePart(File $phpcsFile, int $variablePartPointer): int { $tokens = $phpcsFile->getTokens(); /** @var int $previousPointer */ $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $variablePartPointer - 1); if ($tokens[$previousPointer]['code'] === T_DOLLAR) { /** @var int $previousPointer */ $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $previousPointer - 1); } if (in_array($tokens[$previousPointer]['code'], [T_OBJECT_OPERATOR, T_NULLSAFE_OBJECT_OPERATOR, T_DOUBLE_COLON], true)) { return self::getStartPointerBeforeOperator($phpcsFile, $previousPointer); } if ($tokens[$previousPointer]['code'] === T_CLOSE_SQUARE_BRACKET) { return self::getStartPointerBeforeVariablePart($phpcsFile, $tokens[$previousPointer]['bracket_opener']); } if ( $tokens[$previousPointer]['code'] === T_CLOSE_CURLY_BRACKET && !array_key_exists('scope_condition', $tokens[$previousPointer]) ) { return self::getStartPointerBeforeVariablePart($phpcsFile, $tokens[$previousPointer]['bracket_opener']); } if (in_array($tokens[$previousPointer]['code'], [T_VARIABLE, ...TokenHelper::NAME_TOKEN_CODES], true)) { return self::getStartPointerBeforeVariablePart($phpcsFile, $previousPointer); } return $variablePartPointer; } private static function getEndPointerAfterOperator(File $phpcsFile, int $operatorPointer): int { $tokens = $phpcsFile->getTokens(); /** @var int $nextPointer */ $nextPointer = TokenHelper::findNextEffective($phpcsFile, $operatorPointer + 1); if ($tokens[$nextPointer]['code'] === T_DOLLAR) { /** @var int $nextPointer */ $nextPointer = TokenHelper::findNextEffective($phpcsFile, $nextPointer + 1); } if ($tokens[$nextPointer]['code'] === T_OPEN_CURLY_BRACKET) { return self::getEndPointerAfterVariablePart($phpcsFile, $tokens[$nextPointer]['bracket_closer']); } return self::getEndPointerAfterVariablePart($phpcsFile, $nextPointer); } private static function getEndPointerAfterVariablePart(File $phpcsFile, int $variablePartPointer): int { $tokens = $phpcsFile->getTokens(); /** @var int $nextPointer */ $nextPointer = TokenHelper::findNextEffective($phpcsFile, $variablePartPointer + 1); if (in_array($tokens[$nextPointer]['code'], [T_OBJECT_OPERATOR, T_NULLSAFE_OBJECT_OPERATOR, T_DOUBLE_COLON], true)) { return self::getEndPointerAfterOperator($phpcsFile, $nextPointer); } if ($tokens[$nextPointer]['code'] === T_OPEN_SQUARE_BRACKET) { return self::getEndPointerAfterVariablePart($phpcsFile, $tokens[$nextPointer]['bracket_closer']); } return $variablePartPointer; } } PK41]^[pAcoding-standard/SlevomatCodingStandard/Helpers/ConstantHelper.phpnu[getTokens(); return $tokens[TokenHelper::findNext($phpcsFile, T_STRING, $constantPointer + 1)]['content']; } public static function getFullyQualifiedName(File $phpcsFile, int $constantPointer): string { $name = self::getName($phpcsFile, $constantPointer); $namespace = NamespaceHelper::findCurrentNamespaceName($phpcsFile, $constantPointer); return $namespace !== null ? sprintf('%s%s%s%s', NamespaceHelper::NAMESPACE_SEPARATOR, $namespace, NamespaceHelper::NAMESPACE_SEPARATOR, $name) : $name; } /** * @return list */ public static function getAllNames(File $phpcsFile): array { $previousConstantPointer = 0; return array_map( static fn (int $constantPointer): string => self::getName($phpcsFile, $constantPointer), array_values(array_filter( iterator_to_array(self::getAllConstantPointers($phpcsFile, $previousConstantPointer)), static function (int $constantPointer) use ($phpcsFile): bool { foreach (array_reverse($phpcsFile->getTokens()[$constantPointer]['conditions']) as $conditionTokenCode) { return $conditionTokenCode === T_NAMESPACE; } return true; }, )), ); } /** * @return Generator */ private static function getAllConstantPointers(File $phpcsFile, int &$previousConstantPointer): Generator { do { $nextConstantPointer = TokenHelper::findNext($phpcsFile, T_CONST, $previousConstantPointer + 1); if ($nextConstantPointer === null) { break; } $previousConstantPointer = $nextConstantPointer; yield $nextConstantPointer; } while (true); } } PK41]71Bcoding-standard/SlevomatCodingStandard/Helpers/SniffLocalCache.phpnu[> */ private static array $cache = []; /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.DisallowMixedTypeHint * @return mixed */ public static function getAndSetIfNotCached(File $phpcsFile, string $key, Closure $lazyValue) { $fixerLoops = $phpcsFile->fixer !== null ? $phpcsFile->fixer->loops : 0; $internalKey = sprintf('%s-%s', $phpcsFile->getFilename(), $key); self::setIfNotCached($fixerLoops, $internalKey, $lazyValue); return self::$cache[$fixerLoops][$internalKey] ?? null; } private static function setIfNotCached(int $fixerLoops, string $internalKey, Closure $lazyValue): void { if (array_key_exists($fixerLoops, self::$cache) && array_key_exists($internalKey, self::$cache[$fixerLoops])) { return; } self::$cache[$fixerLoops][$internalKey] = $lazyValue(); if ($fixerLoops > 0) { unset(self::$cache[$fixerLoops - 1]); } } } PK41]KHcoding-standard/SlevomatCodingStandard/Helpers/TernaryOperatorHelper.phpnu[getTokens(); $pointer = $inlineThenPointer; do { $pointer = TokenHelper::findNext( $phpcsFile, [T_INLINE_ELSE, T_OPEN_PARENTHESIS, T_OPEN_SHORT_ARRAY, T_OPEN_SQUARE_BRACKET], $pointer + 1, ); if ($tokens[$pointer]['code'] === T_OPEN_PARENTHESIS) { $pointer = $tokens[$pointer]['parenthesis_closer']; continue; } if (in_array($tokens[$pointer]['code'], [T_OPEN_SHORT_ARRAY, T_OPEN_SQUARE_BRACKET], true)) { $pointer = $tokens[$pointer]['bracket_closer']; continue; } if (ScopeHelper::isInSameScope($phpcsFile, $inlineThenPointer, $pointer)) { break; } } while ($pointer !== null); return $pointer; } public static function getStartPointer(File $phpcsFile, int $inlineThenPointer): int { $tokens = $phpcsFile->getTokens(); $pointerBeforeCondition = $inlineThenPointer; do { $pointerBeforeCondition = TokenHelper::findPrevious( $phpcsFile, [T_EQUAL, T_DOUBLE_ARROW, T_COMMA, T_RETURN, T_THROW, T_CASE, T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO, T_OPEN_SQUARE_BRACKET, T_OPEN_SHORT_ARRAY, T_OPEN_PARENTHESIS], $pointerBeforeCondition - 1, ); if ( in_array($tokens[$pointerBeforeCondition]['code'], [T_OPEN_SQUARE_BRACKET, T_OPEN_SHORT_ARRAY], true) && $tokens[$pointerBeforeCondition]['bracket_closer'] < $inlineThenPointer ) { continue; } if ( $tokens[$pointerBeforeCondition]['code'] === T_OPEN_PARENTHESIS && $tokens[$pointerBeforeCondition]['parenthesis_closer'] < $inlineThenPointer ) { continue; } break; } while (true); return TokenHelper::findNextEffective($phpcsFile, $pointerBeforeCondition + 1); } public static function getEndPointer(File $phpcsFile, int $inlineThenPointer, int $inlineElsePointer): int { $tokens = $phpcsFile->getTokens(); $pointerAfterInlineElseEnd = $inlineElsePointer; do { $pointerAfterInlineElseEnd = TokenHelper::findNext( $phpcsFile, [T_SEMICOLON, T_COLON, T_COMMA, T_DOUBLE_ARROW, T_CLOSE_PARENTHESIS, T_CLOSE_SHORT_ARRAY, T_CLOSE_SQUARE_BRACKET, T_COALESCE], $pointerAfterInlineElseEnd + 1, ); if ($pointerAfterInlineElseEnd === null) { continue; } if ($tokens[$pointerAfterInlineElseEnd]['code'] === T_CLOSE_PARENTHESIS) { if ($tokens[$pointerAfterInlineElseEnd]['parenthesis_opener'] < $inlineThenPointer) { break; } } elseif (in_array($tokens[$pointerAfterInlineElseEnd]['code'], [T_CLOSE_SHORT_ARRAY, T_CLOSE_SQUARE_BRACKET], true)) { if ($tokens[$pointerAfterInlineElseEnd]['bracket_opener'] < $inlineThenPointer) { break; } } elseif ($tokens[$pointerAfterInlineElseEnd]['code'] === T_COMMA) { $previousPointer = TokenHelper::findPrevious( $phpcsFile, [T_OPEN_PARENTHESIS, T_OPEN_SHORT_ARRAY], $pointerAfterInlineElseEnd - 1, $inlineThenPointer, ); if ($previousPointer === null) { break; } if ( $tokens[$previousPointer]['code'] === T_OPEN_PARENTHESIS && $tokens[$previousPointer]['parenthesis_closer'] < $pointerAfterInlineElseEnd ) { break; } if ( $tokens[$previousPointer]['code'] === T_OPEN_SHORT_ARRAY && $tokens[$previousPointer]['bracket_closer'] < $pointerAfterInlineElseEnd ) { break; } } elseif ($tokens[$pointerAfterInlineElseEnd]['code'] === T_DOUBLE_ARROW) { $previousPointer = TokenHelper::findPrevious( $phpcsFile, T_OPEN_SHORT_ARRAY, $pointerAfterInlineElseEnd - 1, $inlineThenPointer, ); if ($previousPointer === null) { break; } } elseif (ScopeHelper::isInSameScope($phpcsFile, $inlineElsePointer, $pointerAfterInlineElseEnd)) { break; } } while ($pointerAfterInlineElseEnd !== null); if ($pointerAfterInlineElseEnd !== null) { return TokenHelper::findPreviousEffective($phpcsFile, $pointerAfterInlineElseEnd - 1); } return TokenHelper::findPreviousEffective($phpcsFile, count($tokens) - 1); } } PK41]U* ?coding-standard/SlevomatCodingStandard/Helpers/UseStatement.phpnu[nameAsReferencedInFile = $nameAsReferencedInFile; $this->normalizedNameAsReferencedInFile = self::normalizedNameAsReferencedInFile($type, $nameAsReferencedInFile); $this->fullyQualifiedTypeName = $fullyQualifiedClassName; $this->usePointer = $usePointer; $this->type = $type; $this->alias = $alias; } public function getNameAsReferencedInFile(): string { return $this->nameAsReferencedInFile; } public function getCanonicalNameAsReferencedInFile(): string { return $this->normalizedNameAsReferencedInFile; } public function getFullyQualifiedTypeName(): string { return $this->fullyQualifiedTypeName; } public function getPointer(): int { return $this->usePointer; } public function getType(): string { return $this->type; } public function getAlias(): ?string { return $this->alias; } public function isClass(): bool { return $this->type === self::TYPE_CLASS; } public function isConstant(): bool { return $this->type === self::TYPE_CONSTANT; } public function isFunction(): bool { return $this->type === self::TYPE_FUNCTION; } public function hasSameType(self $that): bool { return $this->type === $that->type; } public static function getUniqueId(string $type, string $name): string { $normalizedName = self::normalizedNameAsReferencedInFile($type, $name); if ($type === self::TYPE_CLASS) { return $normalizedName; } return sprintf('%s %s', $type, $normalizedName); } public static function normalizedNameAsReferencedInFile(string $type, string $name): string { if ($type === self::TYPE_CONSTANT) { return $name; } return strtolower($name); } public static function getTypeName(string $type): ?string { if ($type === self::TYPE_CONSTANT) { return 'const'; } if ($type === self::TYPE_FUNCTION) { return 'function'; } return null; } } PK41]<coding-standard/SlevomatCodingStandard/Helpers/Attribute.phpnu[attributePointer = $attributePointer; $this->name = $name; $this->fullyQualifiedName = $fullyQualifiedName; $this->startPointer = $startPointer; $this->endPointer = $endPointer; $this->content = $content; } public function getAttributePointer(): int { return $this->attributePointer; } public function getName(): string { return $this->name; } public function getFullyQualifiedName(): string { return $this->fullyQualifiedName; } public function getStartPointer(): int { return $this->startPointer; } public function getEndPointer(): int { return $this->endPointer; } public function getContent(): ?string { return $this->content; } } PK41]x1 =coding-standard/SlevomatCodingStandard/Helpers/TypeHelper.phpnu[filename = $filename; } public function getFilename(): string { return $this->filename; } } PK41]a?coding-standard/SlevomatCodingStandard/Helpers/StringHelper.phpnu[|int|string>> $leftSideTokens * @param array|int|string>> $rightSideTokens */ public static function fix(File $phpcsFile, array $leftSideTokens, array $rightSideTokens): void { $phpcsFile->fixer->beginChangeset(); self::replace($phpcsFile, $leftSideTokens, $rightSideTokens); self::replace($phpcsFile, $rightSideTokens, $leftSideTokens); $phpcsFile->fixer->endChangeset(); } /** * @param array|int|string>> $tokens * @return array|int|string>> */ public static function getLeftSideTokens(array $tokens, int $comparisonTokenPointer): array { $parenthesisDepth = 0; $shortArrayDepth = 0; $examinedTokenPointer = $comparisonTokenPointer; $sideTokens = []; $stopTokenCodes = self::getStopTokenCodes(); while (true) { $examinedTokenPointer--; $examinedToken = $tokens[$examinedTokenPointer]; /** @var string|int $examinedTokenCode */ $examinedTokenCode = $examinedToken['code']; if ($parenthesisDepth === 0 && $shortArrayDepth === 0 && isset($stopTokenCodes[$examinedTokenCode])) { break; } if ($examinedTokenCode === T_CLOSE_SHORT_ARRAY) { $shortArrayDepth++; } elseif ($examinedTokenCode === T_OPEN_SHORT_ARRAY) { if ($shortArrayDepth === 0) { break; } $shortArrayDepth--; } if ($examinedTokenCode === T_CLOSE_PARENTHESIS) { $parenthesisDepth++; } elseif ($examinedTokenCode === T_OPEN_PARENTHESIS) { if ($parenthesisDepth === 0) { break; } $parenthesisDepth--; } $sideTokens[$examinedTokenPointer] = $examinedToken; } return self::trimWhitespaceTokens(array_reverse($sideTokens, true)); } /** * @param array|int|string>> $tokens * @return array|int|string>> */ public static function getRightSideTokens(array $tokens, int $comparisonTokenPointer): array { $parenthesisDepth = 0; $shortArrayDepth = 0; $examinedTokenPointer = $comparisonTokenPointer; $sideTokens = []; $stopTokenCodes = self::getStopTokenCodes(); while (true) { $examinedTokenPointer++; $examinedToken = $tokens[$examinedTokenPointer]; /** @var string|int $examinedTokenCode */ $examinedTokenCode = $examinedToken['code']; if ($parenthesisDepth === 0 && $shortArrayDepth === 0 && isset($stopTokenCodes[$examinedTokenCode])) { break; } if ($examinedTokenCode === T_OPEN_SHORT_ARRAY) { $shortArrayDepth++; } elseif ($examinedTokenCode === T_CLOSE_SHORT_ARRAY) { if ($shortArrayDepth === 0) { break; } $shortArrayDepth--; } if ($examinedTokenCode === T_OPEN_PARENTHESIS) { $parenthesisDepth++; } elseif ($examinedTokenCode === T_CLOSE_PARENTHESIS) { if ($parenthesisDepth === 0) { break; } $parenthesisDepth--; } $sideTokens[$examinedTokenPointer] = $examinedToken; } return self::trimWhitespaceTokens($sideTokens); } /** * @param array|int|string>> $tokens * @param array|int|string>> $sideTokens */ public static function getDynamismForTokens(array $tokens, array $sideTokens): ?int { $sideTokens = array_values(array_filter($sideTokens, static fn (array $token): bool => !in_array( $token['code'], [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT, T_NS_SEPARATOR, T_PLUS, T_MINUS, T_INT_CAST, T_DOUBLE_CAST, T_STRING_CAST, T_ARRAY_CAST, T_OBJECT_CAST, T_BOOL_CAST, T_UNSET_CAST], true, ))); $sideTokensCount = count($sideTokens); $dynamism = self::getTokenDynamism(); if ($sideTokensCount > 0) { if ($sideTokens[0]['code'] === T_VARIABLE) { // Expression starts with a variable - wins over everything else return self::DYNAMISM_VARIABLE; } if ($sideTokens[$sideTokensCount - 1]['code'] === T_CLOSE_PARENTHESIS) { if (array_key_exists('parenthesis_owner', $sideTokens[$sideTokensCount - 1])) { /** @var int $parenthesisOwner */ $parenthesisOwner = $sideTokens[$sideTokensCount - 1]['parenthesis_owner']; if ($tokens[$parenthesisOwner]['code'] === T_ARRAY) { // Array return $dynamism[T_ARRAY]; } } // Function or method call return self::DYNAMISM_FUNCTION_CALL; } if ($sideTokensCount === 1 && $sideTokens[0]['code'] === T_STRING) { // Constant return self::DYNAMISM_CONSTANT; } } if ($sideTokensCount > 2 && $sideTokens[$sideTokensCount - 2]['code'] === T_DOUBLE_COLON) { if ($sideTokens[$sideTokensCount - 1]['code'] === T_VARIABLE) { // Static property access return self::DYNAMISM_VARIABLE; } if ($sideTokens[$sideTokensCount - 1]['code'] === T_STRING) { // Class constant return self::DYNAMISM_CONSTANT; } } if (array_key_exists(0, $sideTokens)) { $sideTokenCode = $sideTokens[0]['code']; /** @phpstan-ignore argument.type */ if (array_key_exists($sideTokenCode, $dynamism)) { return $dynamism[$sideTokenCode]; } } return null; } /** * @param array|int|string>> $tokens * @return array|int|string>> */ public static function trimWhitespaceTokens(array $tokens): array { foreach ($tokens as $pointer => $token) { if ($token['code'] !== T_WHITESPACE) { break; } unset($tokens[$pointer]); } foreach (array_reverse($tokens, true) as $pointer => $token) { if ($token['code'] !== T_WHITESPACE) { break; } unset($tokens[$pointer]); } return $tokens; } /** * @param array|int|string>> $oldTokens * @param array|int|string>> $newTokens */ private static function replace(File $phpcsFile, array $oldTokens, array $newTokens): void { reset($oldTokens); /** @var int $firstOldPointer */ $firstOldPointer = key($oldTokens); end($oldTokens); /** @var int $lastOldPointer */ $lastOldPointer = key($oldTokens); $content = implode('', array_map(static function (array $token): string { /** @var string $content */ $content = $token['content']; return $content; }, $newTokens)); FixerHelper::change($phpcsFile, $firstOldPointer, $lastOldPointer, $content); } /** * @return array */ private static function getTokenDynamism(): array { static $tokenDynamism; if ($tokenDynamism === null) { $tokenDynamism = [ T_TRUE => 0, T_FALSE => 0, T_NULL => 0, T_DNUMBER => 0, T_LNUMBER => 0, T_OPEN_SHORT_ARRAY => 0, // Do not stack error messages when the old-style array syntax is used T_ARRAY => 0, T_CONSTANT_ENCAPSED_STRING => 0, T_VARIABLE => self::DYNAMISM_VARIABLE, T_STRING => self::DYNAMISM_FUNCTION_CALL, ]; $tokenDynamism += array_fill_keys(array_keys(Tokens::$castTokens), 3); } return $tokenDynamism; } /** * @return array */ private static function getStopTokenCodes(): array { static $stopTokenCodes; if ($stopTokenCodes === null) { $stopTokenCodes = [ T_BOOLEAN_AND => true, T_BOOLEAN_OR => true, T_SEMICOLON => true, T_OPEN_TAG => true, T_INLINE_THEN => true, T_INLINE_ELSE => true, T_LOGICAL_AND => true, T_LOGICAL_OR => true, T_LOGICAL_XOR => true, T_COALESCE => true, T_CASE => true, T_COLON => true, T_RETURN => true, T_COMMA => true, T_MATCH_ARROW => true, T_FN_ARROW => true, ]; $stopTokenCodes += array_fill_keys(array_keys(Tokens::$assignmentTokens), true); $stopTokenCodes += array_fill_keys(array_keys(Tokens::$commentTokens), true); } return $stopTokenCodes; } } PK41]M  Acoding-standard/SlevomatCodingStandard/Helpers/SuppressHelper.phpnu[> $annotations */ $annotations = AnnotationHelper::getAnnotations($phpcsFile, $pointer, self::ANNOTATION); return array_reduce( $annotations, static function (bool $carry, Annotation $annotation) use ($suppressName): bool { $annotationSuppressName = explode(' ', $annotation->getValue()->value)[0]; if ( $suppressName === $annotationSuppressName || strpos($suppressName, sprintf('%s.', $annotationSuppressName)) === 0 ) { $carry = true; } return $carry; }, false, ); } public static function removeSuppressAnnotation(File $phpcsFile, int $pointer, string $suppressName): void { $suppressAnnotation = null; /** @var Annotation $annotation */ foreach (AnnotationHelper::getAnnotations($phpcsFile, $pointer, self::ANNOTATION) as $annotation) { if ($annotation->getValue()->value === $suppressName) { $suppressAnnotation = $annotation; break; } } assert($suppressAnnotation !== null); $tokens = $phpcsFile->getTokens(); /** @var int $pointerBefore */ $pointerBefore = TokenHelper::findPrevious( $phpcsFile, [T_DOC_COMMENT_OPEN_TAG, T_DOC_COMMENT_STAR], $suppressAnnotation->getStartPointer() - 1, ); $changeStart = $tokens[$pointerBefore]['code'] === T_DOC_COMMENT_STAR ? $pointerBefore : $suppressAnnotation->getStartPointer(); /** @var int $changeEnd */ $changeEnd = TokenHelper::findNext( $phpcsFile, [T_DOC_COMMENT_CLOSE_TAG, T_DOC_COMMENT_STAR], $suppressAnnotation->getEndPointer() + 1, ) - 1; $phpcsFile->fixer->beginChangeset(); FixerHelper::removeBetweenIncluding($phpcsFile, $changeStart, $changeEnd); $phpcsFile->fixer->endChangeset(); } } PK41]G ;coding-standard/SlevomatCodingStandard/Helpers/TypeHint.phpnu[typeHint = $typeHint; $this->nullable = $nullable; $this->startPointer = $startPointer; $this->endPointer = $endPointer; } public function getTypeHint(): string { return $this->typeHint; } public function getTypeHintWithoutNullabilitySymbol(): string { return strpos($this->typeHint, '?') === 0 ? substr($this->typeHint, 1) : $this->typeHint; } public function isNullable(): bool { return $this->nullable; } public function getStartPointer(): int { return $this->startPointer; } public function getEndPointer(): int { return $this->endPointer; } } PK41]o  >coding-standard/SlevomatCodingStandard/Helpers/ClassHelper.phpnu[getTokens(); $classPointers = array_reverse(self::getAllClassPointers($phpcsFile)); foreach ($classPointers as $classPointer) { if ($tokens[$classPointer]['scope_opener'] < $pointer && $tokens[$classPointer]['scope_closer'] > $pointer) { return $classPointer; } } return null; } public static function isFinal(File $phpcsFile, int $classPointer): bool { return $phpcsFile->getTokens()[TokenHelper::findPreviousEffective($phpcsFile, $classPointer - 1)]['code'] === T_FINAL; } public static function getFullyQualifiedName(File $phpcsFile, int $classPointer): string { $className = self::getName($phpcsFile, $classPointer); $tokens = $phpcsFile->getTokens(); if ($tokens[$classPointer]['code'] === T_ANON_CLASS) { return $className; } $name = sprintf('%s%s', NamespaceHelper::NAMESPACE_SEPARATOR, $className); $namespace = NamespaceHelper::findCurrentNamespaceName($phpcsFile, $classPointer); return $namespace !== null ? sprintf('%s%s%s', NamespaceHelper::NAMESPACE_SEPARATOR, $namespace, $name) : $name; } public static function getName(File $phpcsFile, int $classPointer): string { $tokens = $phpcsFile->getTokens(); if ($tokens[$classPointer]['code'] === T_ANON_CLASS) { return 'class@anonymous'; } return $tokens[TokenHelper::findNext($phpcsFile, T_STRING, $classPointer + 1, $tokens[$classPointer]['scope_opener'])]['content']; } /** * @return array */ public static function getAllNames(File $phpcsFile): array { $tokens = $phpcsFile->getTokens(); $names = []; /** @var int $classPointer */ foreach (self::getAllClassPointers($phpcsFile) as $classPointer) { if ($tokens[$classPointer]['code'] === T_ANON_CLASS) { continue; } $names[$classPointer] = self::getName($phpcsFile, $classPointer); } return $names; } /** * @return list */ public static function getTraitUsePointers(File $phpcsFile, int $classPointer): array { $useStatements = []; $tokens = $phpcsFile->getTokens(); $scopeLevel = $tokens[$classPointer]['level'] + 1; for ($i = $tokens[$classPointer]['scope_opener'] + 1; $i < $tokens[$classPointer]['scope_closer']; $i++) { if ($tokens[$i]['code'] !== T_USE) { continue; } if ($tokens[$i]['level'] !== $scopeLevel) { continue; } $useStatements[] = $i; } return $useStatements; } /** * @return list */ private static function getAllClassPointers(File $phpcsFile): array { $lazyValue = static fn (): array => TokenHelper::findNextAll( $phpcsFile, TokenHelper::CLASS_TYPE_WITH_ANONYMOUS_CLASS_TOKEN_CODES, 0, ); return SniffLocalCache::getAndSetIfNotCached($phpcsFile, 'classPointers', $lazyValue); } } PK41]i>coding-standard/SlevomatCodingStandard/Helpers/CatchHelper.phpnu[getTokens(); $endPointer = $tokens[$catchPointer]['scope_closer']; do { $nextPointer = TokenHelper::findNextEffective($phpcsFile, $endPointer + 1); if ($nextPointer === null || !in_array($tokens[$nextPointer]['code'], [T_CATCH, T_FINALLY], true)) { break; } $endPointer = $tokens[$nextPointer]['scope_closer']; } while (true); return $endPointer; } /** * @param array|int|string> $catchToken * @return list */ public static function findCaughtTypesInCatch(File $phpcsFile, array $catchToken): array { /** @var int $catchParenthesisOpenerPointer */ $catchParenthesisOpenerPointer = $catchToken['parenthesis_opener']; /** @var int $catchParenthesisCloserPointer */ $catchParenthesisCloserPointer = $catchToken['parenthesis_closer']; $nameEndPointer = $catchParenthesisOpenerPointer; $tokens = $phpcsFile->getTokens(); $caughtTypes = []; do { $nameStartPointer = TokenHelper::findNext( $phpcsFile, [T_BITWISE_OR, ...TokenHelper::NAME_TOKEN_CODES], $nameEndPointer + 1, $catchParenthesisCloserPointer, ); if ($nameStartPointer === null) { break; } if ($tokens[$nameStartPointer]['code'] === T_BITWISE_OR) { /** @var int $nameStartPointer */ $nameStartPointer = TokenHelper::findNextEffective($phpcsFile, $nameStartPointer + 1, $catchParenthesisCloserPointer); } $pointerAfterNameEndPointer = TokenHelper::findNextExcluding($phpcsFile, TokenHelper::NAME_TOKEN_CODES, $nameStartPointer + 1); $nameEndPointer = $pointerAfterNameEndPointer === null ? $nameStartPointer : $pointerAfterNameEndPointer - 1; $caughtTypes[] = NamespaceHelper::resolveClassName( $phpcsFile, TokenHelper::getContent($phpcsFile, $nameStartPointer, $nameEndPointer), $catchParenthesisOpenerPointer, ); } while (true); return $caughtTypes; } } PK41]#}}Bcoding-standard/SlevomatCodingStandard/Helpers/ParameterHelper.phpnu[getTokens(); if (!array_key_exists('nested_parenthesis', $tokens[$variablePointer])) { return false; } $parenthesisOpenerPointer = array_reverse(array_keys($tokens[$variablePointer]['nested_parenthesis']))[0]; if (!array_key_exists('parenthesis_owner', $tokens[$parenthesisOpenerPointer])) { return false; } $parenthesisOwnerPointer = $tokens[$parenthesisOpenerPointer]['parenthesis_owner']; return in_array($tokens[$parenthesisOwnerPointer]['code'], TokenHelper::FUNCTION_TOKEN_CODES, true); } } PK41]wee@coding-standard/SlevomatCodingStandard/Helpers/ArrayKeyValue.phpnu[pointerStart = $pointerStart; $this->pointerEnd = $pointerEnd; $this->addValues($phpcsFile); } public function getContent(File $phpcsFile, bool $normalize = false, ?string $indent = null): string { if ($normalize === false) { return TokenHelper::getContent($phpcsFile, $this->pointerStart, $this->pointerEnd); } $content = ''; $addCommaPtr = $this->pointerComma === null ? TokenHelper::findPreviousEffective($phpcsFile, $this->pointerEnd) : null; $tokens = $phpcsFile->getTokens(); for ($pointer = $this->pointerStart; $pointer <= $this->pointerEnd; $pointer++) { $token = $tokens[$pointer]; $content .= $token['content']; if ($pointer === $addCommaPtr) { $content .= ','; } } // Trim, but keep leading empty lines $content = ltrim($content, " \t"); $content = rtrim($content); if ($indent !== null && strpos($content, $phpcsFile->eolChar) !== 0) { $content = $indent . $content; } return $content; } public function getIndent(): ?string { return $this->indent; } public function getKey(): ?string { return $this->key; } public function getPointerArrow(): ?int { return $this->pointerArrow; } public function getPointerComma(): ?int { return $this->pointerComma; } public function getPointerEnd(): int { return $this->pointerEnd; } public function getPointerStart(): int { return $this->pointerStart; } public function isUnpacking(): bool { return $this->unpacking; } private function addValues(File $phpcsFile): void { $key = ''; $tokens = $phpcsFile->getTokens(); $firstNonWhitespace = null; for ($i = $this->pointerStart; $i <= $this->pointerEnd; $i++) { $token = $tokens[$i]; if (in_array($token['code'], TokenHelper::ARRAY_TOKEN_CODES, true)) { $i = ArrayHelper::openClosePointers($token)[1]; continue; } if ($token['code'] === T_DOUBLE_ARROW) { if (current(array_reverse($token['conditions'])) === T_CLOSURE) { continue; } $this->pointerArrow = $i; continue; } if ($token['code'] === T_COMMA) { $this->pointerComma = $i; continue; } if ($token['code'] === T_ELLIPSIS) { $this->unpacking = true; continue; } if ($this->pointerArrow !== null) { continue; } if ($firstNonWhitespace === null && $token['code'] !== T_WHITESPACE) { $firstNonWhitespace = $i; } if (in_array($token['code'], TokenHelper::INLINE_COMMENT_TOKEN_CODES, true) === false) { $key .= $token['content']; } } $haveIndent = $firstNonWhitespace !== null && TokenHelper::findFirstNonWhitespaceOnLine( $phpcsFile, $firstNonWhitespace, ) === $firstNonWhitespace; $this->indent = $haveIndent ? TokenHelper::getContent( $phpcsFile, TokenHelper::findFirstTokenOnLine($phpcsFile, $firstNonWhitespace), $firstNonWhitespace - 1, ) : null; $this->key = $this->pointerArrow !== null ? trim($key) : null; } } PK41]*KL\ \ Dcoding-standard/SlevomatCodingStandard/Helpers/IndentationHelper.phpnu[config->tabWidth !== 0 ? $phpcsFile->config->tabWidth : self::DEFAULT_INDENTATION_WIDTH); } /** * @param list $codePointers */ public static function removeIndentation(File $phpcsFile, array $codePointers, string $defaultIndentation): string { $tokens = $phpcsFile->getTokens(); $eolLength = strlen($phpcsFile->eolChar); $code = ''; $inHeredoc = false; $indentation = self::getOneIndentationLevel($phpcsFile); $indentationLength = strlen($indentation); foreach ($codePointers as $no => $codePointer) { $content = $tokens[$codePointer]['content']; if ( !$inHeredoc && ( $no === 0 || substr($tokens[$codePointer - 1]['content'], -$eolLength) === $phpcsFile->eolChar ) ) { if ($content === $phpcsFile->eolChar) { // Nothing } elseif (substr($content, 0, $indentationLength) === $indentation) { $content = substr($content, $indentationLength); } else { $content = $defaultIndentation . ltrim($content); } } if (in_array($tokens[$codePointer]['code'], [T_START_HEREDOC, T_START_NOWDOC], true)) { $inHeredoc = true; } elseif (in_array($tokens[$codePointer]['code'], [T_END_HEREDOC, T_END_NOWDOC], true)) { $inHeredoc = false; } $code .= $content; } return rtrim($code); } public static function convertSpacesToTabs(File $phpcsFile, string $code): string { // @codeCoverageIgnoreStart if ($phpcsFile->config->tabWidth === 0) { return $code; } // @codeCoverageIgnoreEnd return preg_replace_callback('~^([ ]+)~m', static function (array $matches) use ($phpcsFile): string { $tabsCount = (int) floor(strlen($matches[1]) / $phpcsFile->config->tabWidth); $spacesCountToRemove = $tabsCount * $phpcsFile->config->tabWidth; return str_repeat("\t", $tabsCount) . substr($matches[1], $spacesCountToRemove); }, $code); } } PK41]=!(  Fcoding-standard/SlevomatCodingStandard/Helpers/SniffSettingsHelper.phpnu[ $settings * @return list */ public static function normalizeArray(array $settings): array { $settings = array_map(static fn (string $value): string => trim($value), $settings); $settings = array_filter($settings, static fn (string $value): bool => $value !== ''); return array_values($settings); } /** * @param array $settings * @return array */ public static function normalizeAssociativeArray(array $settings): array { $normalizedSettings = []; foreach ($settings as $key => $value) { if (is_string($key)) { $key = trim($key); } if (is_string($value)) { $value = trim($value); } if ($key === '' || $value === '') { continue; } $normalizedSettings[$key] = $value; } return $normalizedSettings; } public static function isValidRegularExpression(string $expression): bool { return preg_match('~^(?:\(.*\)|\{.*\}|\[.*\])[a-z]*\z~i', $expression) !== 0 || preg_match('~^([^a-z\s\\\\]).*\\1[a-z]*\z~i', $expression) !== 0; } public static function isEnabledByPhpVersion(?bool $value, int $phpVersionLimit): bool { if ($value !== null) { return $value; } $phpVersion = Config::getConfigData('php_version') !== null ? (int) Config::getConfigData('php_version') : PHP_VERSION_ID; return $phpVersion >= $phpVersionLimit; } } PK41]aoo=coding-standard/SlevomatCodingStandard/Helpers/Annotation.phpnu[node = $node; $this->startPointer = $startPointer; $this->endPointer = $endPointer; } public function getNode(): PhpDocTagNode { return $this->node; } public function getName(): string { return $this->node->name; } /** * @return T */ public function getValue(): PhpDocTagValueNode { /** @phpstan-ignore-next-line */ return $this->node->value; } public function getStartPointer(): int { return $this->startPointer; } public function getEndPointer(): int { return $this->endPointer; } public function isInvalid(): bool { return $this->node->value instanceof InvalidTagValueNode; } } PK41].=>coding-standard/SlevomatCodingStandard/Helpers/FixerHelper.phpnu[fixer->addContent($pointer, IndentationHelper::convertSpacesToTabs($phpcsFile, $content)); } public static function addBefore(File $phpcsFile, int $pointer, string $content): void { $phpcsFile->fixer->addContentBefore($pointer, IndentationHelper::convertSpacesToTabs($phpcsFile, $content)); } public static function replace(File $phpcsFile, int $pointer, string $content): void { $phpcsFile->fixer->replaceToken($pointer, IndentationHelper::convertSpacesToTabs($phpcsFile, $content)); } public static function change(File $phpcsFile, int $startPointer, int $endPointer, string $content): void { self::removeBetweenIncluding($phpcsFile, $startPointer, $endPointer); self::replace($phpcsFile, $startPointer, $content); } public static function removeBetween(File $phpcsFile, int $startPointer, int $endPointer): void { self::removeBetweenIncluding($phpcsFile, $startPointer + 1, $endPointer - 1); } public static function removeBetweenIncluding(File $phpcsFile, int $startPointer, int $endPointer): void { for ($i = $startPointer; $i <= $endPointer; $i++) { $phpcsFile->fixer->replaceToken($i, ''); } } public static function removeWhitespaceBefore(File $phpcsFile, int $pointer): void { for ($i = $pointer - 1; $i > 0; $i--) { if (preg_match('~^\\s+$~', $phpcsFile->fixer->getTokenContent($i)) === 0) { break; } $phpcsFile->fixer->replaceToken($i, ''); } } public static function removeWhitespaceAfter(File $phpcsFile, int $pointer): void { for ($i = $pointer + 1; $i < count($phpcsFile->getTokens()); $i++) { if (preg_match('~^\\s+$~', $phpcsFile->fixer->getTokenContent($i)) === 0) { break; } self::replace($phpcsFile, $i, ''); } } } PK41]==coding-standard/LICENSE.mdnu[The MIT License (MIT) Copyright (c) 2015 Slevomat.cz, s.r.o. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK41]C{J7J7coding-standard/doc/classes.mdnu[## Classes #### SlevomatCodingStandard.Classes.BackedEnumTypeSpacing 🔧 * Checks number of spaces before `:` and before type. Sniff provides the following settings: * `spacesCountBeforeColon`: the number of spaces before `:`. * `spacesCountBeforeType`: the number of spaces before type. #### SlevomatCodingStandard.Classes.ClassConstantVisibility 🔧 In PHP 7.1+ it's possible to declare [visibility of class constants](https://wiki.php.net/rfc/class_const_visibility). In a similar vein to optional declaration of visibility for properties and methods which is actually required in sane coding standards, this sniff also requires declaring visibility for all class constants. Sniff provides the following settings: * `fixable`: the sniff is not fixable by default because we think it's better to decide about each constant one by one, however you can enable fixability with this option. ```php const FOO = 1; // visibility missing! public const BAR = 2; // correct ``` #### SlevomatCodingStandard.Classes.ClassLength Disallows long classes. This sniff provides the following settings: * `includeComments` (default: `false`): should comments be included in the count. * `includeWhitespace` (default: `false`): should empty lines be included in the count. * `maxLinesLength` (default: `250`): specifies max allowed function lines length. #### SlevomatCodingStandard.Classes.ClassMemberSpacing 🔧 Sniff checks lines count between different class members, e.g. between last property and first method. Sniff provides the following settings: * `linesCountBetweenMembers`: lines count between different class members #### SlevomatCodingStandard.Classes.ClassStructure 🔧 Checks that class/trait/interface members are in the correct order. Sniff provides the following settings: * `groups`: order of groups. Use multiple groups in one `` to not differentiate among them. You can use specific groups or shortcuts. * `methodGroups`: custom method groups. Define a custom group for special methods based on their name, annotation, or attribute. **List of supported groups**: uses, enum cases, public constants, protected constants, private constants, public properties, public static properties, protected properties, protected static properties, private properties, private static properties, constructor, static constructors, destructor, magic methods, invoke method, public methods, protected methods, private methods, public final methods, public static final methods, protected final methods, protected static final methods, public abstract methods, public static abstract methods, protected abstract methods, protected static abstract methods, public static methods, protected static methods, private static methods **List of supported shortcuts**: constants, properties, static properties, methods, all public methods, all protected methods, all private methods, static methods, final methods, abstract methods ```xml ``` #### SlevomatCodingStandard.Classes.ConstantSpacing 🔧 Checks that there is a certain number of blank lines between constants. Sniff provides the following settings: * `minLinesCountBeforeWithComment`: minimum number of lines before constant with a documentation comment or attribute * `maxLinesCountBeforeWithComment`: maximum number of lines before constant with a documentation comment or attribute * `minLinesCountBeforeWithoutComment`: minimum number of lines before constant without a documentation comment or attribute * `maxLinesCountBeforeWithoutComment`: maximum number of lines before constant without a documentation comment or attribute * `minLinesCountBeforeMultiline` (default: `null`): minimum number of lines before multiline constant * `maxLinesCountBeforeMultiline` (default: `null`): maximum number of lines before multiline constant #### SlevomatCodingStandard.Classes.DisallowConstructorPropertyPromotion Disallows usage of constructor property promotion. #### SlevomatCodingStandard.Classes.DisallowLateStaticBindingForConstants 🔧 Disallows late static binding for constants. #### SlevomatCodingStandard.Classes.DisallowMultiConstantDefinition 🔧 Disallows multi constant definition. #### SlevomatCodingStandard.Classes.DisallowMultiPropertyDefinition 🔧 Disallows multi property definition. #### SlevomatCodingStandard.Classes.DisallowStringExpressionPropertyFetch 🔧 Disallows string expression property fetch `$object->{'foo'}` when the property name is compatible with identifier access. #### SlevomatCodingStandard.Classes.EmptyLinesAroundClassBraces 🔧 Enforces one configurable number of lines after opening class/interface/trait brace and one empty line before the closing brace. Sniff provides the following settings: * `linesCountAfterOpeningBrace`: allows to configure the number of lines after opening brace. * `linesCountBeforeClosingBrace`: allows to configure the number of lines before closing brace. #### SlevomatCodingStandard.Classes.EnumCaseSpacing 🔧 Checks that there is a certain number of blank lines between enum cases. Sniff provides the following settings: * `minLinesCountBeforeWithComment`: minimum number of lines before enum case with a documentation comment or attribute * `maxLinesCountBeforeWithComment`: maximum number of lines before enum case with a documentation comment or attribute * `minLinesCountBeforeWithoutComment`: minimum number of lines before enum case without a documentation comment or attribute * `maxLinesCountBeforeWithoutComment`: maximum number of lines before enum case without a documentation comment or attribute #### SlevomatCodingStandard.Classes.ForbiddenPublicProperty Disallows using public properties. This sniff provides the following setting: * `checkPromoted` (default: `false`): will check promoted properties too. * `allowReadonly` (default: `false`): will allow readonly properties. * `allowNonPublicSet` (default: `true`): will allow properties with `protected(set)` or `private(set)`. #### SlevomatCodingStandard.Classes.MethodSpacing 🔧 Checks that there is a certain number of blank lines between methods. Sniff provides the following settings: * `minLinesCount`: minimum number of blank lines * `maxLinesCount`: maximum number of blank lines #### SlevomatCodingStandard.Classes.ModernClassNameReference 🔧 Reports use of `__CLASS__`, `get_parent_class()`, `get_called_class()`, `get_class()` and `get_class($this)`. Class names should be referenced via `::class` constant when possible. Sniff provides the following settings: * `enableOnObjects`: Enable `::class` on all objects. It's on by default if you're on PHP 8.0+ #### SlevomatCodingStandard.Classes.ParentCallSpacing 🔧 Enforces configurable number of lines around parent method call. Sniff provides the following settings: * `linesCountBefore`: allows to configure the number of lines before parent call. * `linesCountBeforeFirst`: allows to configure the number of lines before first parent call. * `linesCountAfter`: allows to configure the number of lines after parent call. * `linesCountAfterLast`: allows to configure the number of lines after last parent call. #### SlevomatCodingStandard.Classes.PropertyDeclaration 🔧 * Checks that there's a single space between a typehint and a property name: `Foo $foo` * Checks that there's no whitespace between a nullability symbol and a typehint: `?Foo` * Checks that there's a single space before nullability symbol or a typehint: `private ?Foo` or `private Foo` * Checks order of modifiers Sniff provides the following settings: * `modifiersOrder`: allows to configure order of modifiers. * `checkPromoted`: will check promoted properties too. * `enableMultipleSpacesBetweenModifiersCheck`: checks multiple spaces between modifiers. #### SlevomatCodingStandard.Classes.PropertySpacing 🔧 Checks that there is a certain number of blank lines between properties. Sniff provides the following settings: * `minLinesCountBeforeWithComment`: minimum number of lines before property with a documentation comment or attribute * `maxLinesCountBeforeWithComment`: maximum number of lines before property with a documentation comment or attribute * `minLinesCountBeforeWithoutComment`: minimum number of lines before property without a documentation comment or attribute * `maxLinesCountBeforeWithoutComment`: maximum number of lines before property without a documentation comment or attribute * `minLinesCountBeforeMultiline` (default: `null`): minimum number of lines before multiline property * `maxLinesCountBeforeMultiline` (default: `null`): maximum number of lines before multiline property #### SlevomatCodingStandard.Classes.RequireAbstractOrFinal 🔧 Requires the class to be declared either as abstract or as final. #### SlevomatCodingStandard.Classes.RequireConstructorPropertyPromotion 🔧 Requires use of constructor property promotion. This sniff provides the following setting: * `enable`: either to enable or not this sniff. By default, it is enabled for PHP versions 8.0 or higher. #### SlevomatCodingStandard.Classes.RequireMultiLineMethodSignature 🔧 Enforces method signature to be split to more lines so each parameter is on its own line. Sniff provides the following settings: * `minLineLength`: specifies min line length to enforce signature to be split. Use 0 value to enforce for all methods, regardless of length. * `minParametersCount`: specifies min parameters count to enforce signature to be split. * `includedMethodPatterns`: allows to configure which methods are included in sniff detection. This is an array of regular expressions (PCRE) with delimiters. You should not use this with `excludedMethodPatterns`, as it will not work properly. * `excludedMethodPatterns`: allows to configure which methods are excluded from sniff detection. This is an array of regular expressions (PCRE) with delimiters. You should not use this with `includedMethodPatterns`, as it will not work properly. * `withPromotedProperties`: always require multiline signatures for methods with promoted properties. #### SlevomatCodingStandard.Classes.RequireSelfReference 🔧 Requires `self` for local reference. #### SlevomatCodingStandard.Classes.RequireSingleLineMethodSignature 🔧 Enforces method signature to be on a single line. Sniff provides the following settings: * `maxLineLength`: specifies max allowed line length. If signature fit on it, it's enforced. Use 0 value to enforce for all methods, regardless of length. * `includedMethodPatterns`: allows to configure which methods are included in sniff detection. This is an array of regular expressions (PCRE) with delimiters. You should not use this with `excludedMethodPatterns`, as it will not work properly. * `excludedMethodPatterns`: allows to configure which methods are excluded from sniff detection. This is an array of regular expressions (PCRE) with delimiters. You should not use this with `includedMethodPatterns`, as it will not work properly. #### SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming Reports use of superfluous prefix or suffix "Abstract" for abstract classes. #### SlevomatCodingStandard.Classes.SuperfluousInterfaceNaming Reports use of superfluous prefix or suffix "Interface" for interfaces. #### SlevomatCodingStandard.Classes.SuperfluousExceptionNaming Reports use of superfluous suffix "Exception" for exceptions. #### SlevomatCodingStandard.Classes.SuperfluousErrorNaming Reports use of superfluous suffix "Error" for errors. #### SlevomatCodingStandard.Classes.SuperfluousTraitNaming Reports use of superfluous suffix "Trait" for traits. #### SlevomatCodingStandard.Classes.TraitUseDeclaration 🔧 Prohibits multiple traits separated by commas in one `use` statement. #### SlevomatCodingStandard.Classes.TraitUseSpacing 🔧 Enforces configurable number of lines before first `use`, after last `use` and between two `use` statements. Sniff provides the following settings: * `linesCountBeforeFirstUse`: allows to configure the number of lines before first `use`. * `linesCountBeforeFirstUseWhenFirstInClass`: allows to configure the number of lines before first `use` when the `use` is the first statement in the class. * `linesCountBetweenUses`: allows to configure the number of lines between two `use` statements. * `linesCountAfterLastUse`: allows to configure the number of lines after last `use`. * `linesCountAfterLastUseWhenLastInClass`: allows to configure the number of lines after last `use` when the `use` is the last statement in the class. #### SlevomatCodingStandard.Classes.UselessLateStaticBinding 🔧 Reports useless late static binding. PK41]!coding-standard/doc/attributes.mdnu[## Attributes #### SlevomatCodingStandard.Attributes.AttributeAndTargetSpacing 🔧 Sniff checks lines count between attribute and its target (or target's documentation comment). Sniff provides the following settings: * `allowOnSameLine` (default: `false`): allow attribute and its target to be placed on the same line * `linesCount`: lines count between attribute and its target #### SlevomatCodingStandard.Attributes.AttributesOrder 🔧 Requires order of attributes. When more attributes are in one `#[]`, e.g. `#[One, Two]`, the first attribute name is used to resolve the order. Sniff provides the following settings: * `order`: required order of attributes. Supports prefixes, eg. `ORM\`, and mask , eg. `AppAssert*`. * `orderAlphabetically`: order attributes alphabetically. Boolean value, default `false`. Only one order can be set. ```xml ``` #### SlevomatCodingStandard.Attributes.DisallowAttributesJoining 🔧 Requires that only one attribute can be placed inside `#[]` (no comma-separated list). In case of more attributes applied, they are split into individual `#[]` blocks. #### SlevomatCodingStandard.Attributes.DisallowMultipleAttributesPerLine 🔧 Disallows multiple attributes of some target on same line. This sniff treats multiple attributes declared inside one `#[]` as a single attribute. See `DisallowAttributesJoining` to modify this behavior. #### SlevomatCodingStandard.Attributes.RequireAttributeAfterDocComment 🔧 Requires that attributes are always after documentation comment. PK41]L%L%)coding-standard/doc/control-structures.mdnu[## Control structures #### SlevomatCodingStandard.ControlStructures.AssignmentInCondition Disallows assignments in `if`, `elseif` and `do-while` loop conditions: ```php if ($file = findFile($path)) { } ``` Assignment in `while` loop condition is specifically allowed because it's commonly used. This is a great addition to already existing `SlevomatCodingStandard.ControlStructures.DisallowYodaComparison` because it prevents the danger of assigning something by mistake instead of using a comparison operator like `===`. Sniff provides the following settings: * `ignoreAssignmentsInsideFunctionCalls`: ignores assignment inside function calls, like this: ```php if (in_array(1, $haystack, $strict = true)) { } ``` #### SlevomatCodingStandard.ControlStructures.BlockControlStructureSpacing 🔧 Enforces configurable number of lines around block control structures (if, foreach, ...). Sniff provides the following settings: * `linesCountBefore`: allows to configure the number of lines before control structure. * `linesCountBeforeFirst`: allows to configure the number of lines before first control structure. * `linesCountAfter`: allows to configure the number of lines after control structure. * `linesCountAfterLast`: allows to configure the number of lines after last control structure. * `controlStructures`: allows to narrow the list of checked control structures. For example, with the following setting, only `if` and `switch` keywords are checked. ```xml ``` #### SlevomatCodingStandard.ControlStructures.EarlyExit 🔧 Requires use of early exit. Sniff provides the following settings: * `ignoreStandaloneIfInScope`: ignores `if` that is standalone in scope, like this: ```php foreach ($values as $value) { if ($value) { doSomething(); } } ``` * `ignoreOneLineTrailingIf`: ignores `if` that has one line content and is on the last position in scope, like this: ```php foreach ($values as $value) { $value .= 'whatever'; if ($value) { doSomething(); } } ``` * `ignoreTrailingIfWithOneInstruction`: ignores `if` that has only one instruction and is on the last position in scope, like this: ```php foreach ($values as $value) { $value .= 'whatever'; if ($value) { doSomething(function () { // Anything }); } } ``` #### SlevomatCodingStandard.ControlStructures.DisallowContinueWithoutIntegerOperandInSwitch 🔧 Disallows use of `continue` without integer operand in `switch` because it emits a warning in PHP 7.3 and higher. #### SlevomatCodingStandard.ControlStructures.DisallowEmpty Disallows use of `empty()`. #### SlevomatCodingStandard.ControlStructures.DisallowNullSafeObjectOperator Disallows using `?->` operator. #### SlevomatCodingStandard.ControlStructures.DisallowShortTernaryOperator 🔧 Disallows short ternary operator `?:`. Sniff provides the following settings: * `fixable`: the sniff is fixable by default, however in strict code it makes sense to forbid this weakly typed form of ternary altogether, you can disable fixability with this option. #### SlevomatCodingStandard.ControlStructures.DisallowTrailingMultiLineTernaryOperator 🔧 Ternary operator has to be reformatted when the operator is not leading the line. ```php # wrong $t = $someCondition ? $thenThis : $otherwiseThis; # correct $t = $someCondition ? $thenThis : $otherwiseThis; ``` #### SlevomatCodingStandard.ControlStructures.JumpStatementsSpacing 🔧 Enforces configurable number of lines around jump statements (continue, return, ...). Sniff provides the following settings: * `allowSingleLineYieldStacking`: whether or not to allow multiple yield/yield from statements in a row without blank lines. * `linesCountBefore`: allows to configure the number of lines before jump statement. * `linesCountBeforeFirst`: allows to configure the number of lines before first jump statement. * `linesCountBeforeWhenFirstInCaseOrDefault`: allows to configure the number of lines before jump statement that is first in `case` or `default` * `linesCountAfter`: allows to configure the number of lines after jump statement. * `linesCountAfterLast`: allows to configure the number of lines after last jump statement. * `linesCountAfterWhenLastInCaseOrDefault`: allows to configure the number of lines after jump statement that is last in `case` or `default` * `linesCountAfterWhenLastInLastCaseOrDefault`: allows to configure the number of lines after jump statement that is last in last `case` or `default` * `jumpStatements`: allows to narrow the list of checked jump statements. For example, with the following setting, only `continue` and `break` keywords are checked. ```xml ``` #### SlevomatCodingStandard.ControlStructures.LanguageConstructWithParentheses 🔧 `LanguageConstructWithParenthesesSniff` checks and fixes language construct used with parentheses. #### SlevomatCodingStandard.ControlStructures.NewWithParentheses 🔧 Requires `new` with parentheses. #### SlevomatCodingStandard.ControlStructures.NewWithoutParentheses 🔧 Reports `new` with useless parentheses. #### SlevomatCodingStandard.ControlStructures.RequireMultiLineCondition 🔧 Enforces conditions of `if`, `elseif`, `while` and `do-while` with one or more boolean operators to be split to more lines so each condition part is on its own line. Sniff provides the following settings: * `minLineLength`: specifies minimum line length to enforce condition to be split. Use 0 value to enforce for all conditions, regardless of length. * `booleanOperatorOnPreviousLine`: boolean operator is placed at the end of previous line when fixing. * `alwaysSplitAllConditionParts`: require all condition parts to be on its own line - it reports error even if condition is already multi-line but there are some condition parts on the same line. #### SlevomatCodingStandard.ControlStructures.RequireMultiLineTernaryOperator 🔧 Ternary operator has to be reformatted to more lines when the line length exceeds the given limit. Sniff provides the following settings: * `lineLengthLimit` (default: `0`) * `minExpressionsLength` (default: `null`): when the expressions after `?` are shorter than this length, the ternary operator does not have to be reformatted. #### SlevomatCodingStandard.ControlStructures.RequireNullCoalesceEqualOperator 🔧 Requires use of null coalesce equal operator when possible. This sniff provides the following setting: * `enable`: either to enable or not this sniff. By default, it is enabled for PHP versions 7.4 or higher. * `checkIfConditions` (default: `false`): will check `if` conditions too. #### SlevomatCodingStandard.ControlStructures.RequireNullCoalesceOperator 🔧 Requires use of null coalesce operator when possible. #### SlevomatCodingStandard.ControlStructures.RequireNullSafeObjectOperator 🔧 Requires using `?->` operator. Sniff provides the following settings: * `enable`: either to enable or not this sniff. By default, it is enabled for PHP versions 8.0 or higher. #### SlevomatCodingStandard.ControlStructures.RequireSingleLineCondition 🔧 Enforces conditions of `if`, `elseif`, `while` and `do-while` to be on a single line. Sniff provides the following settings: * `maxLineLength`: specifies max allowed line length. If condition (and the rest of the line) would fit on it, it's enforced. Use 0 value to enforce for all conditions, regardless of length. * `alwaysForSimpleConditions`: allows to enforce single line for all simple conditions (i.e no `&&`, `||` or `xor`), regardless of length. #### SlevomatCodingStandard.ControlStructures.RequireShortTernaryOperator 🔧 Requires short ternary operator `?:` when possible. #### SlevomatCodingStandard.ControlStructures.RequireTernaryOperator 🔧 Requires ternary operator when possible. Sniff provides the following settings: * `ignoreMultiLine` (default: `false`): ignores multi-line statements. #### SlevomatCodingStandard.ControlStructures.DisallowYodaComparison 🔧 #### SlevomatCodingStandard.ControlStructures.RequireYodaComparison 🔧 [Yoda conditions](https://en.wikipedia.org/wiki/Yoda_conditions) decrease code comprehensibility and readability by switching operands around comparison operators forcing the reader to read the code in an unnatural way. Sniff provides the following settings: * `alwaysVariableOnRight` (default: `false`): moves variables always to right. `DisallowYodaComparison` looks for and fixes such comparisons not only in `if` statements but in the whole code. However, if you prefer Yoda conditions, you can use `RequireYodaComparison`. #### SlevomatCodingStandard.ControlStructures.UselessIfConditionWithReturn 🔧 Reports useless conditions where both branches return `true` or `false`. Sniff provides the following settings: * `assumeAllConditionExpressionsAreAlreadyBoolean` (default: `false`). #### SlevomatCodingStandard.ControlStructures.UselessTernaryOperator 🔧 Reports useless ternary operator where both branches return `true` or `false`. Sniff provides the following settings: * `assumeAllConditionExpressionsAreAlreadyBoolean` (default: `false`). PK41]!բoo"coding-standard/doc/whitespaces.mdnu[## Whitespaces #### SlevomatCodingStandard.Whitespaces.DuplicateSpaces 🔧 Checks duplicate spaces anywhere because there aren't sniffs for every part of code to check formatting. Sniff provides the following settings: * `ignoreSpacesBeforeAssignment`: to allow multiple spaces to align assignments. * `ignoreSpacesInAnnotation`: to allow multiple spaces to align annotations. * `ignoreSpacesInComment`: to allow multiple spaces to align content of the comment. * `ignoreSpacesInParameters`: to allow multiple spaces to align parameters. * `ignoreSpacesInMatch`: to allow multiple spaces to align `match` expressions. PK41] Ɗqqcoding-standard/doc/numbers.mdnu[## Numbers #### SlevomatCodingStandard.Numbers.DisallowNumericLiteralSeparator 🔧 Disallows numeric literal separators. #### SlevomatCodingStandard.Numbers.RequireNumericLiteralSeparator Requires use of numeric literal separators. This sniff provides the following setting: * `enable`: either to enable or not this sniff. By default, it is enabled for PHP versions 7.4 or higher. * `minDigitsBeforeDecimalPoint`: the minimum digits before decimal point to require separator. * `minDigitsAfterDecimalPoint`: the minimum digits after decimal point to require separator. * `ignoreOctalNumbers`: to ignore octal numbers. PK41]\coding-standard/doc/strings.mdnu[## Strings #### SlevomatCodingStandard.Strings.DisallowVariableParsing Disallows variable parsing inside strings. Sniff provides the following settings: * `disallowDollarCurlySyntax`: disallows usage of `${...}`, enabled by default. * `disallowCurlyDollarSyntax`: disallows usage of `{$...}`, disabled by default. * `disallowSimpleSyntax`: disallows usage of `$...`, disabled by default. PK41]**!coding-standard/doc/type-hints.mdnu[## Type hints #### SlevomatCodingStandard.TypeHints.ClassConstantTypeHint 🔧 * Checks for missing typehints in case they can be declared natively. * Reports useless `@var` annotation (or whole documentation comment) because the type of constant is always clear. Sniff provides the following settings: * `enableNativeTypeHint`: enforces native typehint. It's on by default if you're on PHP 8.3+ * `fixableNativeTypeHint`: (default: `yes`) allows fixing native type hints. Use `no` to disable fixing, or `private` to fix only private constants (safer for inheritance/interface compatibility). #### SlevomatCodingStandard.TypeHints.DeclareStrictTypes 🔧 Enforces having `declare(strict_types = 1)` at the top of each PHP file. Allows configuring how many newlines should be between the ``, `array>`). Sniff provides the following settings: * `traversableTypeHints`: helps fixer detect traversable type hints so `\Traversable|int[]` can be converted to `\Traversable`. #### SlevomatCodingStandard.TypeHints.DisallowMixedTypeHint Disallows usage of "mixed" type hint in phpDocs. #### SlevomatCodingStandard.TypeHints.DNFTypeHintFormat 🔧 Checks format of DNF type hints. Sniff provides the following settings: * `enable`: either to enable or not this sniff. By default, it is enabled for PHP versions 8.0 or higher. * `withSpacesAroundOperators`: `yes` requires spaces around `|` and `&`, `no` requires no space around `|`and `&`. None is set by default so both are enabled. * `withSpacesInsideParentheses`: `yes` requires spaces inside parentheses, `no` requires no spaces inside parentheses. None is set by default so both are enabled. * `shortNullable`: `yes` requires usage of `?` for nullable type hint, `no` disallows it. None is set by default so both are enabled. * `nullPosition`: `first` requires `null` on first position in the type hint, `last` requires last position. None is set by default so `null` can be everywhere. #### SlevomatCodingStandard.TypeHints.LongTypeHints 🔧 Enforces using shorthand scalar typehint variants in phpDocs: `int` instead of `integer` and `bool` instead of `boolean`. This is for consistency with native scalar typehints which also allow shorthand variants only. #### SlevomatCodingStandard.TypeHints.NullTypeHintOnLastPosition 🔧 Enforces `null` type hint on last position in annotations. #### SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue 🔧🚧 Checks whether the nullablity `?` symbol is present before each nullable and optional parameter (which are marked as `= null`): ```php function foo( int $foo = null, // ? missing ?int $bar = null // correct ) { } ``` #### SlevomatCodingStandard.TypeHints.ParameterTypeHint 🔧🚧 * Checks for missing parameter typehints in case they can be declared natively. If the phpDoc contains something that can be written as a native PHP 7.0+ typehint, this sniff reports that. * Checks for useless `@param` annotations. If the native method declaration contains everything and the phpDoc does not add anything useful, it's reported as useless and can optionally be automatically removed with `phpcbf`. * Forces to specify what's in traversable types like `array`, `iterable` and `\Traversable`. Sniff provides the following settings: * `enableObjectTypeHint`: enforces to transform `@param object` into native `object` typehint. It's on by default if you're on PHP 7.2+ * `enableMixedTypeHint`: enforces to transform `@param mixed` into native `mixed` typehint. It's on by default if you're on PHP 8.0+ * `enableUnionTypeHint`: enforces to transform `@param string|int` into native `string|int` typehint. It's on by default if you're on PHP 8.0+ * `enableIntersectionTypeHint`: enforces to transform `@param Foo&Bar` into native `Foo&Bar` typehint. It's on by default if you're on PHP 8.1+ * `enableStandaloneNullTrueFalseTypeHints`: enforces to transform `@param true`, `@param false` or `@param null` into native typehints. It's on by default if you're on PHP 8.2+ * `traversableTypeHints`: enforces which typehints must have specified contained type. E.g. if you set this to `\Doctrine\Common\Collections\Collection`, then `\Doctrine\Common\Collections\Collection` must always be supplied with the contained type: `\Doctrine\Common\Collections\Collection|Foo[]`. This sniff can cause an error if you're overriding or implementing a parent method which does not have typehints. In such cases add `@phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint` annotation to the method to have this sniff skip it. #### SlevomatCodingStandard.TypeHints.ParameterTypeHintSpacing 🔧 * Checks that there's a single space between a typehint and a parameter name: `Foo $foo` * Checks that there's no whitespace between a nullability symbol and a typehint: `?Foo` #### SlevomatCodingStandard.TypeHints.PropertyTypeHint 🔧🚧 * Checks for missing property typehints in case they can be declared natively. If the phpDoc contains something that can be written as a native PHP 7.4+ typehint, this sniff reports that. * Checks for useless `@var` annotations. If the native method declaration contains everything and the phpDoc does not add anything useful, it's reported as useless and can optionally be automatically removed with `phpcbf`. * Forces to specify what's in traversable types like `array`, `iterable` and `\Traversable`. Sniff provides the following settings: * `enableNativeTypeHint`: enforces to transform `@var int` into native `int` typehint. It's on by default if you're on PHP 7.4+ * `enableMixedTypeHint`: enforces to transform `@var mixed` into native `mixed` typehint. It's on by default if you're on PHP 8.0+. It can be enabled only when `enableNativeTypeHint` is enabled too. * `enableUnionTypeHint`: enforces to transform `@var string|int` into native `string|int` typehint. It's on by default if you're on PHP 8.0+. It can be enabled only when `enableNativeTypeHint` is enabled too. * `enableIntersectionTypeHint`: enforces to transform `@var Foo&Bar` into native `Foo&Bar` typehint. It's on by default if you're on PHP 8.1+. It can be enabled only when `enableNativeTypeHint` is enabled too. * `enableStandaloneNullTrueFalseTypeHints`: enforces to transform `@var true`, `@var false` or `@var null` into native typehints. It's on by default if you're on PHP 8.2+. It can be enabled only when `enableNativeTypeHint` is enabled too. * `traversableTypeHints`: enforces which typehints must have specified contained type. E.g. if you set this to `\Doctrine\Common\Collections\Collection`, then `\Doctrine\Common\Collections\Collection` must always be supplied with the contained type: `\Doctrine\Common\Collections\Collection|Foo[]`. This sniff can cause an error if you're overriding parent property which does not have typehints. In such cases add `@phpcsSuppress SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingNativeTypeHint` annotation to the property to have this sniff skip it. #### SlevomatCodingStandard.TypeHints.ReturnTypeHint 🔧🚧 * Checks for missing return typehints in case they can be declared natively. If the phpDoc contains something that can be written as a native PHP 7.0+ typehint, this sniff reports that. * Checks for useless `@return` annotations. If the native method declaration contains everything and the phpDoc does not add anything useful, it's reported as useless and can optionally be automatically removed with `phpcbf`. * Forces to specify what's in traversable types like `array`, `iterable` and `\Traversable`. Sniff provides the following settings: * `enableObjectTypeHint`: enforces to transform `@return object` into native `object` typehint. It's on by default if you're on PHP 7.2+ * `enableStaticTypeHint`: enforces to transform `@return static` into native `static` typehint. It's on by default if you're on PHP 8.0+ * `enableMixedTypeHint`: enforces to transform `@return mixed` into native `mixed` typehint. It's on by default if you're on PHP 8.0+ * `enableUnionTypeHint`: enforces to transform `@return string|int` into native `string|int` typehint. It's on by default if you're on PHP 8.0+. * `enableIntersectionTypeHint`: enforces to transform `@return Foo&Bar` into native `Foo&Bar` typehint. It's on by default if you're on PHP 8.1+. * `enableNeverTypeHint`: enforces to transform `@return never` into native `never` typehint. It's on by default if you're on PHP 8.1+. * `enableStandaloneNullTrueFalseTypeHints`: enforces to transform `@return true`, `@return false` or `@return null` into native typehints. It's on by default if you're on PHP 8.2+. * `traversableTypeHints`: enforces which typehints must have specified contained type. E.g. if you set this to `\Doctrine\Common\Collections\Collection`, then `\Doctrine\Common\Collections\Collection` must always be supplied with the contained type: `\Doctrine\Common\Collections\Collection|Foo[]`. You can add `@phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint` annotation to the method to skip the check. #### SlevomatCodingStandard.TypeHints.ReturnTypeHintSpacing 🔧 Enforces consistent formatting of return typehints, like this: ```php function foo(): ?int ``` Sniff provides the following settings: * `spacesCountBeforeColon`: the number of spaces expected between closing brace and colon. #### SlevomatCodingStandard.TypeHints.UnionTypeHintFormat 🔧 Checks format of union type hints. Sniff provides the following settings: * `enable`: either to enable or not this sniff. By default, it is enabled for PHP versions 8.0 or higher. * `withSpaces`: `yes` requires spaces around `|`, `no` requires no space around `|`. None is set by default so both are enabled. * `shortNullable`: `yes` requires usage of `?` for nullable type hint, `no` disallows it. None is set by default so both are enabled. * `nullPosition`: `first` requires `null` on first position in the type hint, `last` requires last position. None is set by default so `null` can be everywhere. #### SlevomatCodingStandard.TypeHints.UselessConstantTypeHint 🔧 Reports useless `@var` annotation (or whole documentation comment) for constants because the type of constant is always clear. PK41],C~  !coding-standard/doc/complexity.mdnu[## Complexity #### SlevomatCodingStandard.Complexity.Cognitive Enforces maximum [cognitive complexity](https://www.sonarsource.com/docs/CognitiveComplexity.pdf) for functions. Sniff provides the following setting: * `warningThreshold` (default: `6`) * `errorThreshold` (default: `6`) PK41]!coding-standard/doc/exceptions.mdnu[## Exceptions #### SlevomatCodingStandard.Exceptions.DeadCatch This sniff finds unreachable catch blocks: ```php try { doStuff(); } catch (\Throwable $e) { log($e); } catch (\InvalidArgumentException $e) { // unreachable! } ``` #### SlevomatCodingStandard.Exceptions.DisallowNonCapturingCatch This sniff forbids use of non-capturing catch introduced in PHP 8.0 [PHP RFC: non-capturing catches](https://wiki.php.net/rfc/non-capturing_catches). #### SlevomatCodingStandard.Exceptions.ReferenceThrowableOnly 🔧🚧 In PHP 7.0, a [`Throwable` interface was added](https://wiki.php.net/rfc/throwable-interface) that allows catching and handling errors in more cases than `Exception` previously allowed. So, if the catch statement contained `Exception` on PHP 5.x, it means it should probably be rewritten to reference `Throwable` on PHP 7.x. This sniff enforces that. #### SlevomatCodingStandard.Exceptions.RequireNonCapturingCatch 🔧 Sniff provides the following settings: * `enable`: either to enable or not this sniff. By default, it is enabled for PHP versions 8.0 or higher. It requires non-capturing catch when the variable with exception is not used. PK41]x٠  !coding-standard/doc/namespaces.mdnu[## Namespaces #### SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses 🔧 Sniff checks whether `use` declarations at the top of a file are alphabetically sorted. Follows natural sorting and takes edge cases with special symbols into consideration. The following code snippet is an example of correctly sorted uses: ```php use LogableTrait; use LogAware; use LogFactory; use LoggerInterface; use LogLevel; use LogStandard; ``` Sniff provides the following settings: * `psr12Compatible` (default: `true`): sets the required order to `classes`, `functions` and `constants`. `false` sets the required order to `classes`, `constants` and `functions`. * `caseSensitive`: compare namespaces case sensitively, which makes this order correct: ```php use LogAware; use LogFactory; use LogLevel; use LogStandard; use LogableTrait; use LoggerInterface; ``` #### SlevomatCodingStandard.Namespaces.DisallowGroupUse [Group use declarations](https://wiki.php.net/rfc/group_use_declarations) are ugly, make diffs ugly and this sniff prohibits them. #### SlevomatCodingStandard.Namespaces.FullyQualifiedExceptions 🔧 This sniff reduces confusion in the following code snippet: ```php try { $this->foo(); } catch (Exception $e) { // Is this the general exception all exceptions must extend from? Or Exception from the current namespace? } ``` All references to types named `Exception` or ending with `Exception` must be referenced via a fully qualified name: ```php try { $this->foo(); } catch (\FooCurrentNamespace\Exception $e) { } catch (\Exception $e) { } ``` Sniff provides the following settings: * Exceptions with different names can be configured in `specialExceptionNames` property. * If your codebase uses classes that look like exceptions (because they have `Exception` or `Error` suffixes) but aren't, you can add them to `ignoredNames` property and the sniff won't enforce them to be fully qualified. Classes with `Error` suffix have to be added to ignored only if they are in the root namespace (like `LibXMLError`). #### SlevomatCodingStandard.Namespaces.FullyQualifiedGlobalConstants 🔧 All references to global constants must be referenced via a fully qualified name. Sniff provides the following settings: * `include`: list of global constants that must be referenced via FQN. If not set all constants are considered. * `exclude`: list of global constants that are allowed not to be referenced via FQN. #### SlevomatCodingStandard.Namespaces.FullyQualifiedGlobalFunctions 🔧 All references to global functions must be referenced via a fully qualified name. Sniff provides the following settings: * `include`: list of global functions that must be referenced via FQN. If not set all functions are considered. * `includeSpecialFunctions`: include complete list of PHP internal functions that could be optimized when referenced via FQN. * `exclude`: list of global functions that are allowed not to be referenced via FQN. #### SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameInAnnotation 🔧 Enforces fully qualified names of classes and interfaces in phpDocs - in annotations. This results in unambiguous phpDocs. Sniff provides the following settings: * `ignoredAnnotationNames`: case-sensitive list of annotation names that the sniff should ignore. Useful for custom annotation names like `@apiParam` #### SlevomatCodingStandard.Namespaces.MultipleUsesPerLine Prohibits multiple uses separated by commas: ```php use Foo, Bar; ``` #### SlevomatCodingStandard.Namespaces.NamespaceDeclaration 🔧 Enforces one space after `namespace`, disallows content between namespace name and semicolon and disallows use of bracketed syntax. #### SlevomatCodingStandard.Namespaces.NamespaceSpacing 🔧 Enforces configurable number of lines before and after `namespace`. Sniff provides the following settings: * `linesCountBeforeNamespace`: allows to configure the number of lines before `namespace`. * `linesCountAfterNamespace`: allows to configure the number of lines after `namespace`. #### SlevomatCodingStandard.Namespaces.RequireOneNamespaceInFile Requires only one namespace in a file. #### SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly 🔧 Sniff provides the following settings: * `searchAnnotations` (default: `false`): enables searching for mentions in annotations. * `namespacesRequiredToUse`: if not set, all namespaces are required to be used. When set, only mentioned namespaces are required to be used. Useful in tandem with UseOnlyWhitelistedNamespaces sniff. * `allowFullyQualifiedExceptions`, `specialExceptionNames` & `ignoredNames`: allows fully qualified exceptions. Useful in tandem with FullyQualifiedExceptions sniff. * `allowFullyQualifiedNameForCollidingClasses`: allow fully qualified name for a class with a colliding use statement. * `allowFullyQualifiedNameForCollidingFunctions`: allow fully qualified name for a function with a colliding use statement. * `allowFullyQualifiedNameForCollidingConstants`: allow fully qualified name for a constant with a colliding use statement. * `allowFullyQualifiedGlobalClasses`: allows using fully qualified classes from global space (i.e. `\DateTimeImmutable`). * `allowFullyQualifiedGlobalFunctions`: allows using fully qualified functions from global space (i.e. `\phpversion()`). * `allowFullyQualifiedGlobalConstants`: allows using fully qualified constants from global space (i.e. `\PHP_VERSION`). * `allowFallbackGlobalFunctions`: allows using global functions via fallback name without `use` (i.e. `phpversion()`). * `allowFallbackGlobalConstants`: allows using global constants via fallback name without `use` (i.e. `PHP_VERSION`). * `allowPartialUses`: allows using and referencing whole namespaces. * `allowWhenNoNamespace` (default: `true`): force even when there's no namespace in the file. #### SlevomatCodingStandard.Namespaces.UseDoesNotStartWithBackslash 🔧 Disallows leading backslash in use statement: ```php use \Foo\Bar; ``` #### SlevomatCodingStandard.Namespaces.UseFromSameNamespace 🔧 Sniff prohibits uses from the same namespace: ```php namespace Foo; use Foo\Bar; ``` #### SlevomatCodingStandard.Namespaces.UseSpacing 🔧 Enforces configurable number of lines before first `use`, after last `use` and between two different types of `use` (eg. between `use function` and `use const`). Also enforces zero number of lines between same types of `use`. Sniff provides the following settings: * `linesCountBeforeFirstUse`: allows to configure the number of lines before first `use`. * `linesCountBetweenUseTypes`: allows to configure the number of lines between two different types of `use`. * `linesCountAfterLastUse`: allows to configure the number of lines after last `use`. #### SlevomatCodingStandard.Namespaces.UseOnlyWhitelistedNamespaces Sniff disallows uses of other than configured namespaces. Sniff provides the following settings: * `namespacesRequiredToUse`: namespaces in this array are the only ones allowed to be used. E.g. root project namespace. * `allowUseFromRootNamespace`: also allow using top-level namespace: ```php use DateTimeImmutable; ``` #### SlevomatCodingStandard.Namespaces.UselessAlias 🔧 Looks for `use` alias that is same as unqualified name. #### SlevomatCodingStandard.Namespaces.UnusedUses 🔧 Looks for unused imports from other namespaces. Sniff provides the following settings: * `searchAnnotations` (default: `false`): enables searching for class names in annotations. * `ignoredAnnotationNames`: case-sensitive list of annotation names that the sniff should ignore (only the name is ignored, annotation content is still searched). Useful for name collisions like `@testCase` annotation and `TestCase` class. * `ignoredAnnotations`: case-sensitive list of annotation names that the sniff ignore completely (both name and content are ignored). Useful for name collisions like `@group Cache` annotation and `Cache` class. PK41]( coding-standard/doc/functions.mdnu[## Functions #### SlevomatCodingStandard.Functions.ArrowFunctionDeclaration 🔧 Checks `fn` declaration. Sniff provides the following settings: * `spacesCountAfterKeyword`: the number of spaces after `fn`. * `spacesCountBeforeArrow`: the number of spaces before `=>`. * `spacesCountAfterArrow`: the number of spaces after `=>`. * `allowMultiLine`: allows multi-line declaration. #### SlevomatCodingStandard.Functions.DisallowArrowFunction Disallows arrow functions. #### SlevomatCodingStandard.Functions.DisallowEmptyFunction Reports empty functions body and requires at least a comment inside. #### SlevomatCodingStandard.Functions.FunctionLength Disallows long functions. This sniff provides the following setting: * `includeComments` (default: `false`): should comments be included in the count. * `includeWhitespace` (default: `false`): should empty lines be included in the count. * `maxLinesLength` (default: `20`): specifies max allowed function lines length. #### SlevomatCodingStandard.Functions.RequireArrowFunction 🔧 Requires arrow functions. Sniff provides the following settings: * `allowNested` (default: `true`) * `enable`: either to enable or not this sniff. By default, it is enabled for PHP versions 7.4 or higher. #### SlevomatCodingStandard.Functions.RequireMultiLineCall 🔧 Enforces function call to be split to more lines so each parameter is on its own line. Sniff provides the following settings: * `minLineLength`: specifies min line length to enforce call to be split. Use 0 value to enforce for all calls, regardless of length. #### SlevomatCodingStandard.Functions.RequireSingleLineCall 🔧 Enforces function call to be on a single line. Sniff provides the following settings: * `maxLineLength`: specifies max allowed line length. If call would fit on it, it's enforced. Use 0 value to enforce for all calls, regardless of length. * `ignoreWithComplexParameter` (default: `true`): ignores calls with arrays, closures, arrow functions and nested calls. #### SlevomatCodingStandard.Functions.DisallowNamedArguments This sniff disallows usage of named arguments. #### SlevomatCodingStandard.Functions.NamedArgumentSpacing 🔧 Checks spacing in named argument. #### SlevomatCodingStandard.Functions.DisallowTrailingCommaInCall 🔧 This sniff disallows trailing commas in multi-line calls. This sniff provides the following setting: * `onlySingleLine`: to enable checks only for single-line calls. #### SlevomatCodingStandard.Functions.RequireTrailingCommaInCall 🔧 Commas after the last parameter in function or method call make adding a new parameter easier and result in a cleaner versioning diff. This sniff enforces trailing commas in multi-line calls. This sniff provides the following setting: * `enable`: either to enable or not this sniff. By default, it is enabled for PHP versions 7.3 or higher. #### SlevomatCodingStandard.Functions.DisallowTrailingCommaInClosureUse 🔧 This sniff disallows trailing commas in multi-line `use` of closure declaration. This sniff provides the following setting: * `onlySingleLine`: to enable checks only for single-line `use` declarations. #### SlevomatCodingStandard.Functions.RequireTrailingCommaInClosureUse 🔧 Commas after the last inherited variable in multi-line `use` of closure declaration make adding a new variable easier and result in a cleaner versioning diff. This sniff enforces trailing commas in multi-line declarations. This sniff provides the following setting: * `enable`: either to enable or not this sniff. By default, it is enabled for PHP versions 8.0 or higher. #### SlevomatCodingStandard.Functions.DisallowTrailingCommaInDeclaration 🔧 This sniff disallows trailing commas in multi-line declarations. This sniff provides the following setting: * `onlySingleLine`: to enable checks only for single-line declarations. #### SlevomatCodingStandard.Functions.RequireTrailingCommaInDeclaration 🔧 Commas after the last parameter in function or method declaration make adding a new parameter easier and result in a cleaner versioning diff. This sniff enforces trailing commas in multi-line declarations. This sniff provides the following setting: * `enable`: either to enable or not this sniff. By default, it is enabled for PHP versions 8.0 or higher. #### SlevomatCodingStandard.Functions.StaticClosure 🔧 Reports closures not using `$this` that are not declared `static`. #### SlevomatCodingStandard.Functions.StrictCall Some functions have `$strict` parameter. This sniff reports calls to these functions without the parameter or with `$strict = false`. #### SlevomatCodingStandard.Functions.UnusedInheritedVariablePassedToClosure 🔧 Looks for unused inherited variables passed to closure via `use`. #### SlevomatCodingStandard.Functions.UnusedParameter 🚧 Looks for unused parameters. This sniff provides the following setting: * `allowedParameterPatterns`: allows to configure which parameters are always allowed, even if unused. This is an array of regular expressions (PCRE) with delimiters, but without the leading `$` from variable names. (For example, use `[/^_/]` to allow parameters that start with an underscore, like `$_unused`.) #### SlevomatCodingStandard.Functions.UselessParameterDefaultValue 🚧 Looks for useless parameter default value. PK41]!coding-standard/doc/commenting.mdnu[## Commenting #### SlevomatCodingStandard.Commenting.AnnotationName 🔧 Reports incorrect annotation name. It reports standard annotation names used by phpDocumentor, PHPUnit, PHPStan and Psalm by default. Unknown annotation names are ignored. Sniff provides the following settings: * `annotations`: allows to configure which annotations are checked and how. #### SlevomatCodingStandard.Commenting.DeprecatedAnnotationDeclaration Reports `@deprecated` annotations without description. #### SlevomatCodingStandard.Commenting.DisallowCommentAfterCode 🔧 Sniff disallows comments after code at the same line. #### SlevomatCodingStandard.Commenting.ForbiddenAnnotations 🔧 Reports forbidden annotations. No annotations are forbidden by default, the configuration is completely up to the user. It's recommended to forbid obsolete and inappropriate annotations like: * `@author`, `@created`, `@version`: we have version control systems. * `@package`: we have namespaces. * `@copyright`, `@license`: it's not necessary to repeat licensing information in each file. * `@throws`: it's not possible to enforce this annotation and the information can become outdated. Sniff provides the following settings: * `forbiddenAnnotations`: allows to configure which annotations are forbidden to be used. #### SlevomatCodingStandard.Commenting.ForbiddenComments 🔧 Reports forbidden comments in descriptions. Nothing is forbidden by default, the configuration is completely up to the user. It's recommended to forbid generated or inappropriate messages like: * `Constructor.` * `Created by PhpStorm.` Sniff provides the following settings: * `forbiddenCommentPatterns`: allows to configure which comments are forbidden to be used. This is an array of regular expressions (PCRE) with delimiters. #### SlevomatCodingStandard.Commenting.DocCommentSpacing 🔧 Enforces configurable number of lines before first content (description or annotation), after last content (description or annotation), between description and annotations, between two different annotation types (eg. between `@param` and `@return`). Sniff provides the following settings: * `linesCountBeforeFirstContent`: allows to configure the number of lines before first content (description or annotation). * `linesCountBetweenDescriptionAndAnnotations`: allows to configure the number of lines between description and annotations. * `linesCountBetweenDifferentAnnotationsTypes`: allows to configure the number of lines between two different annotation types. * `linesCountBetweenAnnotationsGroups`: allows to configure the number of lines between annotation groups. * `linesCountAfterLastContent`: allows to configure the number of lines after last content (description or annotation). * `annotationsGroups`: allows to configure order of annotation groups and even order of annotations in every group. Supports prefixes, eg. `@ORM\`. ```xml ``` If `annotationsGroups` is set, `linesCountBetweenDifferentAnnotationsTypes` is ignored and `linesCountBetweenAnnotationsGroups` is applied. If `annotationsGroups` is not set, `linesCountBetweenAnnotationsGroups` is ignored and `linesCountBetweenDifferentAnnotationsTypes` is applied. Annotations not in any group are placed to automatically created last group. #### SlevomatCodingStandard.Commenting.EmptyComment 🔧 Reports empty comments. #### SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration 🔧 Reports invalid inline phpDocs with `@var`. Sniff provides the following settings: * `allowDocCommentAboveReturn`: Allows documentation comments without variable name above `return` statement. * `allowAboveNonAssignment`: Allows documentation comments above non-assignment if the line contains the right variable name. #### SlevomatCodingStandard.Commenting.RequireOneLinePropertyDocComment 🔧 Requires property comments with single-line content to be written as one-liners. #### SlevomatCodingStandard.Commenting.RequireOneLineDocComment 🔧 Sniff requires comments with single-line content to be written as one-liners. #### SlevomatCodingStandard.Commenting.DisallowOneLinePropertyDocComment 🔧 Sniff requires comments with single-line content to be written as multi-liners. #### SlevomatCodingStandard.Commenting.UselessFunctionDocComment 🔧 * Checks for useless doc comments. If the native method declaration contains everything and the phpDoc does not add anything useful, it's reported as useless and can optionally be automatically removed with `phpcbf`. * Some phpDocs might still be useful even if they do not add any typehint information. They can contain textual descriptions of code elements and also some meaningful annotations like `@expectException` or `@dataProvider`. Sniff provides the following settings: * `traversableTypeHints`: enforces which typehints must have specified contained type. E.g. if you set this to `\Doctrine\Common\Collections\Collection`, then `\Doctrine\Common\Collections\Collection` must always be supplied with the contained type: `\Doctrine\Common\Collections\Collection|Foo[]`. This sniff can cause an error if you're overriding or implementing a parent method which does not have typehints. In such cases add `@phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint` annotation to the method to have this sniff skip it. #### SlevomatCodingStandard.Commenting.UselessInheritDocComment 🔧 Reports documentation comments containing only `{@inheritDoc}` annotation because inheritance is automatic, and it's not needed to use a special annotation for it. PK41]MMcoding-standard/doc/arrays.mdnu[## Arrays #### SlevomatCodingStandard.Arrays.AlphabeticallySortedByKeys 🔧 Arrays should be defined with keys in alphabetical order. It defines where new entries should be inserted. It reduces merge conflicts and duplicate entries. This sniff enforces natural sorting of array definitions by key in multi-line arrays. #### SlevomatCodingStandard.Arrays.ArrayAccess 🔧 Disallow whitespace between array access operator and the variable, or between array access operators. #### SlevomatCodingStandard.Arrays.DisallowImplicitArrayCreation Disallows implicit array creation. #### SlevomatCodingStandard.Arrays.DisallowPartiallyKeyed 🚧 Array must have keys specified for either all or none of the values. #### SlevomatCodingStandard.Arrays.MultiLineArrayEndBracketPlacement 🔧 Enforces reasonable end bracket placement for multi-line arrays. #### SlevomatCodingStandard.Arrays.SingleLineArrayWhitespace 🔧 Checks whitespace in single line array declarations (whitespace between brackets, around commas, ...). Sniff provides the following settings: * `spacesAroundBrackets`: number of spaces you require to have around array brackets * `enableEmptyArrayCheck` (default: `false`): enables check for empty arrays #### SlevomatCodingStandard.Arrays.TrailingArrayComma 🔧 Commas after last element in an array make adding a new element easier and result in a cleaner versioning diff. This sniff enforces trailing commas in multi-line arrays. Sniff provides the following settings: * `enableAfterHeredoc`: enables/disables trailing commas after HEREDOC/NOWDOC, default based on PHP version. PK41]@ coding-standard/doc/variables.mdnu[## Variables #### SlevomatCodingStandard.Variables.DisallowSuperGlobalVariable Disallows use of super global variables. #### SlevomatCodingStandard.Variables.DisallowVariableVariable Disallows use of variable variables. #### SlevomatCodingStandard.Variables.DuplicateAssignmentToVariable Looks for duplicate assignments to a variable. #### SlevomatCodingStandard.Variables.UnusedVariable Looks for unused variables. Sniff provides the following settings: * `ignoreUnusedValuesWhenOnlyKeysAreUsedInForeach` (default: `false`): ignore unused `$value` in foreach when only `$key` is used ```php foreach ($values as $key => $value) { echo $key; } ``` #### SlevomatCodingStandard.Variables.UselessVariable 🔧 Looks for useless variables. PK41]qq coding-standard/doc/operators.mdnu[## Operators #### SlevomatCodingStandard.Operators.DisallowEqualOperators 🔧 Disallows using loose `==` and `!=` comparison operators. Use `===` and `!==` instead, they are much more secure and predictable. #### SlevomatCodingStandard.Operators.DisallowIncrementAndDecrementOperators Disallows using `++` and `--` operators. #### SlevomatCodingStandard.Operators.NegationOperatorSpacing 🔧 Checks if there is the same number of spaces after negation operator as expected. Sniff provides the following settings: * `spacesCount`: the number of spaces expected after the negation operator #### SlevomatCodingStandard.Operators.RequireCombinedAssignmentOperator 🔧 Requires using combined assignment operators, eg `+=`, `.=` etc. #### SlevomatCodingStandard.Operators.RequireOnlyStandaloneIncrementAndDecrementOperators Reports `++` and `--` operators not used standalone. #### SlevomatCodingStandard.Operators.SpreadOperatorSpacing 🔧 Enforces configurable number of spaces after the `...` operator. Sniff provides the following settings: * `spacesCountAfterOperator`: the number of spaces after the `...` operator. PK41]Pb) ) coding-standard/doc/files.mdnu[## Files #### SlevomatCodingStandard.Files.FileLength Disallows long files. This sniff provides the following settings: * `includeComments` (default: `false`): should comments be included in the count. * `includeWhitespace` (default: `false`): should empty lines be included in the count. * `maxLinesLength` (default: `250`): specifies max allowed function lines length. #### SlevomatCodingStandard.Files.LineLength Enforces maximum length of a single line of code. Sniff provides the following settings: * `lineLengthLimit`: actual limit of the line length * `ignoreComments`: whether to ignore line length of comments * `ignoreImports`: whether to ignore line length of import (use) statements #### SlevomatCodingStandard.Files.TypeNameMatchesFileName For projects not following the [PSR-0](http://www.php-fig.org/psr/psr-0/) or [PSR-4](http://www.php-fig.org/psr/psr-4/) autoloading standards, this sniff checks whether a namespace and a name of a class/interface/trait follows agreed-on way to organize code into directories and files. Other than enforcing that the type name must match the name of the file it's contained in, this sniff is very configurable. Consider the following sample configuration: ```xml ``` Sniff provides the following settings: * `rootNamespaces` property expects configuration similar to PSR-4 - project directories mapped to certain namespaces. * `skipDirs` are not taken into consideration when comparing a path to a namespace. For example, with the above settings, file at path `app/services/Product/Product.php` is expected to contain `Slevomat\Product\Product`, not `Slevomat\services\Product\Product`. * `extensions`: allow different file extensions. Default is `php`. * `ignoredNamespaces`: sniff is not performed on these namespaces. PK41]^MX X coding-standard/doc/php.mdnu[## PHP #### SlevomatCodingStandard.PHP.DisallowDirectMagicInvokeCall 🔧 Disallows direct call of `__invoke()`. #### SlevomatCodingStandard.PHP.DisallowReference Sniff disallows usage of references. #### SlevomatCodingStandard.PHP.ForbiddenClasses 🔧 Reports usage of forbidden classes, interfaces, parent classes and traits. And provide the following settings: * `forbiddenClasses`: forbids creating instances with `new` keyword or accessing with `::` operator * `forbiddenExtends`: forbids extending with `extends` keyword * `forbiddenInterfaces`: forbids usage in `implements` section * `forbiddenTraits`: forbids imports with `use` keyword Optionally can be passed as an alternative for auto fixes. See `phpcs.xml` file example: ```xml ``` #### SlevomatCodingStandard.PHP.ReferenceSpacing 🔧 Enforces configurable number of spaces after reference. Sniff provides the following settings: * `spacesCountAfterReference`: the number of spaces after `&`. #### SlevomatCodingStandard.PHP.RequireExplicitAssertion 🔧 Requires assertion via `assert` instead of inline documentation comments. Sniff provides the following settings: * `enableIntegerRanges` (default: `false`): enables support for `positive-int`, `negative-int` and `int<0, 100>`. * `enableAdvancedStringTypes` (default: `false`): enables support for `callable-string`, `numeric-string` and `non-empty-string`. #### SlevomatCodingStandard.PHP.RequireNowdoc 🔧 Requires nowdoc syntax instead of heredoc when possible. #### SlevomatCodingStandard.PHP.OptimizedFunctionsWithoutUnpacking PHP optimizes some internal functions into special opcodes on VM level. Such optimization results in much faster execution compared to calling standard functions. This only works when these functions are not invoked with argument unpacking (`...`). The list of these functions varies across PHP versions, but is the same as functions that must be referenced by their global name (either by `\ ` prefix or using `use function`), not a fallback name inside namespaced code. #### SlevomatCodingStandard.PHP.ShortList 🔧 Enforces using short form of list syntax, `[...]` instead of `list(...)`. #### SlevomatCodingStandard.PHP.TypeCast 🔧 Enforces using shorthand cast operators, forbids use of unset and binary cast operators: `(bool)` instead of `(boolean)`, `(int)` instead of `(integer)`, `(float)` instead of `(double)` or `(real)`. `(binary)` and `(unset)` are forbidden. #### SlevomatCodingStandard.PHP.UselessParentheses 🔧 Looks for useless parentheses. Sniff provides the following settings: * `ignoreComplexTernaryConditions` (default: `false`): ignores complex ternary conditions - condition must contain `&&`, `||` etc. or end of line. #### SlevomatCodingStandard.PHP.UselessSemicolon 🔧 Looks for useless semicolons. PK41]y-ttcoding-standard/.typos.tomlnu[[files] extend-exclude = [ ".git/", ] ignore-hidden = false [default] extend-ignore-re = [ "const BA =", ] PK41]*3coding-standard/.editorconfignu[root = true [*] charset = utf-8 end_of_line = lf indent_size = 4 indent_style = tab insert_final_newline = true tab_width = 4 trim_trailing_whitespace = true PK41]+6 "coding-standard/CODE_OF_CONDUCT.mdnu[# Contributor Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project maintainer at . All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ PK41]M8u8ucoding-standard/README.mdnu[# Slevomat Coding Standard [![Latest version](https://img.shields.io/packagist/v/slevomat/coding-standard.svg?colorB=007EC6)](https://packagist.org/packages/slevomat/coding-standard) [![Downloads](https://img.shields.io/packagist/dt/slevomat/coding-standard.svg?colorB=007EC6)](https://packagist.org/packages/slevomat/coding-standard) [![Build status](https://github.com/slevomat/coding-standard/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/slevomat/coding-standard/actions?query=workflow%3ABuild+branch%3Amaster) [![Code coverage](https://codecov.io/gh/slevomat/coding-standard/branch/master/graph/badge.svg)](https://codecov.io/gh/slevomat/coding-standard) ![PHPStan](https://img.shields.io/badge/style-level%207-brightgreen.svg?&label=phpstan) Slevomat Coding Standard for [PHP_CodeSniffer](https://github.com/PHPCSStandards/PHP_CodeSniffer) provides sniffs that fall into three categories: * Functional - improving the safety and behaviour of code * Cleaning - detecting dead code * Formatting - rules for consistent code looks ## Table of contents 1. [Alphabetical list of sniffs](#alphabetical-list-of-sniffs) 2. [Installation](#installation) 3. [How to run the sniffs](#how-to-run-the-sniffs) - [Choose which sniffs to run](#choose-which-sniffs-to-run) - [Exclude sniffs you don't want to run](#exclude-sniffs-you-dont-want-to-run) 4. [Fixing errors automatically](#fixing-errors-automatically) 5. [Suppressing sniffs locally](#suppressing-sniffs-locally) 6. [Contributing](#contributing) ## Alphabetical list of sniffs 🔧 = [Automatic errors fixing](#fixing-errors-automatically) 🚧 = [Sniff check can be suppressed locally](#suppressing-sniffs-locally) - [SlevomatCodingStandard.Arrays.AlphabeticallySortedByKeys](doc/arrays.md#slevomatcodingstandardarrayalphabeticallysortedbykeys) 🔧 - [SlevomatCodingStandard.Arrays.ArrayAccess](doc/arrays.md#slevomatcodingstandardarraysarrayaccess-) 🔧 - [SlevomatCodingStandard.Arrays.DisallowImplicitArrayCreation](doc/arrays.md#slevomatcodingstandardarraysdisallowimplicitarraycreation) - [SlevomatCodingStandard.Arrays.DisallowPartiallyKeyed](doc/arrays.md#slevomatcodingstandardarraysdisallowpartiallykeyed) 🚧 - [SlevomatCodingStandard.Arrays.MultiLineArrayEndBracketPlacement](doc/arrays.md#slevomatcodingstandardarraysmultilinearrayendbracketplacement-) 🔧 - [SlevomatCodingStandard.Arrays.SingleLineArrayWhitespace](doc/arrays.md#slevomatcodingstandardarrayssinglelinearraywhitespace-) 🔧 - [SlevomatCodingStandard.Arrays.TrailingArrayComma](doc/arrays.md#slevomatcodingstandardarraystrailingarraycomma-) 🔧 - [SlevomatCodingStandard.Attributes.AttributeAndTargetSpacing](doc/attributes.md#slevomatcodingstandardattributesattributeandtargetspacing-) 🔧 - [SlevomatCodingStandard.Attributes.AttributesOrder](doc/attributes.md#slevomatcodingstandardattributesattributesorder-) 🔧 - [SlevomatCodingStandard.Attributes.DisallowAttributesJoining](doc/attributes.md#slevomatcodingstandardattributesdisallowattributesjoining-) 🔧 - [SlevomatCodingStandard.Attributes.DisallowMultipleAttributesPerLine](doc/attributes.md#slevomatcodingstandardattributesdisallowmultipleattributesperline-) 🔧 - [SlevomatCodingStandard.Attributes.RequireAttributeAfterDocComment](doc/attributes.md#slevomatcodingstandardattributesrequireattributeafterdoccomment-) 🔧 - [SlevomatCodingStandard.Classes.BackedEnumTypeSpacing](doc/classes.md#slevomatcodingstandardclassesbackedenumtypespacing-) 🔧 - [SlevomatCodingStandard.Classes.ClassConstantVisibility](doc/classes.md#slevomatcodingstandardclassesclassconstantvisibility-) 🔧 - [SlevomatCodingStandard.Classes.ClassLength](doc/classes.md#slevomatcodingstandardclassesclasslength) - [SlevomatCodingStandard.Classes.ClassMemberSpacing](doc/classes.md#slevomatcodingstandardclassesclassmemberspacing-) 🔧 - [SlevomatCodingStandard.Classes.ClassStructure](doc/classes.md#slevomatcodingstandardclassesclassstructure-) 🔧 - [SlevomatCodingStandard.Classes.ConstantSpacing](doc/classes.md#slevomatcodingstandardclassesconstantspacing-) 🔧 - [SlevomatCodingStandard.Classes.DisallowConstructorPropertyPromotion](doc/classes.md#slevomatcodingstandardclassesdisallowconstructorpropertypromotion) - [SlevomatCodingStandard.Classes.DisallowLateStaticBindingForConstants](doc/classes.md#slevomatcodingstandardclassesdisallowlatestaticbindingforconstants-) 🔧 - [SlevomatCodingStandard.Classes.DisallowMultiConstantDefinition](doc/classes.md#slevomatcodingstandardclassesdisallowmulticonstantdefinition-) 🔧 - [SlevomatCodingStandard.Classes.DisallowMultiPropertyDefinition](doc/classes.md#slevomatcodingstandardclassesdisallowmultipropertydefinition-) 🔧 - [SlevomatCodingStandard.Classes.DisallowStringExpressionPropertyFetch](doc/classes.md#slevomatcodingstandardclassesdisallowstringexpressionpropertyfetch-) 🔧 - [SlevomatCodingStandard.Classes.EmptyLinesAroundClassBraces](doc/classes.md#slevomatcodingstandardclassesemptylinesaroundclassbraces-) 🔧 - [SlevomatCodingStandard.Classes.EnumCaseSpacing](doc/classes.md#slevomatcodingstandardclassesenumcasespacing-) 🔧 - [SlevomatCodingStandard.Classes.ForbiddenPublicProperty](doc/classes.md#slevomatcodingstandardclassesforbiddenpublicproperty) - [SlevomatCodingStandard.Classes.MethodSpacing](doc/classes.md#slevomatcodingstandardclassesmethodspacing-) 🔧 - [SlevomatCodingStandard.Classes.ModernClassNameReference](doc/classes.md#slevomatcodingstandardclassesmodernclassnamereference-) 🔧 - [SlevomatCodingStandard.Classes.ParentCallSpacing](doc/classes.md#slevomatcodingstandardclassesparentcallspacing-) 🔧 - [SlevomatCodingStandard.Classes.PropertyDeclaration](doc/classes.md#slevomatcodingstandardclassespropertydeclaration-) 🔧 - [SlevomatCodingStandard.Classes.PropertySpacing](doc/classes.md#slevomatcodingstandardclassespropertyspacing-) 🔧 - [SlevomatCodingStandard.Classes.RequireAbstractOrFinal](doc/classes.md#slevomatcodingstandardclassesrequireabstractorfinal-) 🔧 - [SlevomatCodingStandard.Classes.RequireConstructorPropertyPromotion](doc/classes.md#slevomatcodingstandardclassesrequireconstructorpropertypromotion-) 🔧 - [SlevomatCodingStandard.Classes.RequireMultiLineMethodSignature](doc/classes.md#slevomatcodingstandardclassesrequiremultilinemethodsignature-) 🔧 - [SlevomatCodingStandard.Classes.RequireSelfReference](doc/classes.md#slevomatcodingstandardclassesrequireselfreference-) 🔧 - [SlevomatCodingStandard.Classes.RequireSingleLineMethodSignature](doc/classes.md#slevomatcodingstandardclassesrequiresinglelinemethodsignature-) 🔧 - [SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming](doc/classes.md#slevomatcodingstandardclassessuperfluousabstractclassnaming) - [SlevomatCodingStandard.Classes.SuperfluousErrorNaming](doc/classes.md#slevomatcodingstandardclassessuperfluouserrornaming) - [SlevomatCodingStandard.Classes.SuperfluousExceptionNaming](doc/classes.md#slevomatcodingstandardclassessuperfluousexceptionnaming) - [SlevomatCodingStandard.Classes.SuperfluousInterfaceNaming](doc/classes.md#slevomatcodingstandardclassessuperfluousinterfacenaming) - [SlevomatCodingStandard.Classes.SuperfluousTraitNaming](doc/classes.md#slevomatcodingstandardclassessuperfluoustraitnaming) - [SlevomatCodingStandard.Classes.TraitUseDeclaration](doc/classes.md#slevomatcodingstandardclassestraitusedeclaration-) 🔧 - [SlevomatCodingStandard.Classes.TraitUseSpacing](doc/classes.md#slevomatcodingstandardclassestraitusespacing-) 🔧 - [SlevomatCodingStandard.Classes.UselessLateStaticBinding](doc/classes.md#slevomatcodingstandardclassesuselesslatestaticbinding-) 🔧 - [SlevomatCodingStandard.Commenting.AnnotationName](doc/commenting.md#slevomatcodingstandardcommentingannotationname-) - [SlevomatCodingStandard.Commenting.DeprecatedAnnotationDeclaration](doc/commenting.md#slevomatcodingstandardcommentingdeprecatedannotationdeclaration) - [SlevomatCodingStandard.Commenting.DisallowCommentAfterCode](doc/commenting.md#slevomatcodingstandardcommentingdisallowcommentaftercode-) 🔧 - [SlevomatCodingStandard.Commenting.DisallowOneLinePropertyDocComment](doc/commenting.md#slevomatcodingstandardcommentingdisallowonelinepropertydoccomment-) 🔧 - [SlevomatCodingStandard.Commenting.DocCommentSpacing](doc/commenting.md#slevomatcodingstandardcommentingdoccommentspacing-) 🔧 - [SlevomatCodingStandard.Commenting.EmptyComment](doc/commenting.md#slevomatcodingstandardcommentingemptycomment-) 🔧 - [SlevomatCodingStandard.Commenting.ForbiddenAnnotations](doc/commenting.md#slevomatcodingstandardcommentingforbiddenannotations-) 🔧 - [SlevomatCodingStandard.Commenting.ForbiddenComments](doc/commenting.md#slevomatcodingstandardcommentingforbiddencomments-) 🔧 - [SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration](doc/commenting.md#slevomatcodingstandardcommentinginlinedoccommentdeclaration-) 🔧 - [SlevomatCodingStandard.Commenting.RequireOneLineDocComment](doc/commenting.md#slevomatcodingstandardcommentingrequireonelinedoccomment-) 🔧 - [SlevomatCodingStandard.Commenting.RequireOneLinePropertyDocComment](doc/commenting.md#slevomatcodingstandardcommentingrequireonelinepropertydoccomment-) 🔧 - [SlevomatCodingStandard.Commenting.UselessFunctionDocComment](doc/commenting.md#slevomatcodingstandardcommentinguselessfunctiondoccomment-) 🔧 - [SlevomatCodingStandard.Commenting.UselessInheritDocComment](doc/commenting.md#slevomatcodingstandardcommentinguselessinheritdoccomment-) 🔧 - [SlevomatCodingStandard.Complexity.Cognitive](doc/complexity.md#slevomatcodingstandardcomplexitycognitive) - [SlevomatCodingStandard.ControlStructures.AssignmentInCondition](doc/control-structures.md#slevomatcodingstandardcontrolstructuresassignmentincondition) - [SlevomatCodingStandard.ControlStructures.BlockControlStructureSpacing](doc/control-structures.md#slevomatcodingstandardcontrolstructuresblockcontrolstructurespacing-) 🔧 - [SlevomatCodingStandard.ControlStructures.DisallowContinueWithoutIntegerOperandInSwitch](doc/control-structures.md#slevomatcodingstandardcontrolstructuresdisallowcontinuewithoutintegeroperandinswitch-) 🔧 - [SlevomatCodingStandard.ControlStructures.DisallowEmpty](doc/control-structures.md#slevomatcodingstandardcontrolstructuresdisallowempty) - [SlevomatCodingStandard.ControlStructures.DisallowNullSafeObjectOperator](doc/control-structures.md#slevomatcodingstandardcontrolstructuresdisallownullsafeobjectoperator) - [SlevomatCodingStandard.ControlStructures.DisallowShortTernaryOperator](doc/control-structures.md#slevomatcodingstandardcontrolstructuresdisallowshortternaryoperator-) 🔧 - [SlevomatCodingStandard.ControlStructures.DisallowTrailingMultiLineTernaryOperatorSniff](doc/control-structures.md#slevomatcodingstandardcontrolstructuresdisallowtrailingmultilineternaryoperator-) 🔧 - [SlevomatCodingStandard.ControlStructures.DisallowYodaComparison](doc/control-structures.md#slevomatcodingstandardcontrolstructuresdisallowyodacomparison-) 🔧 - [SlevomatCodingStandard.ControlStructures.EarlyExit](doc/control-structures.md#slevomatcodingstandardcontrolstructuresearlyexit-) 🔧 - [SlevomatCodingStandard.ControlStructures.JumpStatementsSpacing](doc/control-structures.md#slevomatcodingstandardcontrolstructuresjumpstatementsspacing-) 🔧 - [SlevomatCodingStandard.ControlStructures.LanguageConstructWithParentheses](doc/control-structures.md#slevomatcodingstandardcontrolstructureslanguageconstructwithparentheses-) 🔧 - [SlevomatCodingStandard.ControlStructures.NewWithParentheses](doc/control-structures.md#slevomatcodingstandardcontrolstructuresnewwithparentheses-) 🔧 - [SlevomatCodingStandard.ControlStructures.NewWithoutParentheses](doc/control-structures.md#slevomatcodingstandardcontrolstructuresnewwithoutparentheses-) 🔧 - [SlevomatCodingStandard.ControlStructures.RequireMultiLineCondition](doc/control-structures.md#slevomatcodingstandardcontrolstructuresrequiremultilinecondition-) 🔧 - [SlevomatCodingStandard.ControlStructures.RequireMultiLineTernaryOperator](doc/control-structures.md#slevomatcodingstandardcontrolstructuresrequiremultilineternaryoperator-) 🔧 - [SlevomatCodingStandard.ControlStructures.RequireNullCoalesceEqualOperator](doc/control-structures.md#slevomatcodingstandardcontrolstructuresrequirenullcoalesceequaloperator-) 🔧 - [SlevomatCodingStandard.ControlStructures.RequireNullCoalesceOperator](doc/control-structures.md#slevomatcodingstandardcontrolstructuresrequirenullcoalesceoperator-) 🔧 - [SlevomatCodingStandard.ControlStructures.RequireNullSafeObjectOperator](doc/control-structures.md#slevomatcodingstandardcontrolstructuresrequirenullsafeobjectoperator-) 🔧 - [SlevomatCodingStandard.ControlStructures.RequireShortTernaryOperator](doc/control-structures.md#slevomatcodingstandardcontrolstructuresrequireshortternaryoperator-) 🔧 - [SlevomatCodingStandard.ControlStructures.RequireSingleLineCondition](doc/control-structures.md#slevomatcodingstandardcontrolstructuresrequiresinglelinecondition-) 🔧 - [SlevomatCodingStandard.ControlStructures.RequireTernaryOperator](doc/control-structures.md#slevomatcodingstandardcontrolstructuresrequireternaryoperator-) 🔧 - [SlevomatCodingStandard.ControlStructures.RequireYodaComparison](doc/control-structures.md#slevomatcodingstandardcontrolstructuresrequireyodacomparison-) 🔧 - [SlevomatCodingStandard.ControlStructures.UselessIfConditionWithReturn](doc/control-structures.md#slevomatcodingstandardcontrolstructuresuselessifconditionwithreturn-) 🔧 - [SlevomatCodingStandard.ControlStructures.UselessTernaryOperator](doc/control-structures.md#slevomatcodingstandardcontrolstructuresuselessternaryoperator-) 🔧 - [SlevomatCodingStandard.Exceptions.DeadCatch](doc/exceptions.md#slevomatcodingstandardexceptionsdeadcatch) - [SlevomatCodingStandard.Exceptions.DisallowNonCapturingCatch](doc/exceptions.md#slevomatcodingstandardexceptionsdisallownoncapturingcatch) - [SlevomatCodingStandard.Exceptions.ReferenceThrowableOnly](doc/exceptions.md#slevomatcodingstandardexceptionsreferencethrowableonly-) 🔧🚧 - [SlevomatCodingStandard.Exceptions.RequireNonCapturingCatch](doc/exceptions.md#slevomatcodingstandardexceptionsrequirenoncapturingcatch-) 🔧 - [SlevomatCodingStandard.Files.FileLength](doc/files.md#slevomatcodingstandardfilesfilelength) - [SlevomatCodingStandard.Files.LineLength](doc/files.md#slevomatcodingstandardfileslinelength) - [SlevomatCodingStandard.Files.TypeNameMatchesFileName](doc/files.md#slevomatcodingstandardfilestypenamematchesfilename) - [SlevomatCodingStandard.Functions.ArrowFunctionDeclaration](doc/functions.md#slevomatcodingstandardfunctionsarrowfunctiondeclaration-) 🔧 - [SlevomatCodingStandard.Functions.DisallowArrowFunction](doc/functions.md#slevomatcodingstandardfunctionsdisallowarrowfunction) - [SlevomatCodingStandard.Functions.DisallowEmptyFunction](doc/functions.md#slevomatcodingstandardfunctionsdisallowemptyfunction) - [SlevomatCodingStandard.Functions.DisallowNamedArguments](doc/functions.md#slevomatcodingstandardfunctionsdisallownamedarguments) - [SlevomatCodingStandard.Functions.DisallowTrailingCommaInCall](doc/functions.md#slevomatcodingstandardfunctionsdisallowtrailingcommaincall-) 🔧 - [SlevomatCodingStandard.Functions.DisallowTrailingCommaInClosureUse](doc/functions.md#slevomatcodingstandardfunctionsdisallowtrailingcommainclosureuse-) 🔧 - [SlevomatCodingStandard.Functions.DisallowTrailingCommaInDeclaration](doc/functions.md#slevomatcodingstandardfunctionsdisallowtrailingcommaindeclaration-) 🔧 - [SlevomatCodingStandard.Functions.FunctionLength](doc/functions.md#slevomatcodingstandardfunctionsfunctionlength) - [SlevomatCodingStandard.Functions.NamedArgumentSpacing](doc/functions.md#slevomatcodingstandardfunctionsnamedargumentspacing-) 🔧 - [SlevomatCodingStandard.Functions.RequireArrowFunction](doc/functions.md#slevomatcodingstandardfunctionsrequirearrowfunction-) 🔧 - [SlevomatCodingStandard.Functions.RequireMultiLineCall](doc/functions.md#slevomatcodingstandardfunctionsrequiremultilinecall-) 🔧 - [SlevomatCodingStandard.Functions.RequireSingleLineCall](doc/functions.md#slevomatcodingstandardfunctionsrequiresinglelinecall-) 🔧 - [SlevomatCodingStandard.Functions.RequireTrailingCommaInCall](doc/functions.md#slevomatcodingstandardfunctionsrequiretrailingcommaincall-) 🔧 - [SlevomatCodingStandard.Functions.RequireTrailingCommaInClosureUse](doc/functions.md#slevomatcodingstandardfunctionsrequiretrailingcommainclosureuse-) 🔧 - [SlevomatCodingStandard.Functions.RequireTrailingCommaInDeclaration](doc/functions.md#slevomatcodingstandardfunctionsrequiretrailingcommaindeclaration-) 🔧 - [SlevomatCodingStandard.Functions.StaticClosure](doc/functions.md#slevomatcodingstandardfunctionsstaticclosure-) 🔧 - [SlevomatCodingStandard.Functions.StrictCall](doc/functions.md#slevomatcodingstandardfunctionsstrictcall) - [SlevomatCodingStandard.Functions.UnusedInheritedVariablePassedToClosure](doc/functions.md#slevomatcodingstandardfunctionsunusedinheritedvariablepassedtoclosure-) 🔧 - [SlevomatCodingStandard.Functions.UnusedParameter](doc/functions.md#slevomatcodingstandardfunctionsunusedparameter-) 🚧 - [SlevomatCodingStandard.Functions.UselessParameterDefaultValue](doc/functions.md#slevomatcodingstandardfunctionsuselessparameterdefaultvalue-) 🚧 - [SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses](doc/namespaces.md#slevomatcodingstandardnamespacesalphabeticallysorteduses-) 🔧 - [SlevomatCodingStandard.Namespaces.DisallowGroupUse](doc/namespaces.md#slevomatcodingstandardnamespacesdisallowgroupuse) - [SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameInAnnotation](doc/namespaces.md#slevomatcodingstandardnamespacesfullyqualifiedclassnameinannotation-) 🔧 - [SlevomatCodingStandard.Namespaces.FullyQualifiedExceptions](doc/namespaces.md#slevomatcodingstandardnamespacesfullyqualifiedexceptions-) 🔧 - [SlevomatCodingStandard.Namespaces.FullyQualifiedGlobalConstants](doc/namespaces.md#slevomatcodingstandardnamespacesfullyqualifiedglobalconstants-) 🔧 - [SlevomatCodingStandard.Namespaces.FullyQualifiedGlobalFunctions](doc/namespaces.md#slevomatcodingstandardnamespacesfullyqualifiedglobalfunctions-) 🔧 - [SlevomatCodingStandard.Namespaces.MultipleUsesPerLine](doc/namespaces.md#slevomatcodingstandardnamespacesmultipleusesperline) - [SlevomatCodingStandard.Namespaces.NamespaceDeclaration](doc/namespaces.md#slevomatcodingstandardnamespacesnamespacedeclaration-) 🔧 - [SlevomatCodingStandard.Namespaces.NamespaceSpacing](doc/namespaces.md#slevomatcodingstandardnamespacesnamespacespacing-) 🔧 - [SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly](doc/namespaces.md#slevomatcodingstandardnamespacesreferenceusednamesonly-) 🔧 - [SlevomatCodingStandard.Namespaces.RequireOneNamespaceInFile](doc/namespaces.md#slevomatcodingstandardnamespacesrequireonenamespaceinfile) - [SlevomatCodingStandard.Namespaces.UnusedUses](doc/namespaces.md#slevomatcodingstandardnamespacesunuseduses-) 🔧 - [SlevomatCodingStandard.Namespaces.UseDoesNotStartWithBackslash](doc/namespaces.md#slevomatcodingstandardnamespacesusedoesnotstartwithbackslash-) 🔧 - [SlevomatCodingStandard.Namespaces.UseFromSameNamespace](doc/namespaces.md#slevomatcodingstandardnamespacesusefromsamenamespace-) 🔧 - [SlevomatCodingStandard.Namespaces.UseOnlyWhitelistedNamespaces](doc/namespaces.md#slevomatcodingstandardnamespacesuseonlywhitelistednamespaces) - [SlevomatCodingStandard.Namespaces.UseSpacing](doc/namespaces.md#slevomatcodingstandardnamespacesusespacing-) 🔧 - [SlevomatCodingStandard.Namespaces.UselessAlias](doc/namespaces.md#slevomatcodingstandardnamespacesuselessalias-) 🔧 - [SlevomatCodingStandard.Numbers.DisallowNumericLiteralSeparator](doc/numbers.md#slevomatcodingstandardnumbersdisallownumericliteralseparator-) 🔧 - [SlevomatCodingStandard.Numbers.RequireNumericLiteralSeparator](doc/numbers.md#slevomatcodingstandardnumbersrequirenumericliteralseparator) - [SlevomatCodingStandard.Operators.DisallowEqualOperators](doc/operators.md#slevomatcodingstandardoperatorsdisallowequaloperators-) 🔧 - [SlevomatCodingStandard.Operators.DisallowIncrementAndDecrementOperators](doc/operators.md#slevomatcodingstandardoperatorsdisallowincrementanddecrementoperators) - [SlevomatCodingStandard.Operators.NegationOperatorSpacing](doc/operators.md#slevomatcodingstandardoperatorsnegationoperatorspacing-) 🔧 - [SlevomatCodingStandard.Operators.RequireCombinedAssignmentOperator](doc/operators.md#slevomatcodingstandardoperatorsrequirecombinedassignmentoperator-) 🔧 - [SlevomatCodingStandard.Operators.RequireOnlyStandaloneIncrementAndDecrementOperators](doc/operators.md#slevomatcodingstandardoperatorsrequireonlystandaloneincrementanddecrementoperators) - [SlevomatCodingStandard.Operators.SpreadOperatorSpacing](doc/operators.md#slevomatcodingstandardoperatorsspreadoperatorspacing-) 🔧 - [SlevomatCodingStandard.PHP.DisallowDirectMagicInvokeCall](doc/php.md#slevomatcodingstandardphpdisallowdirectmagicinvokecall-) 🔧 - [SlevomatCodingStandard.PHP.DisallowReference](doc/php.md#slevomatcodingstandardphpdisallowreference) - [SlevomatCodingStandard.PHP.ForbiddenClasses](doc/php.md#slevomatcodingstandardphpforbiddenclasses-) 🔧 - [SlevomatCodingStandard.PHP.OptimizedFunctionsWithoutUnpacking](doc/php.md#slevomatcodingstandardphpoptimizedfunctionswithoutunpacking) - [SlevomatCodingStandard.PHP.ReferenceSpacing](doc/php.md#slevomatcodingstandardphpreferencespacing-) 🔧 - [SlevomatCodingStandard.PHP.RequireExplicitAssertion](doc/php.md#slevomatcodingstandardphprequireexplicitassertion-) 🔧 - [SlevomatCodingStandard.PHP.RequireNowdoc](doc/php.md#slevomatcodingstandardphprequirenowdoc-) 🔧 - [SlevomatCodingStandard.PHP.ShortList](doc/php.md#slevomatcodingstandardphpshortlist-) 🔧 - [SlevomatCodingStandard.PHP.TypeCast](doc/php.md#slevomatcodingstandardphptypecast-) 🔧 - [SlevomatCodingStandard.PHP.UselessParentheses](doc/php.md#slevomatcodingstandardphpuselessparentheses-) 🔧 - [SlevomatCodingStandard.PHP.UselessSemicolon](doc/php.md#slevomatcodingstandardphpuselesssemicolon-) 🔧 - [SlevomatCodingStandard.Strings.DisallowVariableParsing](doc/strings.md#slevomatcodingstandardstringsdisallowvariableparsing) - [SlevomatCodingStandard.TypeHints.ClassConstantTypeHint](doc/type-hints.md#slevomatcodingstandardtypehintsclassconstanttypehint-) 🔧 - [SlevomatCodingStandard.TypeHints.DeclareStrictTypes](doc/type-hints.md#slevomatcodingstandardtypehintsdeclarestricttypes-) 🔧 - [SlevomatCodingStandard.TypeHints.DisallowArrayTypeHintSyntax](doc/type-hints.md#slevomatcodingstandardtypehintsdisallowarraytypehintsyntax-) 🔧 - [SlevomatCodingStandard.TypeHints.DisallowMixedTypeHint](doc/type-hints.md#slevomatcodingstandardtypehintsdisallowmixedtypehint) - [SlevomatCodingStandard.TypeHints.DNFTypeHintFormat](doc/type-hints.md#slevomatcodingstandardtypehintsdnftypehintformat-) 🔧 - [SlevomatCodingStandard.TypeHints.LongTypeHints](doc/type-hints.md#slevomatcodingstandardtypehintslongtypehints-) 🔧 - [SlevomatCodingStandard.TypeHints.NullTypeHintOnLastPosition](doc/type-hints.md#slevomatcodingstandardtypehintsnulltypehintonlastposition-) 🔧 - [SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue](doc/type-hints.md#slevomatcodingstandardtypehintsnullabletypefornulldefaultvalue-) 🔧🚧 - [SlevomatCodingStandard.TypeHints.ParameterTypeHint](doc/type-hints.md#slevomatcodingstandardtypehintsparametertypehint-) 🔧🚧 - [SlevomatCodingStandard.TypeHints.ParameterTypeHintSpacing](doc/type-hints.md#slevomatcodingstandardtypehintsparametertypehintspacing-) 🔧 - [SlevomatCodingStandard.TypeHints.PropertyTypeHint](doc/type-hints.md#slevomatcodingstandardtypehintspropertytypehint-) 🔧🚧 - [SlevomatCodingStandard.TypeHints.ReturnTypeHint](doc/type-hints.md#slevomatcodingstandardtypehintsreturntypehint-) 🔧🚧 - [SlevomatCodingStandard.TypeHints.ReturnTypeHintSpacing](doc/type-hints.md#slevomatcodingstandardtypehintsreturntypehintspacing-) 🔧 - [SlevomatCodingStandard.TypeHints.UnionTypeHintFormat](doc/type-hints.md#slevomatcodingstandardtypehintsuniontypehintformat-) 🔧 - [SlevomatCodingStandard.TypeHints.UselessConstantTypeHint](doc/type-hints.md#slevomatcodingstandardtypehintsuselessconstanttypehint-) 🔧 - [SlevomatCodingStandard.Variables.DisallowVariableVariable](doc/variables.md#slevomatcodingstandardvariablesdisallowvariablevariable) - [SlevomatCodingStandard.Variables.DisallowSuperGlobalVariable](doc/variables.md#slevomatcodingstandardvariablesdisallowsuperglobalvariable) - [SlevomatCodingStandard.Variables.DuplicateAssignmentToVariable](doc/variables.md#slevomatcodingstandardvariablesduplicateassignmenttovariable) - [SlevomatCodingStandard.Variables.UnusedVariable](doc/variables.md#slevomatcodingstandardvariablesunusedvariable) - [SlevomatCodingStandard.Variables.UselessVariable](doc/variables.md#slevomatcodingstandardvariablesuselessvariable-) 🔧 - [SlevomatCodingStandard.Whitespaces.DuplicateSpaces](doc/whitespaces.md#slevomatcodingstandardwhitespacesduplicatespaces-) 🔧 ## Installation The recommended way to install Slevomat Coding Standard is [through Composer](http://getcomposer.org). ```JSON { "require-dev": { "slevomat/coding-standard": "~8.0" } } ``` It's also recommended to install [php-parallel-lint/php-parallel-lint](https://github.com/php-parallel-lint/PHP-Parallel-Lint) which checks source code for syntax errors. Sniffs count on the processed code to be syntactically valid (no parse errors), otherwise they can behave unexpectedly. It is advised to run `PHP-Parallel-Lint` in your build tool before running `PHP_CodeSniffer` and exiting the build process early if `PHP-Parallel-Lint` fails. ## How to run the sniffs You can choose one of two ways to run only selected sniffs from the standard on your codebase: ### Choose which sniffs to run The recommended way is to write your own ruleset.xml by referencing only the selected sniffs. This is a sample ruleset.xml: ```xml ``` Then run the `phpcs` executable the usual way: ``` vendor/bin/phpcs --standard=ruleset.xml --extensions=php --tab-width=4 -sp src tests ``` ### Exclude sniffs you don't want to run You can also mention Slevomat Coding Standard in your project's `ruleset.xml` and exclude only some sniffs: ```xml ``` However it is not a recommended way to use Slevomat Coding Standard, because your build can break when moving between minor versions of the standard (which can happen if you use `^` or `~` version constraint in `composer.json`). We regularly add new sniffs even in minor versions meaning your code won't most likely comply with new minor versions of the package. ## Fixing errors automatically Sniffs in this standard marked by the 🔧 symbol support [automatic fixing of coding standard violations](https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki/Fixing-Errors-Automatically). To fix your code automatically, run phpcbf instead of phpcs: ``` vendor/bin/phpcbf --standard=ruleset.xml --extensions=php --tab-width=4 -sp src tests ``` Always remember to back up your code before performing automatic fixes and check the results with your own eyes as the automatic fixer can sometimes produce unwanted results. ## Suppressing sniffs locally Selected sniffs in this standard marked by the 🚧 symbol can be suppressed for a specific piece of code using an annotation. Consider the following example: ```php /** * @param int $max */ public function createProgressBar($max = 0): ProgressBar { } ``` The parameter `$max` could have a native `int` scalar typehint. But because the method in the parent class does not have this typehint, so this one cannot have it either. PHP_CodeSniffer shows a following error: ``` ---------------------------------------------------------------------- FOUND 1 ERROR AFFECTING 1 LINE ---------------------------------------------------------------------- 67 | ERROR | [x] Method ErrorsConsoleStyle::createProgressBar() | | does not have native type hint for its parameter $max | | but it should be possible to add it based on @param | | annotation "int". | | (SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint) ``` If we want to suppress this error instead of fixing it, we can take the error code (`SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint`) and use it with a `@phpcsSuppress` annotation like this: ```php /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $max */ public function createProgressBar($max = 0): ProgressBar { } ``` ## Contributing To make this repository work on your machine, clone it and run these two commands in the root directory of the repository: ``` composer install bin/phing ``` After writing some code and editing or adding unit tests, run phing again to check that everything is OK: ``` bin/phing ``` We are always looking forward to your bugreports, feature requests and pull requests. Thank you. ## Code of Conduct This project adheres to a [Contributor Code of Conduct](https://github.com/slevomat/coding-standard/blob/master/CODE_OF_CONDUCT.md). By participating in this project and its community, you are expected to uphold this code. PK41]6Jd##&coding-standard/autoload-bootstrap.phpnu[Qcoding-standard/SlevomatCodingStandard/Sniffs/Attributes/AttributesOrderSniff.phpnu[PK41]`77[,'coding-standard/SlevomatCodingStandard/Sniffs/Attributes/DisallowAttributesJoiningSniff.phpnu[PK41]ra&<[.coding-standard/SlevomatCodingStandard/Sniffs/Attributes/AttributeAndTargetSpacingSniff.phpnu[PK41]<  a=coding-standard/SlevomatCodingStandard/Sniffs/Attributes/RequireAttributeAfterDocCommentSniff.phpnu[PK41][XbPaaW!Hcoding-standard/SlevomatCodingStandard/Sniffs/Operators/DisallowEqualOperatorsSniff.phpnu[PK41]gC)t Ocoding-standard/SlevomatCodingStandard/Sniffs/Operators/RequireOnlyStandaloneIncrementAndDecrementOperatorsSniff.phpnu[PK41],=//b_coding-standard/SlevomatCodingStandard/Sniffs/Operators/RequireCombinedAssignmentOperatorSniff.phpnu[PK41] 1  Vtpcoding-standard/SlevomatCodingStandard/Sniffs/Operators/SpreadOperatorSpacingSniff.phpnu[PK41]V8: : Xycoding-standard/SlevomatCodingStandard/Sniffs/Operators/NegationOperatorSpacingSniff.phpnu[PK41]o2  gɄcoding-standard/SlevomatCodingStandard/Sniffs/Operators/DisallowIncrementAndDecrementOperatorsSniff.phpnu[PK41]#RggRjcoding-standard/SlevomatCodingStandard/Sniffs/Whitespaces/DuplicateSpacesSniff.phpnu[PK41] RgRScoding-standard/SlevomatCodingStandard/Sniffs/Files/FilepathNamespaceExtractor.phpnu[PK41] ::Gcoding-standard/SlevomatCodingStandard/Sniffs/Files/FileLengthSniff.phpnu[PK41]pYTqcoding-standard/SlevomatCodingStandard/Sniffs/Files/TypeNameMatchesFileNameSniff.phpnu[PK41]= Gcoding-standard/SlevomatCodingStandard/Sniffs/Files/LineLengthSniff.phpnu[PK41]JL??bRcoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/AbstractFullyQualifiedGlobalReference.phpnu[PK41]wJB+B+L#coding-standard/SlevomatCodingStandard/Sniffs/Namespaces/UseSpacingSniff.phpnu[PK41])*$$L coding-standard/SlevomatCodingStandard/Sniffs/Namespaces/UnusedUsesSniff.phpnu[PK41]V22Z1coding-standard/SlevomatCodingStandard/Sniffs/Namespaces/FullyQualifiedExceptionsSniff.phpnu[PK41]VEcoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/UseFromSameNamespaceSniff.phpnu[PK41]!MN+Ocoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/UselessAliasSniff.phpnu[PK41]_jXcoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/FullyQualifiedGlobalFunctionsSniff.phpnu[PK41]\T[\coding-standard/SlevomatCodingStandard/Sniffs/Namespaces/RequireOneNamespaceInFileSniff.phpnu[PK41]4  Rccoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/DisallowGroupUseSniff.phpnu[PK41]=gZfcoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/AlphabeticallySortedUsesSniff.phpnu[PK41]H?2Rcoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/NamespaceSpacingSniff.phpnu[PK41]6CKffXcoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/ReferenceUsedNamesOnlySniff.phpnu[PK41]\_coding-standard/SlevomatCodingStandard/Sniffs/Namespaces/FullyQualifiedGlobalConstantsSniff.phpnu[PK41]5M^}coding-standard/SlevomatCodingStandard/Sniffs/Namespaces/UseDoesNotStartWithBackslashSniff.phpnu[PK41]MV coding-standard/SlevomatCodingStandard/Sniffs/Namespaces/NamespaceDeclarationSniff.phpnu[PK41]<܈EEecoding-standard/SlevomatCodingStandard/Sniffs/Namespaces/FullyQualifiedClassNameInAnnotationSniff.phpnu[PK41]5 tkk^0coding-standard/SlevomatCodingStandard/Sniffs/Namespaces/UseOnlyWhitelistedNamespacesSniff.phpnu[PK41]U8coding-standard/SlevomatCodingStandard/Sniffs/Namespaces/MultipleUsesPerLineSniff.phpnu[PK41]7Ҙ:=coding-standard/SlevomatCodingStandard/Sniffs/TestCase.phpnu[PK41]U[3[3R=Ycoding-standard/SlevomatCodingStandard/Sniffs/Classes/PropertyDeclarationSniff.phpnu[PK41]RddNcoding-standard/SlevomatCodingStandard/Sniffs/Classes/EnumCaseSpacingSniff.phpnu[PK41]A9" " Vcoding-standard/SlevomatCodingStandard/Sniffs/Classes/ForbiddenPublicPropertySniff.phpnu[PK41] ?({{Zcoding-standard/SlevomatCodingStandard/Sniffs/Classes/EmptyLinesAroundClassBracesSniff.phpnu[PK41]RZ Vcoding-standard/SlevomatCodingStandard/Sniffs/Classes/ClassConstantVisibilitySniff.phpnu[PK41]hqw22Xcoding-standard/SlevomatCodingStandard/Sniffs/Classes/UnsupportedClassGroupException.phpnu[PK41]jfdcoding-standard/SlevomatCodingStandard/Sniffs/Classes/DisallowStringExpressionPropertyFetchSniff.phpnu[PK41]2 dcoding-standard/SlevomatCodingStandard/Sniffs/Classes/AbstractPropertyConstantAndEnumCaseSpacing.phpnu[PK41]KJ J N8coding-standard/SlevomatCodingStandard/Sniffs/Classes/ConstantSpacingSniff.phpnu[PK41]ñ]Ycoding-standard/SlevomatCodingStandard/Sniffs/Classes/SuperfluousInterfaceNamingSniff.phpnu[PK41]\h ""Qcoding-standard/SlevomatCodingStandard/Sniffs/Classes/ClassMemberSpacingSniff.phpnu[PK41])m)mMFcoding-standard/SlevomatCodingStandard/Sniffs/Classes/ClassStructureSniff.phpnu[PK41]&Ucoding-standard/SlevomatCodingStandard/Sniffs/Classes/SuperfluousTraitNamingSniff.phpnu[PK41]ő / /bcoding-standard/SlevomatCodingStandard/Sniffs/Classes/RequireConstructorPropertyPromotionSniff.phpnu[PK41][$$Ycoding-standard/SlevomatCodingStandard/Sniffs/Classes/SuperfluousExceptionNamingSniff.phpnu[PK41]uJ>coding-standard/SlevomatCodingStandard/Sniffs/Classes/ClassLengthSniff.phpnu[PK41]K\^7coding-standard/SlevomatCodingStandard/Sniffs/Classes/RequireMultiLineMethodSignatureSniff.phpnu[PK41]O^Wcoding-standard/SlevomatCodingStandard/Sniffs/Classes/DisallowMultiPropertyDefinitionSniff.phpnu[PK41]eYu u Nicoding-standard/SlevomatCodingStandard/Sniffs/Classes/PropertySpacingSniff.phpnu[PK41]~$::]\coding-standard/SlevomatCodingStandard/Sniffs/Classes/SuperfluousAbstractClassNamingSniff.phpnu[PK41]kn| | P#coding-standard/SlevomatCodingStandard/Sniffs/Classes/ParentCallSpacingSniff.phpnu[PK41][>|Lcoding-standard/SlevomatCodingStandard/Sniffs/Classes/MethodSpacingSniff.phpnu[PK41]Jcgg^x%coding-standard/SlevomatCodingStandard/Sniffs/Classes/DisallowMultiConstantDefinitionSniff.phpnu[PK41] vWm5coding-standard/SlevomatCodingStandard/Sniffs/Classes/ModernClassNameReferenceSniff.phpnu[PK41]O<U{Icoding-standard/SlevomatCodingStandard/Sniffs/Classes/SuperfluousErrorNamingSniff.phpnu[PK41],UOcoding-standard/SlevomatCodingStandard/Sniffs/Classes/RequireAbstractOrFinalSniff.phpnu[PK41]+0cUcoding-standard/SlevomatCodingStandard/Sniffs/Classes/DisallowConstructorPropertyPromotionSniff.phpnu[PK41]; R]coding-standard/SlevomatCodingStandard/Sniffs/Classes/TraitUseDeclarationSniff.phpnu[PK41]&:y y T9hcoding-standard/SlevomatCodingStandard/Sniffs/Classes/BackedEnumTypeSpacingSniff.phpnu[PK41] {< S6vcoding-standard/SlevomatCodingStandard/Sniffs/Classes/RequireSelfReferenceSniff.phpnu[PK41])  Qcoding-standard/SlevomatCodingStandard/Sniffs/Classes/AbstractMethodSignature.phpnu[PK41] HU*coding-standard/SlevomatCodingStandard/Sniffs/Classes/MissingClassGroupsException.phpnu[PK41]>&&N{coding-standard/SlevomatCodingStandard/Sniffs/Classes/TraitUseSpacingSniff.phpnu[PK41]Pa**d coding-standard/SlevomatCodingStandard/Sniffs/Classes/DisallowLateStaticBindingForConstantsSniff.phpnu[PK41]EZr _ǻcoding-standard/SlevomatCodingStandard/Sniffs/Classes/RequireSingleLineMethodSignatureSniff.phpnu[PK41]6*1  Wcoding-standard/SlevomatCodingStandard/Sniffs/Classes/UselessLateStaticBindingSniff.phpnu[PK41]>3 Kcoding-standard/SlevomatCodingStandard/Sniffs/PHP/ForbiddenClassesSniff.phpnu[PK41]ܻJJM.coding-standard/SlevomatCodingStandard/Sniffs/PHP/UselessParenthesesSniff.phpnu[PK41]]%X X Cz=coding-standard/SlevomatCodingStandard/Sniffs/PHP/TypeCastSniff.phpnu[PK41]%n.j j LEHcoding-standard/SlevomatCodingStandard/Sniffs/PHP/DisallowReferenceSniff.phpnu[PK41]6/K+Vcoding-standard/SlevomatCodingStandard/Sniffs/PHP/UselessSemicolonSniff.phpnu[PK41]ZFHgcoding-standard/SlevomatCodingStandard/Sniffs/PHP/RequireNowdocSniff.phpnu[PK41]~؄Xpcoding-standard/SlevomatCodingStandard/Sniffs/PHP/DisallowDirectMagicInvokeCallSniff.phpnu[PK41][E//D wcoding-standard/SlevomatCodingStandard/Sniffs/PHP/ShortListSniff.phpnu[PK41]ywV]|coding-standard/SlevomatCodingStandard/Sniffs/PHP/OptimizedFunctionsWithoutUnpackingSniff.phpnu[PK41]K$coding-standard/SlevomatCodingStandard/Sniffs/PHP/ReferenceSpacingSniff.phpnu[PK41]#ӕ88Scoding-standard/SlevomatCodingStandard/Sniffs/PHP/RequireExplicitAssertionSniff.phpnu[PK41]dzieeVcoding-standard/SlevomatCodingStandard/Sniffs/Strings/DisallowVariableParsingSniff.phpnu[PK41]u4VVRcoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/ParameterTypeHintSniff.phpnu[PK41]4p!!Sb<coding-standard/SlevomatCodingStandard/Sniffs/TypeHints/DeclareStrictTypesSniff.phpnu[PK41]2t}}V^coding-standard/SlevomatCodingStandard/Sniffs/TypeHints/ClassConstantTypeHintSniff.phpnu[PK41]6Є++Rxcoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/DNFTypeHintFormatSniff.phpnu[PK41]LYcoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/ParameterTypeHintSpacingSniff.phpnu[PK41]~! ! Ncoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/LongTypeHintsSniff.phpnu[PK41] [coding-standard/SlevomatCodingStandard/Sniffs/TypeHints/NullTypeHintOnLastPositionSniff.phpnu[PK41]R m!!\coding-standard/SlevomatCodingStandard/Sniffs/TypeHints/DisallowArrayTypeHintSyntaxSniff.phpnu[PK41]'TTQcoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/PropertyTypeHintSniff.phpnu[PK41]^ӎVnDcoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/DisallowMixedTypeHintSniff.phpnu[PK41]^+VMcoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/ReturnTypeHintSpacingSniff.phpnu[PK41]`4 4 Tecoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/UnionTypeHintFormatSniff.phpnu[PK41] `^coding-standard/SlevomatCodingStandard/Sniffs/TypeHints/NullableTypeForNullDefaultValueSniff.phpnu[PK41]-q1]]Ocoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/ReturnTypeHintSniff.phpnu[PK41]vM M Xcoding-standard/SlevomatCodingStandard/Sniffs/TypeHints/UselessConstantTypeHintSniff.phpnu[PK41]|t??[coding-standard/SlevomatCodingStandard/Sniffs/Exceptions/DisallowNonCapturingCatchSniff.phpnu[PK41]o..Zrcoding-standard/SlevomatCodingStandard/Sniffs/Exceptions/RequireNonCapturingCatchSniff.phpnu[PK41]ԤMK*coding-standard/SlevomatCodingStandard/Sniffs/Exceptions/DeadCatchSniff.phpnu[PK41]~"@@X<coding-standard/SlevomatCodingStandard/Sniffs/Exceptions/ReferenceThrowableOnlySniff.phpnu[PK41]ㄧ^+coding-standard/SlevomatCodingStandard/Sniffs/Numbers/DisallowNumericLiteralSeparatorSniff.phpnu[PK41]Pytt]0coding-standard/SlevomatCodingStandard/Sniffs/Numbers/RequireNumericLiteralSeparatorSniff.phpnu[PK41]w//\8coding-standard/SlevomatCodingStandard/Sniffs/Variables/DisallowSuperGlobalVariableSniff.phpnu[PK41] ~~^h=coding-standard/SlevomatCodingStandard/Sniffs/Variables/DuplicateAssignmentToVariableSniff.phpnu[PK41]On'%O%OOtEcoding-standard/SlevomatCodingStandard/Sniffs/Variables/UnusedVariableSniff.phpnu[PK41]7-.-.Pcoding-standard/SlevomatCodingStandard/Sniffs/Variables/UselessVariableSniff.phpnu[PK41] ((Ycoding-standard/SlevomatCodingStandard/Sniffs/Variables/DisallowVariableVariableSniff.phpnu[PK41]Q![vcoding-standard/SlevomatCodingStandard/Sniffs/Commenting/UselessFunctionDocCommentSniff.phpnu[PK41]4Zcoding-standard/SlevomatCodingStandard/Sniffs/Commenting/DisallowCommentAfterCodeSniff.phpnu[PK41]prř cqcoding-standard/SlevomatCodingStandard/Sniffs/Commenting/DisallowOneLinePropertyDocCommentSniff.phpnu[PK41]72ZddScoding-standard/SlevomatCodingStandard/Sniffs/Commenting/DocCommentSpacingSniff.phpnu[PK41]o88Z[ coding-standard/SlevomatCodingStandard/Sniffs/Commenting/RequireOneLineDocCommentSniff.phpnu[PK41]L..a^ coding-standard/SlevomatCodingStandard/Sniffs/Commenting/DeprecatedAnnotationDeclarationSniff.phpnu[PK41]bed coding-standard/SlevomatCodingStandard/Sniffs/Commenting/RequireOneLinePropertyDocCommentSniff.phpnu[PK41]jNl coding-standard/SlevomatCodingStandard/Sniffs/Commenting/EmptyCommentSniff.phpnu[PK41]"JP coding-standard/SlevomatCodingStandard/Sniffs/Commenting/AnnotationNameSniff.phpnu[PK41]؂B22]2 coding-standard/SlevomatCodingStandard/Sniffs/Commenting/AbstractRequireOneLineDocComment.phpnu[PK41]77] coding-standard/SlevomatCodingStandard/Sniffs/Commenting/InlineDocCommentDeclarationSniff.phpnu[PK41]NNVh coding-standard/SlevomatCodingStandard/Sniffs/Commenting/ForbiddenAnnotationsSniff.phpnu[PK41]TJS< coding-standard/SlevomatCodingStandard/Sniffs/Commenting/ForbiddenCommentsSniff.phpnu[PK41]P ZC coding-standard/SlevomatCodingStandard/Sniffs/Commenting/UselessInheritDocCommentSniff.phpnu[PK41]8: [Q coding-standard/SlevomatCodingStandard/Sniffs/Functions/RequireTrailingCommaInCallSniff.phpnu[PK41].b coding-standard/SlevomatCodingStandard/Sniffs/Functions/RequireTrailingCommaInDeclarationSniff.phpnu[PK41] ΝW ) coding-standard/SlevomatCodingStandard/Sniffs/Functions/DisallowNamedArgumentsSniff.phpnu[PK41]y Sc0- coding-standard/SlevomatCodingStandard/Sniffs/Functions/DisallowTrailingCommaInDeclarationSniff.phpnu[PK41]7%v v K5 coding-standard/SlevomatCodingStandard/Sniffs/Functions/StrictCallSniff.phpnu[PK41]yp;;YC coding-standard/SlevomatCodingStandard/Sniffs/Functions/ArrowFunctionDeclarationSniff.phpnu[PK41]!USX coding-standard/SlevomatCodingStandard/Sniffs/Functions/RequireArrowFunctionSniff.phpnu[PK41]BXC C Ni coding-standard/SlevomatCodingStandard/Sniffs/Functions/StaticClosureSniff.phpnu[PK41]9x` ` Lt coding-standard/SlevomatCodingStandard/Sniffs/Functions/AbstractLineCall.phpnu[PK41]x^U U \b coding-standard/SlevomatCodingStandard/Sniffs/Functions/DisallowTrailingCommaInCallSniff.phpnu[PK41]~zVC coding-standard/SlevomatCodingStandard/Sniffs/Functions/DisallowArrowFunctionSniff.phpnu[PK41]] coding-standard/SlevomatCodingStandard/Sniffs/Functions/UselessParameterDefaultValueSniff.phpnu[PK41]F0a˖ coding-standard/SlevomatCodingStandard/Sniffs/Functions/RequireTrailingCommaInClosureUseSniff.phpnu[PK41]}cp!!U0 coding-standard/SlevomatCodingStandard/Sniffs/Functions/RequireMultiLineCallSniff.phpnu[PK41]o P־ coding-standard/SlevomatCodingStandard/Sniffs/Functions/UnusedParameterSniff.phpnu[PK41]{433gO coding-standard/SlevomatCodingStandard/Sniffs/Functions/UnusedInheritedVariablePassedToClosureSniff.phpnu[PK41]`""V coding-standard/SlevomatCodingStandard/Sniffs/Functions/DisallowEmptyFunctionSniff.phpnu[PK41](jU coding-standard/SlevomatCodingStandard/Sniffs/Functions/NamedArgumentSpacingSniff.phpnu[PK41]dexxO coding-standard/SlevomatCodingStandard/Sniffs/Functions/FunctionLengthSniff.phpnu[PK41]?7~~V coding-standard/SlevomatCodingStandard/Sniffs/Functions/RequireSingleLineCallSniff.phpnu[PK41]Ɛ .P P b coding-standard/SlevomatCodingStandard/Sniffs/Functions/DisallowTrailingCommaInClosureUseSniff.phpnu[PK41]JтN N c coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/RequireSingleLineConditionSniff.phpnu[PK41]/!q66R coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/EarlyExitSniff.phpnu[PK41]fCk_V coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/DisallowYodaComparisonSniff.phpnu[PK41]n' _c` coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/UselessTernaryOperatorSniff.phpnu[PK41]š ^{m coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/NewWithoutParenthesesSniff.phpnu[PK41]F pp_w coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/UnsupportedKeywordException.phpnu[PK41]hD qy coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/DisallowTrailingMultiLineTernaryOperatorSniff.phpnu[PK41]w,aP P i% coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/LanguageConstructWithParenthesesSniff.phpnu[PK41]f]EEc coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/AbstractControlStructureSpacing.phpnu[PK41][H[[e coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/DisallowShortTernaryOperatorSniff.phpnu[PK41]i^ coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/JumpStatementsSpacingSniff.phpnu[PK41]W3a#a#_ coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/RequireTernaryOperatorSniff.phpnu[PK41]D ^$ coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/AssignmentInConditionSniff.phpnu[PK41]0))e1 coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/UselessIfConditionWithReturnSniff.phpnu[PK41]3X[6 eG coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/BlockControlStructureSpacingSniff.phpnu[PK41]b'T coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/RequireMultiLineConditionSniff.phpnu[PK41]*^l coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/RequireYodaComparisonSniff.phpnu[PK41]X [u coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/NewWithParenthesesSniff.phpnu[PK41]]h2 coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/RequireMultiLineTernaryOperatorSniff.phpnu[PK41]3qqvl coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/DisallowContinueWithoutIntegerOperandInSwitchSniff.phpnu[PK41] @d coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/RequireNullCoalesceOperatorSniff.phpnu[PK41]>RAAf coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/RequireNullSafeObjectOperatorSniff.phpnu[PK41]o22V coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/DisallowEmptySniff.phpnu[PK41]BFid coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/RequireNullCoalesceEqualOperatorSniff.phpnu[PK41]00Y coding-standard/SlevomatCodingStandard/Sniffs/ControlStructures/AbstractLineCondition.phpnu[PK41]\ўcoding-standard/SlevomatCodingStandard/Helpers/TokenHelper.phpnu[PK41]@ JJAcoding-standard/SlevomatCodingStandard/Helpers/ReferencedName.phpnu[PK41]ZN;;Eycoding-standard/SlevomatCodingStandard/Helpers/PhpDocParserHelper.phpnu[PK41]Ԡ7B7BA)coding-standard/SlevomatCodingStandard/Helpers/FunctionHelper.phpnu[PK41];""C!coding-standard/SlevomatCodingStandard/Helpers/AnnotationHelper.phpnu[PK41]Q>Dcoding-standard/SlevomatCodingStandard/Helpers/ScopeHelper.phpnu[PK41]\((GIKcoding-standard/SlevomatCodingStandard/Helpers/AnnotationTypeHelper.phpnu[PK41]_>\tcoding-standard/SlevomatCodingStandard/Helpers/ArrayHelper.phpnu[PK41]7  CǍcoding-standard/SlevomatCodingStandard/Helpers/ParsedDocComment.phpnu[PK41] BLcoding-standard/SlevomatCodingStandard/Helpers/AttributeHelper.phpnu[PK41]MgAcoding-standard/SlevomatCodingStandard/Helpers/PropertyHelper.phpnu[PK41]ߦwwCcoding-standard/SlevomatCodingStandard/Helpers/DocCommentHelper.phpnu[PK41],  @coding-standard/SlevomatCodingStandard/Helpers/CommentHelper.phpnu[PK41]^^F.coding-standard/SlevomatCodingStandard/Helpers/IdentificatorHelper.phpnu[PK41]^[pA coding-standard/SlevomatCodingStandard/Helpers/ConstantHelper.phpnu[PK41]71Bcoding-standard/SlevomatCodingStandard/Helpers/SniffLocalCache.phpnu[PK41]KHcoding-standard/SlevomatCodingStandard/Helpers/TernaryOperatorHelper.phpnu[PK41]U* ?--coding-standard/SlevomatCodingStandard/Helpers/UseStatement.phpnu[PK41]<7coding-standard/SlevomatCodingStandard/Helpers/Attribute.phpnu[PK41]x1 =<coding-standard/SlevomatCodingStandard/Helpers/TypeHelper.phpnu[PK41]FE @coding-standard/SlevomatCodingStandard/Helpers/EmptyFileException.phpnu[PK41]a?tBcoding-standard/SlevomatCodingStandard/Helpers/StringHelper.phpnu[PK41]L:%%=Dcoding-standard/SlevomatCodingStandard/Helpers/YodaHelper.phpnu[PK41]M  ARjcoding-standard/SlevomatCodingStandard/Helpers/SuppressHelper.phpnu[PK41]G ;scoding-standard/SlevomatCodingStandard/Helpers/TypeHint.phpnu[PK41]o  >xcoding-standard/SlevomatCodingStandard/Helpers/ClassHelper.phpnu[PK41]i>coding-standard/SlevomatCodingStandard/Helpers/CatchHelper.phpnu[PK41]#}}Bcoding-standard/SlevomatCodingStandard/Helpers/ParameterHelper.phpnu[PK41]wee@coding-standard/SlevomatCodingStandard/Helpers/ArrayKeyValue.phpnu[PK41]*KL\ \ D}coding-standard/SlevomatCodingStandard/Helpers/IndentationHelper.phpnu[PK41]=!(  FMcoding-standard/SlevomatCodingStandard/Helpers/SniffSettingsHelper.phpnu[PK41]aoo=ʹcoding-standard/SlevomatCodingStandard/Helpers/Annotation.phpnu[PK41].=>coding-standard/SlevomatCodingStandard/Helpers/FixerHelper.phpnu[PK41]==coding-standard/LICENSE.mdnu[PK41]C{J7J7|coding-standard/doc/classes.mdnu[PK41]!coding-standard/doc/attributes.mdnu[PK41]L%L%)fcoding-standard/doc/control-structures.mdnu[PK41]!բoo" +coding-standard/doc/whitespaces.mdnu[PK41] Ɗqq-coding-standard/doc/numbers.mdnu[PK41]\0coding-standard/doc/strings.mdnu[PK41]**!a2coding-standard/doc/type-hints.mdnu[PK41],C~  !]coding-standard/doc/complexity.mdnu[PK41]!^coding-standard/doc/exceptions.mdnu[PK41]x٠  !ccoding-standard/doc/namespaces.mdnu[PK41]( <coding-standard/doc/functions.mdnu[PK41]!ecoding-standard/doc/commenting.mdnu[PK41]MMcoding-standard/doc/arrays.mdnu[PK41]@ 4coding-standard/doc/variables.mdnu[PK41]qq qcoding-standard/doc/operators.mdnu[PK41]Pb) ) 2coding-standard/doc/files.mdnu[PK41]^MX X coding-standard/doc/php.mdnu[PK41]y-ttIcoding-standard/.typos.tomlnu[PK41]*3coding-standard/.editorconfignu[PK41]+6 "coding-standard/CODE_OF_CONDUCT.mdnu[PK41]M8u8ucoding-standard/README.mdnu[PK41]6Jd##&^Ycoding-standard/autoload-bootstrap.phpnu[PKZ