From c0a6d57e884f47318362a01db90206fbebc927fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20Janu=CC=8A?= Date: Wed, 10 Jul 2024 13:28:12 +0200 Subject: [PATCH 1/3] PHP 8.3 presets --- preset-fixer/php83.php | 12 ++++++++++++ preset-sniffer/php83.xml | 6 ++++++ 2 files changed, 18 insertions(+) create mode 100644 preset-fixer/php83.php create mode 100644 preset-sniffer/php83.xml diff --git a/preset-fixer/php83.php b/preset-fixer/php83.php new file mode 100644 index 0000000..5d18433 --- /dev/null +++ b/preset-fixer/php83.php @@ -0,0 +1,12 @@ + true, +]; + +$config->setRules($rules + $config->getRules()); +return $config; diff --git a/preset-sniffer/php83.xml b/preset-sniffer/php83.xml new file mode 100644 index 0000000..ee325b6 --- /dev/null +++ b/preset-sniffer/php83.xml @@ -0,0 +1,6 @@ + + + + + + From 4d2ee1a215fe1758cd0441ce66ec300f359092c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20Janu=CC=8A?= Date: Thu, 11 Jul 2024 09:30:22 +0200 Subject: [PATCH 2/3] Upgrade cs-fixer and slevomat coding standard. PHP 7.4+ --- .gitignore | 1 + composer.json | 6 +- fix.php | 2 +- preset-fixer/base.php | 3 +- preset-fixer/common/Nette.php | 21 +- preset-sniffer/php83.xml | 3 + src/Fixer/CurlyBracesPositionFixer.php | 436 ----------------- src/Fixer/StatementIndentationFixer.php | 620 ------------------------ 8 files changed, 16 insertions(+), 1076 deletions(-) delete mode 100644 src/Fixer/CurlyBracesPositionFixer.php delete mode 100644 src/Fixer/StatementIndentationFixer.php diff --git a/.gitignore b/.gitignore index 8b7ef35..434ab8c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /vendor composer.lock +filelist.tmp diff --git a/composer.json b/composer.json index a109b68..4ea51a1 100644 --- a/composer.json +++ b/composer.json @@ -2,9 +2,9 @@ "name": "nette/coding-standard", "license": "MIT", "require": { - "php": "^7.2 || ^8.0", - "slevomat/coding-standard": "^8.6 <8.12", - "friendsofphp/php-cs-fixer": "^3.10 <3.16", + "php": "^7.4 || ^8.0", + "slevomat/coding-standard": "^8.6 <8.16", + "friendsofphp/php-cs-fixer": "^3.10 <3.59", "squizlabs/php_codesniffer": "^3.6", "kubawerlos/php-cs-fixer-custom-fixers": "^3.5" }, diff --git a/fix.php b/fix.php index 1ca6543..0b692cb 100644 --- a/fix.php +++ b/fix.php @@ -46,7 +46,7 @@ // try to find out the PHP version from the composer.json -$presetVersions = ['8.1', '8.0', '7.4', '7.3', '7.1']; +$presetVersions = ['8.3', '8.2', '8.1', '8.0', '7.4', '7.3', '7.1']; $root = getcwd(); diff --git a/preset-fixer/base.php b/preset-fixer/base.php index 5d17cbe..33a8551 100644 --- a/preset-fixer/base.php +++ b/preset-fixer/base.php @@ -8,9 +8,8 @@ }, $files); $config = new PhpCsFixer\Config; +$config->setParallelConfig(PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect()); $config->registerCustomFixers([ - new NetteCodingStandard\Fixer\Whitespace\StatementIndentationFixer, - new NetteCodingStandard\Fixer\Basic\CurlyBracesPositionFixer, new NetteCodingStandard\Fixer\ClassNotation\ClassAndTraitVisibilityRequiredFixer, new NetteCodingStandard\Fixer\FunctionNotation\MethodArgumentSpaceFixer, new NetteCodingStandard\Fixer\FunctionNotation\FunctionDeclarationFixer, diff --git a/preset-fixer/common/Nette.php b/preset-fixer/common/Nette.php index 9c96b62..6a75ff0 100644 --- a/preset-fixer/common/Nette.php +++ b/preset-fixer/common/Nette.php @@ -5,16 +5,14 @@ return [ '@PSR12' => true, '@PSR12:risky' => true, - 'new_with_braces' => false, // new stdClass + 'new_with_parentheses' => false, // new stdClass 'single_line_after_imports' => false, // Nette uses two empty lines 'blank_line_after_namespace' => false, - 'ordered_imports' => true, // Use statements are alphabetically ordered + 'ordered_imports' => false, // Use statements are alphabetically ordered 'blank_line_between_import_groups' => false, - // braces split - 'braces' => false, // Ensures a single space after language constructs - 'single_space_after_construct' => true, + 'single_space_around_construct' => true, // The body of each control structure MUST be enclosed within braces 'control_structure_braces' => true, // Control structure continuation keyword must be on the configured line @@ -23,15 +21,10 @@ 'declare_parentheses' => true, // There must not be more than one statement per line 'no_multiple_statements_per_line' => true, - - - // overriden rules - // Curly braces must be placed as configured - 'Nette/curly_braces_position' => true, - + 'braces_position' => true, // Each statement must be indented - 'Nette/statement_indentation' => true, + 'statement_indentation' => true, // In the argument list, there must be one space after each comma, and there must no be a space before each comma 'method_argument_space' => false, @@ -106,7 +99,7 @@ // Remove useless semicolon statements 'no_empty_statement' => true, - 'no_unneeded_curly_braces' => true, + 'no_unneeded_braces' => true, // Remove trailing commas in list() calls. 'no_trailing_comma_in_singleline' => true, @@ -164,7 +157,7 @@ // Convert double quotes to single quotes for simple strings. 'single_quote' => true, - 'escape_implicit_backslashes' => true, + 'string_implicit_backslashes' => true, // Convert ${..} to {$..} 'simple_to_complex_string_variable' => true, diff --git a/preset-sniffer/php83.xml b/preset-sniffer/php83.xml index ee325b6..7857120 100644 --- a/preset-sniffer/php83.xml +++ b/preset-sniffer/php83.xml @@ -1,6 +1,9 @@ + + *\.php + diff --git a/src/Fixer/CurlyBracesPositionFixer.php b/src/Fixer/CurlyBracesPositionFixer.php deleted file mode 100644 index 0ae270a..0000000 --- a/src/Fixer/CurlyBracesPositionFixer.php +++ /dev/null @@ -1,436 +0,0 @@ - - * Dariusz Rumiński - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace NetteCodingStandard\Fixer\Basic; - -use PhpCsFixer\AbstractFixer; -use PhpCsFixer\Fixer\ConfigurableFixerInterface; -use PhpCsFixer\Fixer\Indentation; -use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface; -use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver; -use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface; -use PhpCsFixer\FixerConfiguration\FixerOptionBuilder; -use PhpCsFixer\FixerDefinition\CodeSample; -use PhpCsFixer\FixerDefinition\FixerDefinition; -use PhpCsFixer\FixerDefinition\FixerDefinitionInterface; -use PhpCsFixer\FixerDefinition\VersionSpecification; -use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample; -use PhpCsFixer\Preg; -use PhpCsFixer\Tokenizer\CT; -use PhpCsFixer\Tokenizer\Token; -use PhpCsFixer\Tokenizer\Tokens; -use PhpCsFixer\Tokenizer\TokensAnalyzer; - -final class CurlyBracesPositionFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface -{ - use Indentation; - - /** - * @internal - */ - public const NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END = 'next_line_unless_newline_at_signature_end'; - - /** - * @internal - */ - public const SAME_LINE = 'same_line'; - - /** - * {@inheritdoc} - */ - public function getDefinition(): FixerDefinitionInterface - { - return new FixerDefinition( - 'Curly braces must be placed as configured.', - [ - new CodeSample( - ' self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END] - ), - new CodeSample( - ' self::SAME_LINE] - ), - new CodeSample( - ' self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END] - ), - new CodeSample( - ' self::SAME_LINE] - ), - new VersionSpecificCodeSample( - ' self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END] - ), - new VersionSpecificCodeSample( - ' true] - ), - new CodeSample( - ' true] - ), - ] - ); - } - - /** - * {@inheritdoc} - */ - public function isCandidate(Tokens $tokens): bool - { - return $tokens->isTokenKindFound('{'); - } - - /** - * {@inheritdoc} - * - * Must run after ControlStructureBracesFixer. - */ - public function getPriority(): int - { - return parent::getPriority(); - } - - /** - * {@inheritdoc} - */ - protected function applyFix(\SplFileInfo $file, Tokens $tokens): void - { - $classyTokens = Token::getClassyTokenKinds(); - $controlStructureTokens = [T_DECLARE, T_DO, T_ELSE, T_ELSEIF, T_FINALLY, T_FOR, T_FOREACH, T_IF, T_WHILE, T_TRY, T_CATCH, T_SWITCH]; - // @TODO: drop condition when PHP 8.0+ is required - if (\defined('T_MATCH')) { - $controlStructureTokens[] = T_MATCH; - } - - $tokensAnalyzer = new TokensAnalyzer($tokens); - - $allowSingleLineUntil = null; - - foreach ($tokens as $index => $token) { - $allowSingleLine = false; - $allowSingleLineIfEmpty = false; - - if ($token->isGivenKind($classyTokens)) { - $openBraceIndex = $tokens->getNextTokenOfKind($index, ['{']); - - if ($tokensAnalyzer->isAnonymousClass($index)) { - $allowSingleLineIfEmpty = $this->configuration['allow_single_line_empty_anonymous_classes']; - $positionOption = 'anonymous_classes_opening_brace'; - } else { - $positionOption = 'classes_opening_brace'; - } - } elseif ($token->isGivenKind(T_FUNCTION)) { - $openBraceIndex = $tokens->getNextTokenOfKind($index, ['{', ';']); - - if ($tokens[$openBraceIndex]->equals(';')) { - continue; - } - - if ($tokensAnalyzer->isLambda($index)) { - $allowSingleLine = $this->configuration['allow_single_line_anonymous_functions']; - $positionOption = 'anonymous_functions_opening_brace'; - } else { - $positionOption = 'functions_opening_brace'; - } - } elseif ($token->isGivenKind($controlStructureTokens)) { - $parenthesisEndIndex = $this->findParenthesisEnd($tokens, $index); - $openBraceIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex); - - if (!$tokens[$openBraceIndex]->equals('{')) { - continue; - } - - $positionOption = 'control_structures_opening_brace'; - } else { - continue; - } - - $closeBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $openBraceIndex); - - $addNewlinesInsideBraces = true; - if ($allowSingleLine || $allowSingleLineIfEmpty || $index < $allowSingleLineUntil) { - $addNewlinesInsideBraces = false; - - for ($indexInsideBraces = $openBraceIndex + 1; $indexInsideBraces < $closeBraceIndex; ++$indexInsideBraces) { - $tokenInsideBraces = $tokens[$indexInsideBraces]; - - if ( - ($allowSingleLineIfEmpty && !$tokenInsideBraces->isWhitespace() && !$tokenInsideBraces->isComment()) - || ($tokenInsideBraces->isWhitespace() && 1 === Preg::match('/\R/', $tokenInsideBraces->getContent())) - ) { - $addNewlinesInsideBraces = true; - - break; - } - } - - if (!$addNewlinesInsideBraces && null === $allowSingleLineUntil) { - $allowSingleLineUntil = $closeBraceIndex; - } - } - - if ( - $addNewlinesInsideBraces - && !$this->isFollowedByNewLine($tokens, $openBraceIndex) - && !$this->hasCommentOnSameLine($tokens, $openBraceIndex) - && !$tokens[$tokens->getNextMeaningfulToken($openBraceIndex)]->isGivenKind(T_CLOSE_TAG) - ) { - $whitespace = $this->whitespacesConfig->getLineEnding().$this->getLineIndentation($tokens, $openBraceIndex); - if ($tokens->ensureWhitespaceAtIndex($openBraceIndex + 1, 0, $whitespace)) { - ++$closeBraceIndex; - } - } - - $whitespace = ' '; - if (self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END === $this->configuration[$positionOption]) { - $whitespace = $this->whitespacesConfig->getLineEnding().$this->getLineIndentation($tokens, $index); - - $colon = false; - $previousTokenIndex = $openBraceIndex; - do { - $previousTokenIndex = $tokens->getPrevMeaningfulToken($previousTokenIndex); - $colon = $colon || $tokens[$previousTokenIndex]->isGivenKind([CT::T_TYPE_COLON]); - } while ($tokens[$previousTokenIndex]->isGivenKind([CT::T_TYPE_COLON, CT::T_NULLABLE_TYPE, T_STRING, T_NS_SEPARATOR, CT::T_ARRAY_TYPEHINT, T_STATIC, CT::T_TYPE_ALTERNATION, CT::T_TYPE_INTERSECTION])); - - if (!$colon && $tokens[$previousTokenIndex]->equals(')')) { - if ($tokens[--$previousTokenIndex]->isComment()) { - --$previousTokenIndex; - } - if ( - $tokens[$previousTokenIndex]->isWhitespace() - && 1 === Preg::match('/\R/', $tokens[$previousTokenIndex]->getContent()) - ) { - $whitespace = ' '; - } - } - } - - $moveBraceToIndex = null; - - if (' ' === $whitespace) { - $previousMeaningfulIndex = $tokens->getPrevMeaningfulToken($openBraceIndex); - for ($indexBeforeOpenBrace = $openBraceIndex - 1; $indexBeforeOpenBrace > $previousMeaningfulIndex; --$indexBeforeOpenBrace) { - if (!$tokens[$indexBeforeOpenBrace]->isComment()) { - continue; - } - - $tokenBeforeOpenBrace = $tokens[--$indexBeforeOpenBrace]; - if ($tokenBeforeOpenBrace->isWhitespace()) { - $moveBraceToIndex = $indexBeforeOpenBrace; - } elseif ($indexBeforeOpenBrace === $previousMeaningfulIndex) { - $moveBraceToIndex = $previousMeaningfulIndex + 1; - } - } - } elseif (!$tokens[$openBraceIndex - 1]->isWhitespace() || !Preg::match('/\R/', $tokens[$openBraceIndex - 1]->getContent())) { - for ($indexAfterOpenBrace = $openBraceIndex + 1; $indexAfterOpenBrace < $closeBraceIndex; ++$indexAfterOpenBrace) { - if ($tokens[$indexAfterOpenBrace]->isWhitespace() && Preg::match('/\R/', $tokens[$indexAfterOpenBrace]->getContent())) { - break; - } - - if ($tokens[$indexAfterOpenBrace]->isComment() && !str_starts_with($tokens[$indexAfterOpenBrace]->getContent(), '/*')) { - $moveBraceToIndex = $indexAfterOpenBrace + 1; - } - } - } - - if (null !== $moveBraceToIndex) { - /** @var Token $movedToken */ - $movedToken = clone $tokens[$openBraceIndex]; - - $delta = $openBraceIndex < $moveBraceToIndex ? 1 : -1; - - if ($tokens[$openBraceIndex + $delta]->isWhitespace()) { - if (-1 === $delta && Preg::match('/\R/', $tokens[$openBraceIndex - 1]->getContent())) { - $content = Preg::replace('/^(\h*?\R)?\h*/', '', $tokens[$openBraceIndex + 1]->getContent()); - if ('' !== $content) { - $tokens[$openBraceIndex + 1] = new Token([T_WHITESPACE, $content]); - } else { - $tokens->clearAt($openBraceIndex + 1); - } - } else { - $tokens->clearAt($openBraceIndex - 1); - } - } - - for (; $openBraceIndex !== $moveBraceToIndex; $openBraceIndex += $delta) { - /** @var Token $siblingToken */ - $siblingToken = $tokens[$openBraceIndex + $delta]; - $tokens[$openBraceIndex] = $siblingToken; - } - - $tokens[$openBraceIndex] = $movedToken; - - $openBraceIndex = $moveBraceToIndex; - } - - if ($tokens->ensureWhitespaceAtIndex($openBraceIndex - 1, 1, $whitespace)) { - ++$closeBraceIndex; - if (null !== $allowSingleLineUntil) { - ++$allowSingleLineUntil; - } - } - - if ( - !$addNewlinesInsideBraces - || $tokens[$tokens->getPrevMeaningfulToken($closeBraceIndex)]->isGivenKind(T_OPEN_TAG) - ) { - continue; - } - - for ($prevIndex = $closeBraceIndex - 1; $tokens->isEmptyAt($prevIndex); --$prevIndex); - - $prevToken = $tokens[$prevIndex]; - if ($prevToken->isWhitespace() && 1 === Preg::match('/\R/', $prevToken->getContent())) { - continue; - } - - $whitespace = $this->whitespacesConfig->getLineEnding().$this->getLineIndentation($tokens, $openBraceIndex); - $tokens->ensureWhitespaceAtIndex($prevIndex, 1, $whitespace); - } - } - - /** - * {@inheritdoc} - */ - protected function createConfigurationDefinition(): FixerConfigurationResolverInterface - { - return new FixerConfigurationResolver([ - (new FixerOptionBuilder('control_structures_opening_brace', 'the position of the opening brace of control structures body.')) - ->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE]) - ->setDefault(self::SAME_LINE) - ->getOption(), - (new FixerOptionBuilder('functions_opening_brace', 'the position of the opening brace of functions body.')) - ->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE]) - ->setDefault(self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END) - ->getOption(), - (new FixerOptionBuilder('anonymous_functions_opening_brace', 'the position of the opening brace of anonymous functions body.')) - ->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE]) - ->setDefault(self::SAME_LINE) - ->getOption(), - (new FixerOptionBuilder('classes_opening_brace', 'the position of the opening brace of classes body.')) - ->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE]) - ->setDefault(self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END) - ->getOption(), - (new FixerOptionBuilder('anonymous_classes_opening_brace', 'the position of the opening brace of anonymous classes body.')) - ->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE]) - ->setDefault(self::SAME_LINE) - ->getOption(), - (new FixerOptionBuilder('allow_single_line_empty_anonymous_classes', 'allow anonymous classes to have opening and closing braces on the same line.')) - ->setAllowedTypes(['bool']) - ->setDefault(true) - ->getOption(), - (new FixerOptionBuilder('allow_single_line_anonymous_functions', 'allow anonymous functions to have opening and closing braces on the same line.')) - ->setAllowedTypes(['bool']) - ->setDefault(true) - ->getOption(), - ]); - } - - private function findParenthesisEnd(Tokens $tokens, int $structureTokenIndex): int - { - $nextIndex = $tokens->getNextMeaningfulToken($structureTokenIndex); - $nextToken = $tokens[$nextIndex]; - - // return if next token is not opening parenthesis - if (!$nextToken->equals('(')) { - return $structureTokenIndex; - } - - return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextIndex); - } - - private function isFollowedByNewLine(Tokens $tokens, int $index): bool - { - for (++$index, $max = \count($tokens) - 1; $index < $max; ++$index) { - $token = $tokens[$index]; - if (!$token->isComment()) { - return $token->isWhitespace() && 1 === Preg::match('/\R/', $token->getContent()); - } - } - - return false; - } - - private function hasCommentOnSameLine(Tokens $tokens, int $index): bool - { - $token = $tokens[$index + 1]; - - if ($token->isWhitespace() && 1 !== Preg::match('/\R/', $token->getContent())) { - $token = $tokens[$index + 2]; - } - - return $token->isComment(); - } - - public function getName(): string - { - return 'Nette/' . parent::getName(); - } -} diff --git a/src/Fixer/StatementIndentationFixer.php b/src/Fixer/StatementIndentationFixer.php deleted file mode 100644 index ed6a68a..0000000 --- a/src/Fixer/StatementIndentationFixer.php +++ /dev/null @@ -1,620 +0,0 @@ - - * Dariusz Rumiński - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace NetteCodingStandard\Fixer\Whitespace; - -use PhpCsFixer\AbstractFixer; -use PhpCsFixer\Fixer\Indentation; -use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface; -use PhpCsFixer\FixerDefinition\CodeSample; -use PhpCsFixer\FixerDefinition\FixerDefinition; -use PhpCsFixer\FixerDefinition\FixerDefinitionInterface; -use PhpCsFixer\Preg; -use PhpCsFixer\Tokenizer\Analyzer\AlternativeSyntaxAnalyzer; -use PhpCsFixer\Tokenizer\CT; -use PhpCsFixer\Tokenizer\Token; -use PhpCsFixer\Tokenizer\Tokens; - -final class StatementIndentationFixer extends AbstractFixer implements WhitespacesAwareFixerInterface -{ - use Indentation; - - private AlternativeSyntaxAnalyzer $alternativeSyntaxAnalyzer; - - private bool $bracesFixerCompatibility; - - public function __construct(bool $bracesFixerCompatibility = false) - { - parent::__construct(); - - $this->bracesFixerCompatibility = $bracesFixerCompatibility; - } - - /** - * {@inheritdoc} - */ - public function getDefinition(): FixerDefinitionInterface - { - return new FixerDefinition( - 'Each statement must be indented.', - [ - new CodeSample( - 'alternativeSyntaxAnalyzer = new AlternativeSyntaxAnalyzer(); - - $blockSignatureFirstTokens = [ - T_USE, - T_IF, - T_ELSE, - T_ELSEIF, - T_FOR, - T_FOREACH, - T_WHILE, - T_SWITCH, - T_CASE, - T_DEFAULT, - T_TRY, - T_FUNCTION, - T_CLASS, - T_INTERFACE, - T_TRAIT, - T_EXTENDS, - T_IMPLEMENTS, - ]; - if (\defined('T_MATCH')) { // @TODO: drop condition when PHP 8.0+ is required - $blockSignatureFirstTokens[] = T_MATCH; - } - - $blockFirstTokens = ['{', [CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN], [CT::T_USE_TRAIT], [CT::T_GROUP_IMPORT_BRACE_OPEN]]; - if (\defined('T_ATTRIBUTE')) { // @TODO: drop condition when PHP 8.0+ is required - $blockFirstTokens[] = [T_ATTRIBUTE]; - } - - $endIndex = \count($tokens) - 1; - if ($tokens[$endIndex]->isWhitespace()) { - --$endIndex; - } - - $lastIndent = $this->getLineIndentationWithBracesCompatibility( - $tokens, - 0, - $this->extractIndent($this->computeNewLineContent($tokens, 0)), - ); - - /** - * @var list $scopes - */ - $scopes = [ - [ - 'type' => 'block', - 'skip' => false, - 'end_index' => $endIndex, - 'end_index_inclusive' => true, - 'initial_indent' => $lastIndent, - 'is_indented_block' => false, - ], - ]; - - $previousLineInitialIndent = ''; - $previousLineNewIndent = ''; - $alternativeBlockStarts = []; - $caseBlockStarts = []; - - foreach ($tokens as $index => $token) { - $currentScope = \count($scopes) - 1; - - if ( - $token->equalsAny($blockFirstTokens) - || ($token->equals('(') && !$tokens[$tokens->getPrevMeaningfulToken($index)]->isGivenKind(T_ARRAY)) - || isset($alternativeBlockStarts[$index]) - || isset($caseBlockStarts[$index]) - ) { - $endIndexInclusive = true; - - if ($token->isGivenKind([T_EXTENDS, T_IMPLEMENTS])) { - $endIndex = $tokens->getNextTokenOfKind($index, ['{']); - } elseif ($token->isGivenKind(CT::T_USE_TRAIT)) { - $endIndex = $tokens->getNextTokenOfKind($index, [';']); - } elseif ($token->equals(':')) { - if (isset($caseBlockStarts[$index])) { - [$endIndex, $endIndexInclusive] = $this->findCaseBlockEnd($tokens, $index); - } else { - $endIndex = $this->alternativeSyntaxAnalyzer->findAlternativeSyntaxBlockEnd($tokens, $alternativeBlockStarts[$index]); - } - } elseif ($token->isGivenKind(CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN)) { - $endIndex = $tokens->getNextTokenOfKind($index, [[CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE]]); - } elseif ($token->isGivenKind(CT::T_GROUP_IMPORT_BRACE_OPEN)) { - $endIndex = $tokens->getNextTokenOfKind($index, [[CT::T_GROUP_IMPORT_BRACE_CLOSE]]); - } elseif ($token->equals('{')) { - $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index); - } elseif ($token->equals('(')) { - $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index); - } else { - $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ATTRIBUTE, $index); - } - - if ('block_signature' === $scopes[$currentScope]['type']) { - $initialIndent = $scopes[$currentScope]['initial_indent']; - } else { - $initialIndent = $this->getLineIndentationWithBracesCompatibility($tokens, $index, $lastIndent); - } - - $skip = false; - if ($this->bracesFixerCompatibility) { - $prevIndex = $tokens->getPrevMeaningfulToken($index); - if (null !== $prevIndex) { - $prevIndex = $tokens->getPrevMeaningfulToken($prevIndex); - } - if (null !== $prevIndex && $tokens[$prevIndex]->isGivenKind([T_FUNCTION, T_FN])) { - $skip = true; - } - } - - $scopes[] = [ - 'type' => 'block', - 'skip' => $skip, - 'end_index' => $endIndex, - 'end_index_inclusive' => $endIndexInclusive, - 'initial_indent' => $initialIndent, - 'is_indented_block' => true, - ]; - ++$currentScope; - - while ($index >= $scopes[$currentScope]['end_index']) { - array_pop($scopes); - - --$currentScope; - } - - continue; - } - - if ($token->isGivenKind($blockSignatureFirstTokens)) { - for ($endIndex = $index + 1, $max = \count($tokens); $endIndex < $max; ++$endIndex) { - if ($tokens[$endIndex]->equals('(')) { - $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $endIndex); - - continue; - } - - if ($tokens[$endIndex]->equalsAny(['{', ';', [T_DOUBLE_ARROW], [T_IMPLEMENTS]])) { - break; - } - - if ($tokens[$endIndex]->equals(':')) { - if ($token->isGivenKind([T_CASE, T_DEFAULT])) { - $caseBlockStarts[$endIndex] = $index; - } else { - $alternativeBlockStarts[$endIndex] = $index; - } - - break; - } - } - - $scopes[] = [ - 'type' => 'block_signature', - 'skip' => false, - 'end_index' => $endIndex, - 'end_index_inclusive' => true, - 'initial_indent' => $this->getLineIndentationWithBracesCompatibility($tokens, $index, $lastIndent), - 'is_indented_block' => $token->isGivenKind([T_EXTENDS, T_IMPLEMENTS]), - ]; - - continue; - } - - if ( - $token->isWhitespace() - || ($index > 0 && $tokens[$index - 1]->isGivenKind(T_OPEN_TAG)) - ) { - $previousOpenTagContent = $tokens[$index - 1]->isGivenKind(T_OPEN_TAG) - ? Preg::replace('/\S/', '', $tokens[$index - 1]->getContent()) - : '' - ; - - $content = $previousOpenTagContent.($token->isWhitespace() ? $token->getContent() : ''); - - if (!Preg::match('/\R/', $content)) { - continue; - } - - $nextToken = $tokens[$index + 1] ?? null; - - if ( - $this->bracesFixerCompatibility - && null !== $nextToken - && $nextToken->isComment() - && !$this->isCommentWithFixableIndentation($tokens, $index + 1) - ) { - continue; - } - - if ('block' === $scopes[$currentScope]['type'] || 'block_signature' === $scopes[$currentScope]['type']) { - $indent = false; - - if ($scopes[$currentScope]['is_indented_block']) { - $firstNonWhitespaceTokenIndex = null; - $nextNewlineIndex = null; - for ($searchIndex = $index + 1, $max = \count($tokens); $searchIndex < $max; ++$searchIndex) { - $searchToken = $tokens[$searchIndex]; - - if (!$searchToken->isWhitespace()) { - if (null === $firstNonWhitespaceTokenIndex) { - $firstNonWhitespaceTokenIndex = $searchIndex; - } - - continue; - } - - if (Preg::match('/\R/', $searchToken->getContent())) { - $nextNewlineIndex = $searchIndex; - - break; - } - } - - if (!$this->isCommentForControlSructureContinuation($tokens, $index + 1)) { - $endIndex = $scopes[$currentScope]['end_index']; - - if (!$scopes[$currentScope]['end_index_inclusive']) { - ++$endIndex; - } - - if ( - (null !== $firstNonWhitespaceTokenIndex && $firstNonWhitespaceTokenIndex < $endIndex) - || (null !== $nextNewlineIndex && $nextNewlineIndex < $endIndex) - ) { - $indent = true; - } - } - } - - $previousLineInitialIndent = $this->extractIndent($content); - - if ($scopes[$currentScope]['skip']) { - $whitespaces = $previousLineInitialIndent; - } else { - $whitespaces = $scopes[$currentScope]['initial_indent'].($indent ? $this->whitespacesConfig->getIndent() : ''); - } - - $content = Preg::replace( - '/(\R+)\h*$/', - '$1'.$whitespaces, - $content - ); - - $previousLineNewIndent = $this->extractIndent($content); - } else { - $content = Preg::replace( - '/(\R)'.$scopes[$currentScope]['initial_indent'].'(\h*)$/D', - '$1'.$scopes[$currentScope]['new_indent'].'$2', - $content - ); - } - - $lastIndent = $this->extractIndent($content); - - if ('' !== $previousOpenTagContent) { - $content = Preg::replace("/^{$previousOpenTagContent}/", '', $content); - } - - if ('' !== $content) { - $tokens->ensureWhitespaceAtIndex($index, 0, $content); - } elseif ($token->isWhitespace()) { - $tokens->clearAt($index); - } - - if (null !== $nextToken && $nextToken->isComment()) { - $tokens[$index + 1] = new Token([ - $nextToken->getId(), - Preg::replace( - '/(\R)'.preg_quote($previousLineInitialIndent, '/').'(\h*\S+.*)/', - '$1'.$previousLineNewIndent.'$2', - $nextToken->getContent() - ), - ]); - } - - if ($token->isWhitespace()) { - continue; - } - } - - if ($this->isNewLineToken($tokens, $index)) { - $lastIndent = $this->extractIndent($this->computeNewLineContent($tokens, $index)); - } - - while ($index >= $scopes[$currentScope]['end_index']) { - array_pop($scopes); - - if ([] === $scopes) { - return; - } - - --$currentScope; - } - - if ($token->equalsAny([';', '}', [T_OPEN_TAG], [T_CLOSE_TAG], [CT::T_ATTRIBUTE_CLOSE]])) { - continue; - } - - if ('statement' !== $scopes[$currentScope]['type'] && 'block_signature' !== $scopes[$currentScope]['type']) { - $endIndex = $this->findStatementEndIndex($tokens, $index, $scopes[$currentScope]['end_index']); - - if ($endIndex === $index) { - continue; - } - - $scopes[] = [ - 'type' => 'statement', - 'skip' => false, - 'end_index' => $endIndex, - 'end_index_inclusive' => false, - 'initial_indent' => $previousLineInitialIndent, - 'new_indent' => $previousLineNewIndent, - ]; - } - } - } - - private function findStatementEndIndex(Tokens $tokens, int $index, int $parentScopeEndIndex): int - { - $endIndex = null; - - for ($searchEndIndex = $index; $searchEndIndex <= $parentScopeEndIndex; ++$searchEndIndex) { - $searchEndToken = $tokens[$searchEndIndex]; - - if ($searchEndToken->equalsAny(['(', '{', [CT::T_ARRAY_SQUARE_BRACE_OPEN]])) { - if ($searchEndToken->equals('(')) { - $blockType = Tokens::BLOCK_TYPE_PARENTHESIS_BRACE; - } elseif ($searchEndToken->equals('{')) { - $blockType = Tokens::BLOCK_TYPE_CURLY_BRACE; - } else { - $blockType = Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE; - } - - $searchEndIndex = $tokens->findBlockEnd($blockType, $searchEndIndex); - - continue; - } - - if ($searchEndToken->equalsAny([';', ',', '}', [T_CLOSE_TAG]])) { - $endIndex = $tokens->getPrevNonWhitespace($searchEndIndex); - - break; - } - } - - return $endIndex ?? $tokens->getPrevMeaningfulToken($parentScopeEndIndex); - } - - /** - * @return array{int, bool} - */ - private function findCaseBlockEnd(Tokens $tokens, int $index): array - { - for ($max = \count($tokens); $index < $max; ++$index) { - if ($tokens[$index]->isGivenKind(T_SWITCH)) { - $braceIndex = $tokens->getNextMeaningfulToken( - $tokens->findBlockEnd( - Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, - $tokens->getNextMeaningfulToken($index) - ) - ); - - if ($tokens[$braceIndex]->equals(':')) { - $index = $this->alternativeSyntaxAnalyzer->findAlternativeSyntaxBlockEnd($tokens, $index); - } else { - $index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $braceIndex); - } - - continue; - } - - if ($tokens[$index]->equals('{')) { - $index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index); - - continue; - } - - if ($tokens[$index]->equalsAny([[T_CASE], [T_DEFAULT]])) { - return [$index, true]; - } - - if ($tokens[$index]->equalsAny(['}', [T_ENDSWITCH]])) { - return [$tokens->getPrevNonWhitespace($index), false]; - } - } - - throw new \LogicException('End of case block not found.'); - } - - private function getLineIndentationWithBracesCompatibility(Tokens $tokens, int $index, string $regularIndent): string - { - if ( - $this->bracesFixerCompatibility - && $tokens[$index]->isGivenKind(T_OPEN_TAG) - && Preg::match('/\R/', $tokens[$index]->getContent()) - && isset($tokens[$index + 1]) - && $tokens[$index + 1]->isWhitespace() - && Preg::match('/\h+$/D', $tokens[$index + 1]->getContent()) - ) { - return Preg::replace('/.*?(\h+)$/D', '$1', $tokens[$index + 1]->getContent()); - } - - return $regularIndent; - } - - private function isCommentForControlSructureContinuation(Tokens $tokens, int $index): bool - { - if (!isset($tokens[$index], $tokens[$index + 1])) { - return false; - } - - if (!$tokens[$index]->isComment() || 1 !== Preg::match('~^(//|#)~', $tokens[$index]->getContent())) { - return false; - } - - if (!$tokens[$index + 1]->isWhitespace() || 1 !== Preg::match('/\R/', $tokens[$index + 1]->getContent())) { - return false; - } - - $prevIndex = $tokens->getPrevMeaningfulToken($index); - if (null !== $prevIndex && $tokens[$prevIndex]->equals('{')) { - return false; - } - - $index = $tokens->getNextMeaningfulToken($index + 1); - - if (null === $index || !$tokens[$index]->equals('}')) { - return false; - } - - $index = $tokens->getNextMeaningfulToken($index); - - return null !== $index && $tokens[$index]->equalsAny([[T_ELSE], [T_ELSEIF], ',']); - } - - /** - * Returns whether the token at given index is a comment whose indentation - * can be fixed. - * - * Indentation of a comment is not changed when the comment is part of a - * multi-line message whose lines are all single-line comments and at least - * one line has meaningful content. - */ - private function isCommentWithFixableIndentation(Tokens $tokens, int $index): bool - { - if (!$tokens[$index]->isComment()) { - return false; - } - - if (str_starts_with($tokens[$index]->getContent(), '/*')) { - return true; - } - - $indent = preg_quote($this->whitespacesConfig->getIndent(), '~'); - - if (1 === Preg::match("~^(//|#)({$indent}.*)?$~", $tokens[$index]->getContent())) { - return false; - } - - $firstCommentIndex = $index; - while (true) { - $i = $this->getSiblingContinuousSingleLineComment($tokens, $firstCommentIndex, false); - if (null === $i) { - break; - } - - $firstCommentIndex = $i; - } - - $lastCommentIndex = $index; - while (true) { - $i = $this->getSiblingContinuousSingleLineComment($tokens, $lastCommentIndex, true); - if (null === $i) { - break; - } - - $lastCommentIndex = $i; - } - - if ($firstCommentIndex === $lastCommentIndex) { - return true; - } - - for ($i = $firstCommentIndex + 1; $i < $lastCommentIndex; ++$i) { - if (!$tokens[$i]->isWhitespace() && !$tokens[$i]->isComment()) { - return false; - } - } - - return true; - } - - private function getSiblingContinuousSingleLineComment(Tokens $tokens, int $index, bool $after): ?int - { - $siblingIndex = $index; - do { - if ($after) { - $siblingIndex = $tokens->getNextTokenOfKind($siblingIndex, [[T_COMMENT]]); - } else { - $siblingIndex = $tokens->getPrevTokenOfKind($siblingIndex, [[T_COMMENT]]); - } - - if (null === $siblingIndex) { - return null; - } - } while (str_starts_with($tokens[$siblingIndex]->getContent(), '/*')); - - $newLines = 0; - for ($i = min($siblingIndex, $index) + 1, $max = max($siblingIndex, $index); $i < $max; ++$i) { - if ($tokens[$i]->isWhitespace() && Preg::match('/\R/', $tokens[$i]->getContent())) { - if (1 === $newLines || Preg::match('/\R.*\R/', $tokens[$i]->getContent())) { - return null; - } - - ++$newLines; - } - } - - return $siblingIndex; - } - - public function getName(): string - { - return 'Nette/' . parent::getName(); - } -} From 0abc20cf83d7fc35aa55a359bcfdf69c244f1c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20Janu=CC=8A?= Date: Thu, 11 Jul 2024 09:41:18 +0200 Subject: [PATCH 3/3] Fixer and Sniffer do not have the same result of sorting uses. --- preset-sniffer/php71.xml | 5 +++++ preset-sniffer/php83.xml | 3 --- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/preset-sniffer/php71.xml b/preset-sniffer/php71.xml index ffba5a4..b725bd8 100644 --- a/preset-sniffer/php71.xml +++ b/preset-sniffer/php71.xml @@ -2,6 +2,11 @@ + + + *\.phpt? + + diff --git a/preset-sniffer/php83.xml b/preset-sniffer/php83.xml index 7857120..ee325b6 100644 --- a/preset-sniffer/php83.xml +++ b/preset-sniffer/php83.xml @@ -1,9 +1,6 @@ - - *\.php -