diff --git a/README.md b/README.md index bcce0e52fc..4d9bd680ed 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ [![Downloads total](https://img.shields.io/packagist/dt/behastan/behastan.svg?style=flat-square)](https://packagist.org/packages/behastan/behastan/stats) +Find unused and duplicated definitions easily – without running Behat tests. +
## Install diff --git a/bin/behastan.php b/bin/behastan.php index 6ac48959cd..dd83dd5ec8 100755 --- a/bin/behastan.php +++ b/bin/behastan.php @@ -2,10 +2,10 @@ declare(strict_types=1); +use Rector\Behastan\DependencyInjection\ContainerFactory; use Symfony\Component\Console\Application; use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Output\ConsoleOutput; -use Behastan\DependencyInjection\ContainerFactory; $possibleAutoloadPaths = [ // dependency diff --git a/composer.json b/composer.json index da5be911a6..88347c78f3 100644 --- a/composer.json +++ b/composer.json @@ -1,18 +1,17 @@ { - "name": "behastan/behastan", - "description": "Modern Static analysis for Behat tests", + "name": "rector/behastan", + "description": "Static analysis for Behat definitions and context files", "license": "MIT", "bin": [ "bin/behastan" ], "require": { "php": "^8.2", - "illuminate/container": "^11.41|^12.0", - "nette/utils": "^4.0", + "illuminate/container": "^11.41|^12.37", "nikic/php-parser": "^5.6", "symfony/console": "^6.4|^7.0|^8.0", "symfony/finder": "^6.4|^7.0|^8.0", - "webmozart/assert": "^1.11" + "webmozart/assert": "^1.12" }, "require-dev": { "phpstan/extension-installer": "^1.4", @@ -25,12 +24,12 @@ }, "autoload": { "psr-4": { - "Behastan\\": "src" + "Rector\\Behastan\\": "src" } }, "autoload-dev": { "psr-4": { - "Behastan\\Tests\\": "tests" + "Rector\\Behastan\\Tests\\": "tests" }, "classmap": [ "stubs" diff --git a/ecs.php b/ecs.php index 596410890a..1b582c4fe9 100644 --- a/ecs.php +++ b/ecs.php @@ -5,6 +5,6 @@ use Symplify\EasyCodingStandard\Config\ECSConfig; return ECSConfig::configure() - ->withPaths([__DIR__ . '/src', __DIR__ . '/tests']) + ->withPaths([__DIR__ . '/bin', __DIR__ . '/src', __DIR__ . '/tests']) ->withRootFiles() ->withPreparedSets(psr12: true, common: true, symplify: true); diff --git a/src/Analyzer/ClassMethodContextDefinitionsAnalyzer.php b/src/Analyzer/ClassMethodContextDefinitionsAnalyzer.php index dfe7c0b4a3..57f8f5204b 100644 --- a/src/Analyzer/ClassMethodContextDefinitionsAnalyzer.php +++ b/src/Analyzer/ClassMethodContextDefinitionsAnalyzer.php @@ -2,16 +2,16 @@ declare(strict_types=1); -namespace Behastan\Analyzer; +namespace Rector\Behastan\Analyzer; -use Behastan\PhpParser\SimplePhpParser; -use Behastan\Resolver\ClassMethodMasksResolver; -use Behastan\ValueObject\ClassMethodContextDefinition; use PhpParser\Node\Name; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\NodeFinder; use PhpParser\PrettyPrinter\Standard; +use Rector\Behastan\PhpParser\SimplePhpParser; +use Rector\Behastan\Resolver\ClassMethodMasksResolver; +use Rector\Behastan\ValueObject\ClassMethodContextDefinition; use Symfony\Component\Finder\SplFileInfo; final readonly class ClassMethodContextDefinitionsAnalyzer diff --git a/src/Analyzer/MaskAnalyzer.php b/src/Analyzer/MaskAnalyzer.php index 0601a64a7c..c575fffce0 100644 --- a/src/Analyzer/MaskAnalyzer.php +++ b/src/Analyzer/MaskAnalyzer.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Behastan\Analyzer; +namespace Rector\Behastan\Analyzer; final class MaskAnalyzer { diff --git a/src/Analyzer/UnusedDefinitionsAnalyzer.php b/src/Analyzer/UnusedDefinitionsAnalyzer.php index 59509bdffc..29406da808 100644 --- a/src/Analyzer/UnusedDefinitionsAnalyzer.php +++ b/src/Analyzer/UnusedDefinitionsAnalyzer.php @@ -2,17 +2,17 @@ declare(strict_types=1); -namespace Behastan\Analyzer; - -use Behastan\DefinitionMasksResolver; -use Behastan\Reporting\MaskCollectionStatsPrinter; -use Behastan\UsedInstructionResolver; -use Behastan\ValueObject\Mask\AbstractMask; -use Behastan\ValueObject\Mask\ExactMask; -use Behastan\ValueObject\Mask\NamedMask; -use Behastan\ValueObject\Mask\RegexMask; -use Behastan\ValueObject\Mask\SkippedMask; +namespace Rector\Behastan\Analyzer; + use Nette\Utils\Strings; +use Rector\Behastan\DefinitionMasksResolver; +use Rector\Behastan\Reporting\MaskCollectionStatsPrinter; +use Rector\Behastan\UsedInstructionResolver; +use Rector\Behastan\ValueObject\Mask\AbstractMask; +use Rector\Behastan\ValueObject\Mask\ExactMask; +use Rector\Behastan\ValueObject\Mask\NamedMask; +use Rector\Behastan\ValueObject\Mask\RegexMask; +use Rector\Behastan\ValueObject\Mask\SkippedMask; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Finder\SplFileInfo; @@ -68,7 +68,7 @@ public function analyse(array $contextFiles, array $featureFiles): array private function isRegexDefinitionUsed(string $regexBehatDefinition, array $featureInstructions): bool { foreach ($featureInstructions as $featureInstruction) { - if (Strings::match($featureInstruction, $regexBehatDefinition)) { + if (preg_match($featureInstruction, $regexBehatDefinition)) { // it is used! return true; } @@ -98,7 +98,8 @@ private function isMaskUsed(AbstractMask $mask, array $featureInstructions): boo if ($mask instanceof NamedMask) { // normalize :mask definition to regex - $regexMask = '#' . Strings::replace($mask->mask, self::MASK_VALUE_REGEX, '(.*?)') . '#'; + $regexMask = '#' . preg_replace(self::MASK_VALUE_REGEX, '(.*?)', $mask->mask) . '#'; + if ($this->isRegexDefinitionUsed($regexMask, $featureInstructions)) { return true; } diff --git a/src/Command/DuplicatedDefinitionsCommand.php b/src/Command/DuplicatedDefinitionsCommand.php index c4fb945134..2592475929 100644 --- a/src/Command/DuplicatedDefinitionsCommand.php +++ b/src/Command/DuplicatedDefinitionsCommand.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace Behastan\Command; +namespace Rector\Behastan\Command; -use Behastan\Analyzer\ClassMethodContextDefinitionsAnalyzer; -use Behastan\Enum\Option; -use Behastan\Finder\BehatMetafilesFinder; +use Rector\Behastan\Analyzer\ClassMethodContextDefinitionsAnalyzer; +use Rector\Behastan\Enum\Option; +use Rector\Behastan\Finder\BehatMetafilesFinder; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; diff --git a/src/Command/StatsCommand.php b/src/Command/StatsCommand.php index acf81581d9..e5b2a0d405 100644 --- a/src/Command/StatsCommand.php +++ b/src/Command/StatsCommand.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace Behastan\Command; +namespace Rector\Behastan\Command; -use Behastan\Analyzer\ClassMethodContextDefinitionsAnalyzer; -use Behastan\Analyzer\MaskAnalyzer; -use Behastan\Finder\BehatMetafilesFinder; -use Behastan\UsedInstructionResolver; +use Rector\Behastan\Analyzer\ClassMethodContextDefinitionsAnalyzer; +use Rector\Behastan\Analyzer\MaskAnalyzer; +use Rector\Behastan\Finder\BehatMetafilesFinder; +use Rector\Behastan\UsedInstructionResolver; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; diff --git a/src/Command/UnusedDefinitionsCommand.php b/src/Command/UnusedDefinitionsCommand.php index 656b2c063b..26ef53feb7 100644 --- a/src/Command/UnusedDefinitionsCommand.php +++ b/src/Command/UnusedDefinitionsCommand.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace Behastan\Command; +namespace Rector\Behastan\Command; -use Behastan\Analyzer\UnusedDefinitionsAnalyzer; -use Behastan\Enum\Option; -use Behastan\Finder\BehatMetafilesFinder; -use Behastan\ValueObject\Mask\AbstractMask; +use Rector\Behastan\Analyzer\UnusedDefinitionsAnalyzer; +use Rector\Behastan\Enum\Option; +use Rector\Behastan\Finder\BehatMetafilesFinder; +use Rector\Behastan\ValueObject\Mask\AbstractMask; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; diff --git a/src/Contract/MaskInterface.php b/src/Contract/MaskInterface.php index ec4035b808..4880470c00 100644 --- a/src/Contract/MaskInterface.php +++ b/src/Contract/MaskInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Behastan\Contract; +namespace Rector\Behastan\Contract; interface MaskInterface { diff --git a/src/DefinitionMasksResolver.php b/src/DefinitionMasksResolver.php index 63da8e64cb..c1e7d92c08 100644 --- a/src/DefinitionMasksResolver.php +++ b/src/DefinitionMasksResolver.php @@ -2,20 +2,20 @@ declare(strict_types=1); -namespace Behastan; - -use Behastan\Analyzer\MaskAnalyzer; -use Behastan\PhpParser\SimplePhpParser; -use Behastan\Resolver\ClassMethodMasksResolver; -use Behastan\ValueObject\ClassMethodContextDefinition; -use Behastan\ValueObject\Mask\ExactMask; -use Behastan\ValueObject\Mask\NamedMask; -use Behastan\ValueObject\Mask\RegexMask; -use Behastan\ValueObject\Mask\SkippedMask; -use Behastan\ValueObject\MaskCollection; +namespace Rector\Behastan; + use PhpParser\Node\Name; use PhpParser\Node\Stmt\Class_; use PhpParser\NodeFinder; +use Rector\Behastan\Analyzer\MaskAnalyzer; +use Rector\Behastan\PhpParser\SimplePhpParser; +use Rector\Behastan\Resolver\ClassMethodMasksResolver; +use Rector\Behastan\ValueObject\ClassMethodContextDefinition; +use Rector\Behastan\ValueObject\Mask\ExactMask; +use Rector\Behastan\ValueObject\Mask\NamedMask; +use Rector\Behastan\ValueObject\Mask\RegexMask; +use Rector\Behastan\ValueObject\Mask\SkippedMask; +use Rector\Behastan\ValueObject\MaskCollection; use SplFileInfo; final readonly class DefinitionMasksResolver diff --git a/src/DependencyInjection/ContainerFactory.php b/src/DependencyInjection/ContainerFactory.php index ea4d576cb8..12f6a31ecc 100644 --- a/src/DependencyInjection/ContainerFactory.php +++ b/src/DependencyInjection/ContainerFactory.php @@ -2,14 +2,14 @@ declare(strict_types=1); -namespace Behastan\DependencyInjection; +namespace Rector\Behastan\DependencyInjection; -use Behastan\Command\DuplicatedDefinitionsCommand; -use Behastan\Command\StatsCommand; -use Behastan\Command\UnusedDefinitionsCommand; use Illuminate\Container\Container; use PhpParser\Parser; use PhpParser\ParserFactory; +use Rector\Behastan\Command\DuplicatedDefinitionsCommand; +use Rector\Behastan\Command\StatsCommand; +use Rector\Behastan\Command\UnusedDefinitionsCommand; use Symfony\Component\Console\Application; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\ConsoleOutput; diff --git a/src/Enum/Option.php b/src/Enum/Option.php index bc90d5693c..bb8dad9a23 100644 --- a/src/Enum/Option.php +++ b/src/Enum/Option.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Behastan\Enum; +namespace Rector\Behastan\Enum; final class Option { diff --git a/src/Finder/BehatMetafilesFinder.php b/src/Finder/BehatMetafilesFinder.php index 8eac71c293..060e1aef59 100644 --- a/src/Finder/BehatMetafilesFinder.php +++ b/src/Finder/BehatMetafilesFinder.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Behastan\Finder; +namespace Rector\Behastan\Finder; use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\SplFileInfo; diff --git a/src/PhpParser/SimplePhpParser.php b/src/PhpParser/SimplePhpParser.php index 024c2e1587..f2d3c521d8 100644 --- a/src/PhpParser/SimplePhpParser.php +++ b/src/PhpParser/SimplePhpParser.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Behastan\PhpParser; +namespace Rector\Behastan\PhpParser; use PhpParser\Node\Stmt; use PhpParser\NodeTraverser; diff --git a/src/Reporting/MaskCollectionStatsPrinter.php b/src/Reporting/MaskCollectionStatsPrinter.php index 5e1e15448f..bc8f4eeddc 100644 --- a/src/Reporting/MaskCollectionStatsPrinter.php +++ b/src/Reporting/MaskCollectionStatsPrinter.php @@ -2,14 +2,14 @@ declare(strict_types=1); -namespace Behastan\Reporting; - -use Behastan\ValueObject\Mask\AbstractMask; -use Behastan\ValueObject\Mask\ExactMask; -use Behastan\ValueObject\Mask\NamedMask; -use Behastan\ValueObject\Mask\RegexMask; -use Behastan\ValueObject\Mask\SkippedMask; -use Behastan\ValueObject\MaskCollection; +namespace Rector\Behastan\Reporting; + +use Rector\Behastan\ValueObject\Mask\AbstractMask; +use Rector\Behastan\ValueObject\Mask\ExactMask; +use Rector\Behastan\ValueObject\Mask\NamedMask; +use Rector\Behastan\ValueObject\Mask\RegexMask; +use Rector\Behastan\ValueObject\Mask\SkippedMask; +use Rector\Behastan\ValueObject\MaskCollection; use Symfony\Component\Console\Style\SymfonyStyle; final readonly class MaskCollectionStatsPrinter diff --git a/src/Resolver/ClassMethodMasksResolver.php b/src/Resolver/ClassMethodMasksResolver.php index 844828488e..ee87eec75c 100644 --- a/src/Resolver/ClassMethodMasksResolver.php +++ b/src/Resolver/ClassMethodMasksResolver.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Behastan\Resolver; +namespace Rector\Behastan\Resolver; use PhpParser\Comment\Doc; use PhpParser\Node\Scalar\String_; diff --git a/src/UsedInstructionResolver.php b/src/UsedInstructionResolver.php index 100256890a..a35fbcbc62 100644 --- a/src/UsedInstructionResolver.php +++ b/src/UsedInstructionResolver.php @@ -2,9 +2,8 @@ declare(strict_types=1); -namespace Behastan; +namespace Rector\Behastan; -use Nette\Utils\Strings; use RuntimeException; use Symfony\Component\Finder\SplFileInfo; @@ -19,9 +18,11 @@ public function resolveInstructionsFromFeatureFiles(array $featureFileInfos): ar $instructions = []; foreach ($featureFileInfos as $featureFileInfo) { - $matches = Strings::matchAll( + preg_match_all( + '#\s+(Given|When|And|Then)\s+(?.*?)\n#m', $featureFileInfo->getContents(), - '#\s+(Given|When|And|Then)\s+(?.*?)\n#m' + $matches, + PREG_SET_ORDER ); if ($matches === []) { diff --git a/src/ValueObject/ClassMethodContextDefinition.php b/src/ValueObject/ClassMethodContextDefinition.php index b5d0b805e2..0727e64311 100644 --- a/src/ValueObject/ClassMethodContextDefinition.php +++ b/src/ValueObject/ClassMethodContextDefinition.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Behastan\ValueObject; +namespace Rector\Behastan\ValueObject; final class ClassMethodContextDefinition { diff --git a/src/ValueObject/Mask/AbstractMask.php b/src/ValueObject/Mask/AbstractMask.php index a195707b29..fd471be34b 100644 --- a/src/ValueObject/Mask/AbstractMask.php +++ b/src/ValueObject/Mask/AbstractMask.php @@ -1,8 +1,8 @@