Skip to content

Commit

Permalink
Add YAML config discovery of entities for finalize-classes command (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasVotruba committed Feb 13, 2024
1 parent 9732841 commit 591898d
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 16 deletions.
4 changes: 2 additions & 2 deletions src/Command/FinalizeClassesCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$this->symfonyStyle->title('1. Detecting parent and entity classes');

$phpFileInfos = PhpFilesFinder::findPhpFileInfos($paths);
$phpFileInfos = PhpFilesFinder::find($paths);

// double to count for both parent and entity resolver
$this->symfonyStyle->progressStart(2 * count($phpFileInfos));
Expand All @@ -69,7 +69,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
};

$parentClassNames = $this->parentClassResolver->resolve($phpFileInfos, $progressClosure);
$entityClassNames = $this->entityClassResolver->resolve($phpFileInfos, $progressClosure);
$entityClassNames = $this->entityClassResolver->resolve($paths, $progressClosure);

$this->symfonyStyle->progressFinish();

Expand Down
59 changes: 52 additions & 7 deletions src/EntityClassResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,56 @@

namespace Rector\SwissKnife;

use Closure;
use PhpParser\NodeTraverser;
use Rector\SwissKnife\Finder\PhpFilesFinder;
use Rector\SwissKnife\Finder\YamlFilesFinder;
use Rector\SwissKnife\PhpParser\CachedPhpParser;
use Rector\SwissKnife\PhpParser\NodeVisitor\EntityClassNameCollectingNodeVisitor;
use Symfony\Component\Finder\SplFileInfo;
use Webmozart\Assert\Assert;

/**
* @see \Rector\SwissKnife\Tests\EntityClassResolver\EntityClassResolverTest
*/
final readonly class EntityClassResolver
{
/**
* @var string
* @see https://regex101.com/r/YFbH1x/1
*/
private const YAML_ENTITY_CLASS_NAME_REGEX = '#^(?<class_name>[\w+\\\\]+)\:\n#m';
public function __construct(
private CachedPhpParser $cachedPhpParser
) {
}
/**
* @param SplFileInfo[] $phpFileInfos
* @param string[] $paths
* @return string[]
*/
public function resolve(array $phpFileInfos, Closure $progressClosure): array
public function resolve(array $paths, ?callable $progressClosure = null): array
{
Assert::allString($paths);
Assert::allFileExists($paths);
// 1. resolve from yaml annotations
$yamlEntityClassNames = $this->resolveYamlEntityClassNames($paths);
// 2. resolve from direct class names with namespace parts, doctrine annotation or docblock
$phpFileInfos = PhpFilesFinder::find($paths);
$entityClassNameCollectingNodeVisitor = new EntityClassNameCollectingNodeVisitor();
$nodeTraverser = new NodeTraverser();
$nodeTraverser->addVisitor($entityClassNameCollectingNodeVisitor);

$this->traverseFileInfos($phpFileInfos, $nodeTraverser, $progressClosure);
return $entityClassNameCollectingNodeVisitor->getEntityClassNames();
$markedEntityClassNames = $entityClassNameCollectingNodeVisitor->getEntityClassNames();
$entityClassNames = array_merge($yamlEntityClassNames, $markedEntityClassNames);
sort($entityClassNames);
return array_unique($entityClassNames);
}
/**
Expand All @@ -42,13 +62,38 @@ public function resolve(array $phpFileInfos, Closure $progressClosure): array
private function traverseFileInfos(
array $phpFileInfos,
NodeTraverser $nodeTraverser,
callable $progressClosure
?callable $progressClosure = null
): void {
foreach ($phpFileInfos as $phpFileInfo) {
$stmts = $this->cachedPhpParser->parseFile($phpFileInfo->getRealPath());
$nodeTraverser->traverse($stmts);
$progressClosure();
if (is_callable($progressClosure)) {
$progressClosure();
}
}
}
/**
* @param string[] $paths
* @return string[]
*/
private function resolveYamlEntityClassNames(array $paths): array
{
$yamlFileInfos = YamlFilesFinder::find($paths);
$yamlEntityClassNames = [];
/** @var SplFileInfo $yamlFileInfo */
foreach ($yamlFileInfos as $yamlFileInfo) {
$matches = \Nette\Utils\Strings::matchAll($yamlFileInfo->getContents(), self::YAML_ENTITY_CLASS_NAME_REGEX);
foreach ($matches as $match) {
$yamlEntityClassNames[] = $match['class_name'];
}
}

return $yamlEntityClassNames;
}
}
10 changes: 7 additions & 3 deletions src/Finder/PhpFilesFinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,24 @@

use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use Webmozart\Assert\Assert;

final class PhpFilesFinder
{
/**
* @param string[] $paths
* @return SplFileInfo[]
*/
public static function findPhpFileInfos(array $paths): array
public static function find(array $paths): array
{
$phpFinder = Finder::create()
Assert::allString($paths);
Assert::allFileExists($paths);

$finder = Finder::create()
->files()
->in($paths)
->name('*.php');

return iterator_to_array($phpFinder);
return iterator_to_array($finder);
}
}
30 changes: 30 additions & 0 deletions src/Finder/YamlFilesFinder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace Rector\SwissKnife\Finder;

use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use Webmozart\Assert\Assert;

final class YamlFilesFinder
{
/**
* @param string[] $paths
* @return SplFileInfo[]
*/
public static function find(array $paths): array
{
Assert::allString($paths);
Assert::allFileExists($paths);

$finder = Finder::create()
->files()
->in($paths)
->name('*.yml')
->name('*.yaml');

return iterator_to_array($finder);
}
}
9 changes: 5 additions & 4 deletions tests/EntityClassResolver/EntityClassResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace Rector\SwissKnife\Tests\EntityClassResolver;

use Rector\SwissKnife\EntityClassResolver;
use Rector\SwissKnife\Finder\PhpFilesFinder;
use Rector\SwissKnife\Tests\AbstractTestCase;
use Rector\SwissKnife\Tests\EntityClassResolver\Fixture\Entity\SomeEntity;

Expand All @@ -22,10 +21,12 @@ protected function setUp(): void

public function test(): void
{
$fileInfos = PhpFilesFinder::findPhpFileInfos([__DIR__ . '/Fixture']);
$entityClasses = $this->entityClassResolver->resolve($fileInfos, function () {
$entityClasses = $this->entityClassResolver->resolve([__DIR__ . '/Fixture'], function () {
});

$this->assertSame([SomeEntity::class], $entityClasses);
$this->assertSame(
['App\Some\Entity\Conference', 'App\Some\Entity\Project', 'App\Some\Entity\Talk', SomeEntity::class],
$entityClasses
);
}
}
7 changes: 7 additions & 0 deletions tests/EntityClassResolver/Fixture/config/next.orm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
App\Some\Entity\Talk:
type: entity
manyToOne:
property:

App\Some\Entity\Project:
type: entity
2 changes: 2 additions & 0 deletions tests/EntityClassResolver/Fixture/config/some.orm.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
App\Some\Entity\Conference:
type: entity

0 comments on commit 591898d

Please sign in to comment.