* Dariusz Rumiński * * This source file is subject to the MIT license that is bundled * with this source code in the file LICENSE. */ namespace PhpCsFixer\Fixer\PhpUnit; use PhpCsFixer\Fixer\AbstractPhpUnitFixer; use PhpCsFixer\Fixer\ConfigurableFixerInterface; 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\Tokenizer\Analyzer\DataProviderAnalyzer; use PhpCsFixer\Tokenizer\Token; use PhpCsFixer\Tokenizer\Tokens; use PhpCsFixer\Tokenizer\TokensAnalyzer; /** * @author Kuba Werłos */ final class PhpUnitDataProviderStaticFixer extends AbstractPhpUnitFixer implements ConfigurableFixerInterface { public function getDefinition(): FixerDefinitionInterface { return new FixerDefinition( 'Data providers must be static.', [ new CodeSample( 'getData1(); } public function provideSomethingCases2() { self::getData2(); } } ', ['force' => true] ), new CodeSample( 'getData1(); } public function provideSomething2Cases() { self::getData2(); } } ', ['force' => false] ), ], null, 'Fixer could be risky if one is calling data provider function dynamically.' ); } public function isRisky(): bool { return true; } protected function createConfigurationDefinition(): FixerConfigurationResolverInterface { return new FixerConfigurationResolver([ (new FixerOptionBuilder( 'force', 'Whether to make the data providers static even if they have a dynamic class call' .' (may introduce fatal error "using $this when not in object context",' .' and you may have to adjust the code manually by converting dynamic calls to static ones).' )) ->setAllowedTypes(['bool']) ->setDefault(false) ->getOption(), ]); } protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void { $dataProviderAnalyzer = new DataProviderAnalyzer(); $tokensAnalyzer = new TokensAnalyzer($tokens); $inserts = []; foreach ($dataProviderAnalyzer->getDataProviders($tokens, $startIndex, $endIndex) as $dataProviderDefinitionIndex) { $methodStartIndex = $tokens->getNextTokenOfKind($dataProviderDefinitionIndex->getNameIndex(), ['{']); if (null !== $methodStartIndex) { $methodEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $methodStartIndex); if (false === $this->configuration['force'] && null !== $tokens->findSequence([[T_VARIABLE, '$this']], $methodStartIndex, $methodEndIndex)) { continue; } } $functionIndex = $tokens->getPrevTokenOfKind($dataProviderDefinitionIndex->getNameIndex(), [[T_FUNCTION]]); $methodAttributes = $tokensAnalyzer->getMethodAttributes($functionIndex); if (false !== $methodAttributes['static']) { continue; } $inserts[$functionIndex] = [new Token([T_STATIC, 'static']), new Token([T_WHITESPACE, ' '])]; } $tokens->insertSlices($inserts); } }