Skip to content

Commit e6a4cb9

Browse files
committed
Extract collaborators from spec instead of scan soruce files 🚀
1 parent 04dd44e commit e6a4cb9

File tree

4 files changed

+69
-21
lines changed

4 files changed

+69
-21
lines changed

extension.neon

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,5 @@ extensions:
77
phpspec: Proget\PHPStan\PhpSpec\DependencyInjection\PhpSpecDynamicMethodReturnTypeExtensionCreator
88

99
parameters:
10-
phpspecSourceFiles:
11-
- 'src/'
12-
- 'tests/'
10+
specDirs:
11+
- 'spec/'

src/DependencyInjection/PhpSpecDynamicMethodReturnTypeExtensionCreator.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66

77
use Nette\DI\CompilerExtension;
88
use Nette\DI\ServiceDefinition;
9-
use Proget\PHPStan\PhpSpec\Locator\SourceClassLocator;
9+
use Proget\PHPStan\PhpSpec\Extractor\CollaboratorExtractor;
10+
use Proget\PHPStan\PhpSpec\Locator\SpecClassLocator;
1011
use Proget\PHPStan\PhpSpec\Reflection\CollaboratorDynamicMethodReturnTypeExtension;
1112

1213
final class PhpSpecDynamicMethodReturnTypeExtensionCreator extends CompilerExtension
@@ -15,18 +16,18 @@ public function beforeCompile()
1516
{
1617
$builder = $this->getContainerBuilder();
1718
$workingDir = $this->getContainerBuilder()->parameters['currentWorkingDirectory'];
18-
$classes = (new SourceClassLocator())->locate(array_map(function (string $dir) use ($workingDir) {
19+
$specClasses = (new SpecClassLocator())->locate(array_map(function (string $dir) use ($workingDir) {
1920
return $workingDir.DIRECTORY_SEPARATOR.ltrim($dir, DIRECTORY_SEPARATOR);
20-
}, $this->getContainerBuilder()->parameters['phpspecSourceFiles']));
21+
}, $this->getContainerBuilder()->parameters['specDirs']));
2122

22-
foreach ($classes as $class) {
23+
foreach ((new CollaboratorExtractor())->extract($specClasses) as $class) {
2324
$definition = new ServiceDefinition();
2425
$definition->addTag('phpstan.broker.dynamicMethodReturnTypeExtension');
2526
$definition->setType(CollaboratorDynamicMethodReturnTypeExtension::class);
2627
$definition->setArguments([$class]);
2728

2829
$builder->addDefinition(
29-
'collaborator.'.strtolower((string) preg_replace('/[^a-zA-Z0-9]+/', '', (string) $class)),
30+
'collaborator.'.strtolower((string) preg_replace('/[^a-zA-Z0-9]+/', '', $class)),
3031
$definition
3132
);
3233
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Proget\PHPStan\PhpSpec\Extractor;
6+
7+
final class CollaboratorExtractor
8+
{
9+
/**
10+
* @param string[] $classes
11+
*
12+
* @return string[]
13+
*/
14+
public function extract(array $classes): array
15+
{
16+
$collaborators = [];
17+
foreach ($classes as $className) {
18+
$reflection = new \ReflectionClass($className);
19+
$this->extractFromMethods($reflection->getMethods(\ReflectionMethod::IS_PUBLIC), $collaborators);
20+
}
21+
22+
return array_unique($collaborators);
23+
}
24+
25+
/**
26+
* @param \ReflectionMethod[] $methods
27+
* @param string[] $collaborators
28+
*/
29+
private function extractFromMethods(array $methods, array &$collaborators): void
30+
{
31+
foreach ($methods as $method) {
32+
if (!preg_match('/^(it|its)[^a-zA-Z]/', $method->getName())) {
33+
continue;
34+
}
35+
$this->extractFromAttributes($method->getParameters(), $collaborators);
36+
}
37+
}
38+
39+
/**
40+
* @param \ReflectionParameter[] $parameters
41+
* @param string[] $collaborators
42+
*/
43+
private function extractFromAttributes(array $parameters, array &$collaborators): void
44+
{
45+
foreach ($parameters as $parameter) {
46+
$type = $parameter->getType();
47+
if ($type !== null) {
48+
$collaborators[] = $type->getName();
49+
}
50+
}
51+
}
52+
}
Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,19 @@
77
use Symfony\Component\Finder\Finder;
88
use Symfony\Component\Finder\SplFileInfo;
99

10-
final class SourceClassLocator
10+
final class SpecClassLocator
1111
{
1212
public function locate(array $dirs): array
1313
{
14-
$finder = new Finder();
14+
$finder = (new Finder())->in($dirs)->name('*.php');
1515

16-
$nonFinalClasses = [];
17-
foreach (iterator_to_array($finder->in($dirs)->name('*.php')->files()) as $file) {
18-
$className = $this->getClassName($file);
19-
if ($className === null || (new \ReflectionClass($className))->isFinal()) {
20-
continue;
21-
}
22-
23-
$nonFinalClasses[] = $className;
24-
}
16+
$classes = array_map(function (SplFileInfo $fileInfo): string {
17+
return (string) $this->getClassName($fileInfo);
18+
}, iterator_to_array($finder->files()));
2519

26-
return $nonFinalClasses;
20+
return array_values(array_filter($classes, function (string $className): bool {
21+
return preg_match('/Spec$/', $className) !== false;
22+
}));
2723
}
2824

2925
private function getClassName(SplFileInfo $fileInfo): ?string
@@ -44,7 +40,7 @@ private function getClassName(SplFileInfo $fileInfo): ?string
4440
}
4541
}
4642

47-
if ($tokens[$i][0] === T_CLASS || $tokens[$i][0] === T_INTERFACE) {
43+
if ($tokens[$i][0] === T_CLASS) {
4844
for ($j = $i + 1; $j < $count; ++$j) {
4945
if ($tokens[$j] === '{') {
5046
return $namespace.$tokens[$i + 2][1];

0 commit comments

Comments
 (0)