Skip to content

Commit 76f5f2f

Browse files
authored
Token classifier & refactor filters (#41)
1 parent 2db3db7 commit 76f5f2f

28 files changed

+696
-111
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@ jobs:
2828
os: >-
2929
['ubuntu-latest', 'windows-latest']
3030
php: >-
31-
['8.0', '8.1']
31+
['8.1', '8.2']

.github/workflows/composer-require-checker.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,4 @@ jobs:
3030
os: >-
3131
['ubuntu-latest']
3232
php: >-
33-
['8.0']
33+
['8.1']

.github/workflows/mutation.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@ jobs:
2626
os: >-
2727
['ubuntu-latest']
2828
php: >-
29-
['8.1']
29+
['8.2']
3030
secrets:
3131
STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }}

.github/workflows/rector.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ jobs:
1818
os: >-
1919
['ubuntu-latest']
2020
php: >-
21-
['8.0']
21+
['8.2']

.github/workflows/static.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@ jobs:
2828
os: >-
2929
['ubuntu-latest']
3030
php: >-
31-
['8.0', '8.1']
31+
['8.1', '8.2']

.styleci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ finder:
99
- vendor
1010
not-name:
1111
- wrong_file.php
12+
- namespace.php
1213

1314
enabled:
1415
- alpha_ordered_traits

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
"minimum-stability": "dev",
2929
"prefer-stable": true,
3030
"require": {
31-
"php": "^8.0",
31+
"php": "^8.1",
32+
"ext-tokenizer": "*",
3233
"symfony/finder": "^5.4|^6.0"
3334
},
3435
"require-dev": {

src/AbstractClassifier.php

Lines changed: 41 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,24 @@
44

55
namespace Yiisoft\Classifier;
66

7+
use ReflectionClass;
78
use Symfony\Component\Finder\Finder;
9+
use Yiisoft\Classifier\Filter\FilterInterface;
810

911
/**
1012
* Base implementation for {@see ClassifierInterface} with common filters.
1113
*/
1214
abstract class AbstractClassifier implements ClassifierInterface
1315
{
1416
/**
15-
* @var string[]
17+
* @var array<class-string|trait-string, ReflectionClass>
1618
*/
17-
protected array $interfaces = [];
18-
/**
19-
* @var string[]
20-
*/
21-
protected array $attributes = [];
19+
protected static array $reflectionsCache = [];
20+
2221
/**
23-
* @psalm-var class-string
22+
* @var FilterInterface[]
2423
*/
25-
protected ?string $parentClass = null;
24+
private array $filters = [];
2625
/**
2726
* @var string[]
2827
*/
@@ -33,48 +32,25 @@ public function __construct(string $directory, string ...$directories)
3332
$this->directories = [$directory, ...array_values($directories)];
3433
}
3534

36-
/**
37-
* @psalm-param class-string ...$interfaces
38-
*/
39-
public function withInterface(string ...$interfaces): self
40-
{
41-
$new = clone $this;
42-
array_push($new->interfaces, ...array_values($interfaces));
43-
44-
return $new;
45-
}
46-
47-
/**
48-
* @psalm-param class-string $parentClass
49-
*/
50-
public function withParentClass(string $parentClass): self
51-
{
52-
$new = clone $this;
53-
$new->parentClass = $parentClass;
54-
return $new;
55-
}
56-
57-
/**
58-
* @psalm-param class-string ...$attributes
59-
*/
60-
public function withAttribute(string ...$attributes): self
35+
public function withFilter(FilterInterface ...$filter): static
6136
{
6237
$new = clone $this;
63-
array_push($new->attributes, ...array_values($attributes));
38+
array_push($new->filters, ...array_values($filter));
6439

6540
return $new;
6641
}
6742

6843
/**
69-
* @psalm-return iterable<class-string>
44+
* @return iterable<class-string>
7045
*/
7146
public function find(): iterable
7247
{
73-
if (empty($this->interfaces) && empty($this->attributes) && $this->parentClass === null) {
74-
return [];
48+
foreach ($this->getAvailableDeclarations() as $declaration) {
49+
if ($this->skipDeclaration($declaration)) {
50+
continue;
51+
}
52+
yield $declaration;
7553
}
76-
77-
yield from $this->getAvailableClasses();
7854
}
7955

8056
protected function getFiles(): Finder
@@ -87,7 +63,31 @@ protected function getFiles(): Finder
8763
}
8864

8965
/**
90-
* @return iterable<class-string>
66+
* @param class-string|trait-string $declaration
67+
*/
68+
private function skipDeclaration(string $declaration): bool
69+
{
70+
try {
71+
$reflectionClass = self::$reflectionsCache[$declaration] ??= new ReflectionClass($declaration);
72+
} catch (\Throwable) {
73+
return true;
74+
}
75+
76+
if ($reflectionClass->isInternal() || $reflectionClass->isAnonymous()) {
77+
return true;
78+
}
79+
80+
foreach ($this->filters as $filter) {
81+
if (!$filter->match($reflectionClass)) {
82+
return true;
83+
}
84+
}
85+
86+
return false;
87+
}
88+
89+
/**
90+
* @return iterable<class-string|trait-string>
9191
*/
92-
abstract protected function getAvailableClasses(): iterable;
92+
abstract protected function getAvailableDeclarations(): iterable;
9393
}

src/ClassifierInterface.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,19 @@
44

55
namespace Yiisoft\Classifier;
66

7+
use Yiisoft\Classifier\Filter\FilterInterface;
8+
79
/**
810
* `Classifier` is a class finder that represents the classes found.
911
*/
1012
interface ClassifierInterface
1113
{
14+
public function withFilter(FilterInterface ...$filter): static;
15+
1216
/**
1317
* Returns all the class names found.
1418
*
15-
* @return iterable List of class names.
16-
* @psalm-return iterable<class-string>
19+
* @return iterable<class-string> List of class names.
1720
*/
1821
public function find(): iterable;
1922
}

src/Filter/ClassAttributes.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Classifier\Filter;
6+
7+
use ReflectionAttribute;
8+
use ReflectionClass;
9+
10+
final class ClassAttributes implements FilterInterface
11+
{
12+
private array $attributes;
13+
14+
public function __construct(string ...$attributes)
15+
{
16+
$this->attributes = $attributes;
17+
}
18+
19+
public function match(ReflectionClass $reflectionClass): bool
20+
{
21+
if (empty($this->attributes)) {
22+
return false;
23+
}
24+
25+
$attributes = $reflectionClass->getAttributes();
26+
$attributeNames = array_map(
27+
static fn(ReflectionAttribute $attribute) => $attribute->getName(),
28+
$attributes
29+
);
30+
31+
return count(array_intersect($this->attributes, $attributeNames)) === count($this->attributes);
32+
}
33+
}

0 commit comments

Comments
 (0)