Skip to content

Commit

Permalink
Add regex search command for easy quick stats
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasVotruba committed Jan 7, 2025
1 parent 4002e0e commit bf913bf
Show file tree
Hide file tree
Showing 17 changed files with 133 additions and 55 deletions.
8 changes: 4 additions & 4 deletions src/Behastan/Behastan.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@
/**
* @see \Rector\SwissKnife\Tests\Behastan\Behastan\BehastanTest
*/
final class Behastan
final readonly class Behastan
{
public function __construct(
private readonly SymfonyStyle $symfonyStyle,
private readonly DefinitionMasksResolver $definitionMasksResolver,
private readonly UsedInstructionResolver $usedInstructionResolver
private SymfonyStyle $symfonyStyle,
private DefinitionMasksResolver $definitionMasksResolver,
private UsedInstructionResolver $usedInstructionResolver
) {
}

Expand Down
4 changes: 2 additions & 2 deletions src/Behastan/Command/BehastanCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int
Assert::allDirectory($testDirectories);

$featureFiles = $this->behatMetafilesFinder->findFeatureFiles($testDirectories);
if (count($featureFiles) === 0) {
if ($featureFiles === []) {
$this->symfonyStyle->error('No *.feature files found. Please provide correct test directory');
return self::FAILURE;
}

$contextFiles = $this->behatMetafilesFinder->findContextFiles($testDirectories);
if (count($contextFiles) === 0) {
if ($contextFiles === []) {
$this->symfonyStyle->error('No *Context.php files found. Please provide correct test directory');
return self::FAILURE;
}
Expand Down
14 changes: 6 additions & 8 deletions src/Behastan/Finder/BehatMetafilesFinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,12 @@ public function findContextFiles(array $directories): array
Assert::allString($directories);
Assert::allDirectory($directories);

$phpFilesFinder = Finder::create()
$filesFinder = Finder::create()
->files()
->name('*Context.php')
->in($directories)
->getIterator();
->in($directories);

return iterator_to_array($phpFilesFinder);
return iterator_to_array($filesFinder->getIterator());
}

/**
Expand All @@ -38,12 +37,11 @@ public function findFeatureFiles(array $directories): array
Assert::allString($directories);
Assert::allDirectory($directories);

$featureFilesFinder = Finder::create()
$filesFinder = Finder::create()
->files()
->name('*.feature')
->in($directories)
->getIterator();
->in($directories);

return iterator_to_array($featureFilesFinder);
return iterator_to_array($filesFinder->getIterator());
}
}
4 changes: 2 additions & 2 deletions src/Behastan/ValueObject/MaskCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

namespace Rector\SwissKnife\Behastan\ValueObject;

final class MaskCollection
final readonly class MaskCollection
{
/**
* @param AbstractMask[] $masks
*/
public function __construct(
private readonly array $masks
private array $masks
) {
}

Expand Down
80 changes: 80 additions & 0 deletions src/Command/SearchRegexCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

declare(strict_types=1);

namespace Rector\SwissKnife\Command;

use Nette\Utils\Strings;
use Rector\SwissKnife\Finder\PhpFilesFinder;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Webmozart\Assert\Assert;

final class SearchRegexCommand extends Command
{
public function __construct(
private readonly SymfonyStyle $symfonyStyle,
) {
parent::__construct();
}

protected function configure(): void
{
$this->setName('search-regex');

$this->addArgument(
'regex',
InputArgument::REQUIRED,
'Code snippet to look in PHP files in the whole codebase'
);

$this->addOption('project-directory', null, InputOption::VALUE_REQUIRED, 'Project directory', getcwd());

$this->setDescription('Search for regex in PHP files of the whole codebase');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$regex = (string) $input->getArgument('regex');

$projectDirectory = (string) $input->getOption('project-directory');
Assert::directory($projectDirectory);

$phpFileInfos = PhpFilesFinder::find([$projectDirectory]);

$message = sprintf('Going through %d *.php files', count($phpFileInfos));
$this->symfonyStyle->writeln($message);

$this->symfonyStyle->writeln('Searching for regex: ' . $regex);
$this->symfonyStyle->newLine();

$foundCasesCount = 0;
$markedFileCount = 0;

$progressBar = $this->symfonyStyle->createProgressBar(count($phpFileInfos));

foreach ($phpFileInfos as $phpFileInfo) {
$matches = Strings::matchAll($phpFileInfo->getContents(), $regex);
$currentMatchesCount = count($matches);
if ($currentMatchesCount === 0) {
continue;
}

$foundCasesCount += $currentMatchesCount;
++$markedFileCount;

$progressBar->advance();
}

$progressBar->finish();

$this->symfonyStyle->newLine(2);
$this->symfonyStyle->success(sprintf('Found %d cases in %d files', $foundCasesCount, $markedFileCount));

return self::SUCCESS;
}
}
2 changes: 2 additions & 0 deletions src/DependencyInjection/ContainerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Rector\SwissKnife\Command\NamespaceToPSR4Command;
use Rector\SwissKnife\Command\PrettyJsonCommand;
use Rector\SwissKnife\Command\PrivatizeConstantsCommand;
use Rector\SwissKnife\Command\SearchRegexCommand;
use Rector\SwissKnife\Testing\Command\DetectUnitTestsCommand;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
Expand Down Expand Up @@ -48,6 +49,7 @@ public function create(): Container
$container->make(PrivatizeConstantsCommand::class),

$container->make(BehastanCommand::class),
$container->make(SearchRegexCommand::class),
];

$application->addCommands($commands);
Expand Down
3 changes: 3 additions & 0 deletions src/Finder/PhpFilesFinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ private static function createFinderForPathsAndExcludedPaths(array $paths, array
->in($paths)
->name('*.php')
->notPath('vendor')
->notPath('var')
->notPath('data-fixtures')
->notPath('node_modules')
// exclude paths, as notPaths() does no work
->filter(static function (SplFileInfo $splFileInfo) use ($excludedPaths): bool {
foreach ($excludedPaths as $excludedPath) {
Expand Down
6 changes: 4 additions & 2 deletions src/PhpParser/CachedPhpParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
use PhpParser\Node\Stmt;
use PhpParser\NodeVisitor\NameResolver;
use PhpParser\Parser;
use RuntimeException;
use Throwable;

/**
* Parse file just once
Expand Down Expand Up @@ -36,8 +38,8 @@ public function parseFile(string $filePath): array
$fileContents = FileSystem::read($filePath);
try {
$stmts = $this->phpParser->parse($fileContents);
} catch (\Throwable $throwable) {
throw new \RuntimeException(sprintf(
} catch (Throwable $throwable) {
throw new RuntimeException(sprintf(
'Could not parse file "%s": %s',
$filePath,
$throwable->getMessage()
Expand Down
2 changes: 1 addition & 1 deletion src/PhpParser/Finder/ClassConstFinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
/**
* @see \Rector\SwissKnife\Tests\PhpParser\Finder\ClassConstFinder\ClassConstFinderTest
*/
final class ClassConstFinder
final readonly class ClassConstFinder
{
public function __construct(
private CachedPhpParser $cachedPhpParser
Expand Down
2 changes: 1 addition & 1 deletion src/PhpParser/Finder/ClassConstantFetchFinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
/**
* @see \Rector\SwissKnife\Tests\PhpParser\ClassConstantFetchFinder\ClassConstantFetchFinderTest
*/
final class ClassConstantFetchFinder
final readonly class ClassConstantFetchFinder
{
public function __construct(
private CachedPhpParser $cachedPhpParser,
Expand Down
2 changes: 2 additions & 0 deletions src/PhpParser/NodeVisitor/FindClassConstFetchNodeVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,15 @@ public function enterNode(Node $node): Node|int|null
$this->classConstantFetches[] = new CurrentClassConstantFetch($currentClassName, $constantName);
return $node;
}

// check if parent class is vendor
if ($this->currentClass->extends instanceof Name) {
$parentClassName = $this->currentClass->extends->toString();
if ($this->isVendorClassName($parentClassName)) {
return null;
}
}

$this->classConstantFetches[] = new ParentClassConstantFetch($currentClassName, $constantName);
return $node;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,7 @@ public function enterNode(Node $node): ?Node
}

$methodName = $node->name->toString();
$mockMethodNames = [
'createMock',
'createPartialMock',
'getMock',
'getMockBuilder',
'mock',
];
$mockMethodNames = ['createMock', 'createPartialMock', 'getMock', 'getMockBuilder', 'mock'];
if (! in_array($methodName, $mockMethodNames, true)) {
return null;
}
Expand Down
17 changes: 9 additions & 8 deletions src/Testing/MockWire.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

use InvalidArgumentException;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
use ReflectionMethod;
use ReflectionNamedType;
use ReflectionParameter;

Expand Down Expand Up @@ -60,10 +62,10 @@ public static function create(string $class, array $constructorDependencies = []
));
}

$classReflection = new ReflectionClass($class);
$constructorClassMethod = $classReflection->getConstructor();
$reflectionClass = new ReflectionClass($class);
$constructorClassMethod = $reflectionClass->getConstructor();

if (! $constructorClassMethod instanceof \ReflectionMethod) {
if (! $constructorClassMethod instanceof ReflectionMethod) {
// no dependencies, create it directly
return new $class();
}
Expand All @@ -79,14 +81,13 @@ public static function create(string $class, array $constructorDependencies = []

/**
* @param object[] $constructorDependencies
* @return object|MockObject
*/
private static function matchPassedMockOrCreate(
array $constructorDependencies,
ReflectionParameter $reflectionParameter
): object {
if (! $reflectionParameter->getType() instanceof ReflectionNamedType) {
throw new \InvalidArgumentException(sprintf(
throw new InvalidArgumentException(sprintf(
'Only typed parameters can be automocked. Provide the typehint for "%s" param',
$reflectionParameter->getName()
));
Expand All @@ -106,16 +107,16 @@ private static function matchPassedMockOrCreate(
}

// is bare object type equal to reflection type?
if (get_class($constructorDependency) === $parameterType) {
if ($constructorDependency::class === $parameterType) {
return $constructorDependency;
}
}

// fallback to directly created mock
// support for PHPUnit 10 and 9
$testCaseReflectionClass = new ReflectionClass('PHPUnit\Framework\TestCase');
$testCaseReflectionClass = new ReflectionClass(TestCase::class);
$testCaseConstructor = $testCaseReflectionClass->getConstructor();
if ($testCaseConstructor instanceof \ReflectionMethod && $testCaseConstructor->getNumberOfRequiredParameters() > 0) {
if ($testCaseConstructor instanceof ReflectionMethod && $testCaseConstructor->getNumberOfRequiredParameters() > 0) {
$phpunitMocker = new PHPUnitMocker('testName');
} else {
$phpunitMocker = new PHPUnitMocker();
Expand Down
4 changes: 2 additions & 2 deletions src/Twig/TwigTemplateConstantExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ private function findClassConstantFetchesInFile(string $filePath): array
$constantMatchValue = $constantMatch['constant'];

// global constant → skip
if (! str_contains($constantMatchValue, '::')) {
if (! str_contains((string) $constantMatchValue, '::')) {
continue;
}

[$className, $constantName] = explode('::', $constantMatchValue);
[$className, $constantName] = explode('::', (string) $constantMatchValue);
$className = str_replace('\\\\', '\\', $className);

$externalClassAccessConstantFetches[] = new ExternalClassAccessConstantFetch($className, $constantName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,19 @@
use Rector\SwissKnife\Tests\AbstractTestCase;
use Rector\SwissKnife\ValueObject\ClassConstantFetch\CurrentClassConstantFetch;
use Rector\SwissKnife\ValueObject\ClassConstantFetch\ExternalClassAccessConstantFetch;
use RuntimeException;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\NullOutput;

final class ClassConstantFetchFinderTest extends AbstractTestCase
{
private ClassConstantFetchFinder $classConstantsFetchFinder;
private ClassConstantFetchFinder $classConstantFetchFinder;

protected function setUp(): void
{
parent::setUp();

$this->classConstantsFetchFinder = $this->make(ClassConstantFetchFinder::class);
$this->classConstantFetchFinder = $this->make(ClassConstantFetchFinder::class);
}

public function testSkipInterfaceTraitAndEnum(): void
Expand All @@ -44,15 +45,15 @@ public function test(): void

public function testParseError(): void
{
$this->expectException(\RuntimeException::class);
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage(
'Could not parse file "' . __DIR__ . '/Fixture/Error/ParseError.php": Syntax error, unexpected T_STRING on line 6'
);

$directory = __DIR__ . '/Fixture/Error';
$progressBar = new ProgressBar(new NullOutput());
$fileInfos = PhpFilesFinder::find([$directory]);
$this->classConstantsFetchFinder->find($fileInfos, $progressBar, false);
$this->classConstantFetchFinder->find($fileInfos, $progressBar, false);
}

/**
Expand All @@ -63,6 +64,6 @@ private function findInDirectory(string $directory): array
$progressBar = new ProgressBar(new NullOutput());
$fileInfos = PhpFilesFinder::find([$directory]);

return $this->classConstantsFetchFinder->find($fileInfos, $progressBar, false);
return $this->classConstantFetchFinder->find($fileInfos, $progressBar, false);
}
}
Loading

0 comments on commit bf913bf

Please sign in to comment.