diff --git a/README.md b/README.md
index bcce0e52fc..4d9bd680ed 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,8 @@
[](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 @@