Skip to content

Commit

Permalink
feat: support phpdocumentor reflection 6 (includes php attributes sup…
Browse files Browse the repository at this point in the history
…port)
  • Loading branch information
alekitto committed Apr 13, 2024
1 parent 3ee1794 commit 6a125b3
Show file tree
Hide file tree
Showing 11 changed files with 174 additions and 115 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
},
"require-dev": {
"doctrine/annotations": "^1.0",
"phpdocumentor/reflection": "^4.0 || ^5.0",
"phpdocumentor/reflection": "^4.0 || ^5.0 || ^6.0@dev",
"phpunit/phpunit": "^10.5",
"roave/better-reflection": "^6.0",
"roave/security-advisories": "dev-master",
Expand Down
199 changes: 109 additions & 90 deletions composer.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ final class AnnotationFilterIterator extends FilterIterator
* @param Iterator<Element> $iterator
* @phpstan-param class-string $annotation
*/
public function __construct(Iterator $iterator, private string $annotation)
public function __construct(Iterator $iterator, private readonly string $annotation)
{
parent::__construct($iterator);
}
Expand Down
18 changes: 14 additions & 4 deletions lib/FilterIterator/PhpDocumentor/AttributeFilterIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,25 @@
use FilterIterator;
use Iterator;
use phpDocumentor\Reflection\Element;
use phpDocumentor\Reflection\Php\Attribute;
use phpDocumentor\Reflection\Php\Class_;
use phpDocumentor\Reflection\Php\Interface_;
use phpDocumentor\Reflection\Php\Trait_;
use RuntimeException;

use function array_filter;
use function assert;
use function count;
use function ltrim;
use function method_exists;

final class AttributeFilterIterator extends FilterIterator
{
// phpcs:ignore SlevomatCodingStandard.Classes.UnusedPrivateElements.WriteOnlyProperty

/**
* @param Iterator<Element> $iterator
* @phpstan-param class-string $attribute
*/
public function __construct(Iterator $iterator, private string $attribute) // @phpstan-ignore-line
public function __construct(Iterator $iterator, private readonly string $attribute)
{
parent::__construct($iterator);
}
Expand All @@ -32,6 +35,13 @@ public function accept(): bool
$reflector = $this->getInnerIterator()->current();
assert($reflector instanceof Class_ || $reflector instanceof Interface_ || $reflector instanceof Trait_);

throw new RuntimeException('Attributes support is not implemented in phpdocumentor');
if (! method_exists($reflector, 'getAttributes')) {
throw new RuntimeException('Attributes support is not implemented in phpdocumentor');
}

return count(array_filter(
$reflector->getAttributes(),
fn (Attribute $attr): bool => ltrim((string) $attr->getFqsen(), '\\') === $this->attribute,
)) > 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ final class InterfaceImplementationFilterIterator extends FilterIterator
* @param string[] $interfaces
* @phpstan-param class-string[] $interfaces
*/
public function __construct(Iterator $iterator, private array $interfaces)
public function __construct(Iterator $iterator, private readonly array $interfaces)
{
parent::__construct($iterator);
}
Expand Down
8 changes: 4 additions & 4 deletions lib/FilterIterator/PhpDocumentor/NamespaceFilterIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@

use function assert;
use function ltrim;
use function Safe\substr;
use function strpos;
use function str_starts_with;
use function strrpos;
use function substr;

final class NamespaceFilterIterator extends FilterIterator
{
/**
* @param Iterator<Element> $iterator
* @param string[] $namespaces
*/
public function __construct(Iterator $iterator, private array $namespaces)
public function __construct(Iterator $iterator, private readonly array $namespaces)
{
parent::__construct($iterator);
}
Expand All @@ -35,7 +35,7 @@ public function accept(): bool
$classNamespace = ltrim($index !== false ? substr($fqen, 0, $index) : $fqen, '\\');

foreach ($this->namespaces as $namespace) {
if ($classNamespace === $namespace || strpos($classNamespace, $namespace . '\\') === 0) {
if ($classNamespace === $namespace || str_starts_with($classNamespace, $namespace . '\\')) {
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@

use function assert;
use function ltrim;
use function Safe\substr;
use function strpos;
use function str_starts_with;
use function strrpos;
use function substr;

final class NotNamespaceFilterIterator extends FilterIterator
{
/**
* @param Iterator<Element> $iterator
* @param string[] $namespaces
*/
public function __construct(Iterator $iterator, private array $namespaces)
public function __construct(Iterator $iterator, private readonly array $namespaces)
{
parent::__construct($iterator);
}
Expand All @@ -35,7 +35,7 @@ public function accept(): bool
$classNamespace = ltrim($index !== false ? substr($fqen, 0, $index) : $fqen, '\\');

foreach ($this->namespaces as $namespace) {
if ($classNamespace === $namespace || strpos($classNamespace, $namespace . '\\') === 0) {
if ($classNamespace === $namespace || str_starts_with($classNamespace, $namespace . '\\')) {
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ final class SuperClassFilterIterator extends FilterIterator
* @param Iterator<Element> $iterator
* @phpstan-param class-string $superClass
*/
public function __construct(Iterator $iterator, private string $superClass)
public function __construct(Iterator $iterator, private readonly string $superClass)
{
parent::__construct($iterator);
}
Expand Down
2 changes: 1 addition & 1 deletion lib/Finder/ComposerFinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ final class ComposerFinder implements FinderInterface
private ReflectorFactoryInterface|null $reflectorFactory = null;

/** @var array<string, string> */
private array $files = [];
private array $files;
private bool $useAutoloading = true;

public function __construct(ClassLoader|null $loader = null)
Expand Down
35 changes: 31 additions & 4 deletions lib/Iterator/PhpDocumentorIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Kcs\ClassFinder\FilterIterator\Reflection\PathFilterIterator;
use Kcs\ClassFinder\PathNormalizer;
use phpDocumentor\Reflection\DocBlockFactory;
use phpDocumentor\Reflection\DocBlockFactoryInterface;
use phpDocumentor\Reflection\File\LocalFile;
use phpDocumentor\Reflection\Php\Class_;
use phpDocumentor\Reflection\Php\Factory;
Expand Down Expand Up @@ -62,7 +63,7 @@ final class PhpDocumentorIterator extends ClassIterator
/** @var string[] */
private array|null $notPaths = null;
private ProjectFactoryStrategies $strategies;
private DocBlockFactory $docBlockFactory;
private DocBlockFactory|DocBlockFactoryInterface $docBlockFactory;

public function __construct(string $path, int $flags = 0, Closure|null $pathCallback = null)
{
Expand All @@ -71,7 +72,7 @@ public function __construct(string $path, int $flags = 0, Closure|null $pathCall
if (class_exists(Factory\DocBlock::class)) {
// phpdoc/reflection 4.x
$this->strategies = new ProjectFactoryStrategies([ // @phpstan-ignore-line
new Factory\Argument(new PrettyPrinter()),
new Factory\Argument(new PrettyPrinter()), // @phpstan-ignore-line
new Factory\Class_(), // @phpstan-ignore-line
new Factory\Define(new PrettyPrinter()), // @phpstan-ignore-line
new Factory\GlobalConstant(new PrettyPrinter()), // @phpstan-ignore-line
Expand All @@ -84,8 +85,8 @@ public function __construct(string $path, int $flags = 0, Closure|null $pathCall
new Factory\Property(new PrettyPrinter()), // @phpstan-ignore-line
new Factory\Trait_(), // @phpstan-ignore-line
]);
} else {
$this->strategies = new ProjectFactoryStrategies([
} elseif (class_exists(Factory\Argument::class)) {
$this->strategies = new ProjectFactoryStrategies([ // @phpstan-ignore-line
new Factory\Argument(new PrettyPrinter()),
new Factory\Class_($this->docBlockFactory),
new Factory\Define($this->docBlockFactory, new PrettyPrinter()),
Expand All @@ -100,6 +101,32 @@ public function __construct(string $path, int $flags = 0, Closure|null $pathCall
new Factory\Trait_($this->docBlockFactory),
]);

$this->strategies->addStrategy(new Factory\Noop(), -1000);
} else {
$attributeReducer = new Factory\Reducer\Attribute();
$parameterReducer = new Factory\Reducer\Parameter(new PrettyPrinter());
$methodStrategy = new Factory\Method($this->docBlockFactory, [$attributeReducer, $parameterReducer]);

$this->strategies = new ProjectFactoryStrategies([
new Factory\Namespace_(),
new Factory\Class_($this->docBlockFactory, [$attributeReducer]),
new Factory\Enum_($this->docBlockFactory, [$attributeReducer]),
new Factory\EnumCase($this->docBlockFactory, new PrettyPrinter()),
new Factory\Define($this->docBlockFactory, new PrettyPrinter()),
new Factory\GlobalConstant($this->docBlockFactory, new PrettyPrinter()),
new Factory\ClassConstant($this->docBlockFactory, new PrettyPrinter()),
new Factory\File($this->docBlockFactory, NodesFactory::createInstance()),
new Factory\Function_($this->docBlockFactory, [$attributeReducer, $parameterReducer]),
new Factory\Interface_($this->docBlockFactory),
$methodStrategy,
new Factory\Property($this->docBlockFactory, new PrettyPrinter()),
new Factory\Trait_($this->docBlockFactory),

new Factory\IfStatement(),
new Factory\TraitUse(),
]);

$this->strategies->addStrategy(new Factory\ConstructorPromotion($methodStrategy, $this->docBlockFactory, new PrettyPrinter()), -1000);
$this->strategies->addStrategy(new Factory\Noop(), -1000);
}

Expand Down
11 changes: 7 additions & 4 deletions tests/unit/Finder/PhpDocumentorFinderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,14 @@ public function testFinderShouldFilterByAttribute(): void
$finder->withAttribute(Psr4\SubNs\FooBaz::class);

// Not implemented yet
$this->expectException(RuntimeException::class);
/* $classes = */ iterator_to_array($finder);
if (!method_exists(Class_::class, 'getAttributes')) {
$this->expectException(RuntimeException::class);
}

// self::assertArrayHasKey(Psr4\AbstractClass::class, $classes);
// self::assertInstanceOf(Class_::class, $classes[Psr4\AbstractClass::class]);
$classes = iterator_to_array($finder);

self::assertArrayHasKey(Psr4\AbstractClass::class, $classes);
self::assertInstanceOf(Class_::class, $classes[Psr4\AbstractClass::class]);
}

public function testFinderShouldFilterByCallback(): void
Expand Down

0 comments on commit 6a125b3

Please sign in to comment.