Skip to content

Commit

Permalink
Improve AnnotatedTargetParserOptions into a value object
Browse files Browse the repository at this point in the history
  • Loading branch information
cspray committed Jul 5, 2024
1 parent a28fcdc commit 5bb1bde
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 137 deletions.
28 changes: 22 additions & 6 deletions src/AnnotatedTargetParserOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,32 @@

namespace Cspray\AnnotatedTarget;

interface AnnotatedTargetParserOptions {
final class AnnotatedTargetParserOptions {

/**
* @return non-empty-list<non-empty-string>
* @param non-empty-list<non-empty-string> $sourceDirectories
* @param list<class-string> $filteredAttributes
*/
public function sourceDirectories() : array;
private function __construct(
public readonly array $sourceDirectories,
public readonly array $filteredAttributes
) {}

/**
* @return non-empty-list<class-string>
* @param non-empty-string $path
* @param non-empty-string ...$additionalPaths
* @return self
*/
public function attributeTypes() : array;
public static function scanAllAttributes(string $path, string... $additionalPaths) : self {
return new self(array_values([$path, ...$additionalPaths]), []);
}

}
/**
* @param non-empty-list<non-empty-string> $paths
* @param non-empty-list<class-string> $attributes
*/
public static function scanForSpecificAttributes(array $paths, array $attributes) : self {
return new self($paths, $attributes);
}

}
56 changes: 0 additions & 56 deletions src/AnnotatedTargetParserOptionsBuilder.php

This file was deleted.

52 changes: 33 additions & 19 deletions src/PhpParserAnnotatedTargetParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Cspray\AnnotatedTarget;

use Closure;
use Cspray\AnnotatedTarget\Exception\InvalidPhpSyntax;
use FilesystemIterator;
use Generator;
Expand Down Expand Up @@ -36,11 +37,12 @@ public function parse(AnnotatedTargetParserOptions $options) : Generator {
$nodeTraverser = new NodeTraverser();
$nodeTraverser->addVisitor(new NodeVisitor\NodeConnectingVisitor());
$nodeTraverser->addVisitor(new NodeVisitor\NameResolver());
/** @var \stdClass{targets: list<AnnotatedTarget>} $data */
$data = new \stdClass();
$data->targets = [];
$nodeTraverser->addVisitor($this->getVisitor(
static fn($target) => $data->targets[] = $target,
$options->attributeTypes()
static fn(AnnotatedTarget $target) => $data->targets[] = $target,
$options->filteredAttributes
));

foreach ($this->getSourceIterator($options) as $sourceFile) {
Expand All @@ -61,7 +63,7 @@ public function parse(AnnotatedTargetParserOptions $options) : Generator {
}

private function getSourceIterator(AnnotatedTargetParserOptions $options) : Iterator {
foreach ($options->sourceDirectories() as $directory) {
foreach ($options->sourceDirectories as $directory) {
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($directory, FilesystemIterator::SKIP_DOTS)
);
Expand Down Expand Up @@ -126,25 +128,25 @@ private function isAttributeInstanceOfFilteredAttribute(string $attrType) : bool

private function getAnnotatedTargetFromClassNode(Node\Stmt\Class_|Node\Stmt\Interface_ $class, int $index) : AnnotatedTarget {
$classType = $class->namespacedName->toString();
return $this->getAnnotatedTarget(fn() => new ReflectionClass($classType), $index);
return $this->getAnnotatedTarget(static fn() : ReflectionClass => new ReflectionClass($classType), $index);
}

private function getAnnotatedTargetFromPropertyNode(Node\Stmt\PropertyProperty $property, int $index) : AnnotatedTarget {
$classType = $property->getAttribute('parent')->getAttribute('parent')->namespacedName->toString();
$propertyName = $property->name->toString();
return $this->getAnnotatedTarget(fn() => new ReflectionProperty($classType, $propertyName), $index);
return $this->getAnnotatedTarget(static fn() : ReflectionProperty => new ReflectionProperty($classType, $propertyName), $index);
}

private function getAnnotatedTargetFromClassConstantNode(Node\Const_ $classConst, int $index) : AnnotatedTarget {
$classType = $classConst->getAttribute('parent')->getAttribute('parent')->namespacedName->toString();
$constName = $classConst->name->toString();
return $this->getAnnotatedTarget(fn() => new ReflectionClassConstant($classType, $constName), $index);
return $this->getAnnotatedTarget(static fn() : ReflectionClassConstant => new ReflectionClassConstant($classType, $constName), $index);
}

private function getAnnotatedTargetFromMethodNode(Node\Stmt\ClassMethod $classMethod, int $index) : AnnotatedTarget {
$classType = $classMethod->getAttribute('parent')->namespacedName->toString();
$methodName = $classMethod->name->toString();
return $this->getAnnotatedTarget(fn() => new ReflectionMethod(sprintf('%s::%s', $classType, $methodName)), $index);
return $this->getAnnotatedTarget(static fn() : ReflectionMethod => new ReflectionMethod(sprintf('%s::%s', $classType, $methodName)), $index);
}

private function getAnnotatedTargetFromMethodParameter(Node\Param $param, int $index) : AnnotatedTarget {
Expand All @@ -157,45 +159,57 @@ private function getAnnotatedTargetFromMethodParameter(Node\Param $param, int $i
$callable = $paramParent->namespacedName->toString();
}
$paramName = $param->var->name;
return $this->getAnnotatedTarget(fn() => new ReflectionParameter($callable, $paramName), $index);
return $this->getAnnotatedTarget(static fn() : ReflectionParameter => new ReflectionParameter($callable, $paramName), $index);
}

private function getAnnotatedTargetFromFunction(Node\Stmt\Function_ $function, int $index) : AnnotatedTarget {
$function = $function->namespacedName->toString();
return $this->getAnnotatedTarget(fn() => new ReflectionFunction($function), $index);
return $this->getAnnotatedTarget(static fn() => new ReflectionFunction($function), $index);
}

private function getAnnotatedTarget(callable $reflectorSupplier, int $index) : AnnotatedTarget {
/**
* @param Closure():ReflectionClass|ReflectionProperty|ReflectionClassConstant|ReflectionMethod|ReflectionParameter|ReflectionFunction $reflectorSupplier
* @param int $index
* @return AnnotatedTarget
*/
private function getAnnotatedTarget(Closure $reflectorSupplier, int $index) : AnnotatedTarget {
return new class($reflectorSupplier, $index) implements AnnotatedTarget {

private $reflectorSupplier;
private ReflectionClass|ReflectionProperty|ReflectionClassConstant|ReflectionMethod|ReflectionParameter|ReflectionFunction $reflection;
private ReflectionAttribute $reflectionAttribute;
private object $attribute;

/**
* @var Closure():ReflectionClass|ReflectionProperty|ReflectionClassConstant|ReflectionFunction|ReflectionMethod|ReflectionParameter
*/
private Closure $reflectorSupplier;
private ReflectionClass|ReflectionProperty|ReflectionClassConstant|ReflectionMethod|ReflectionParameter|ReflectionFunction|null $reflection = null;
private ReflectionAttribute|null $reflectionAttribute = null;
private object|null $attribute = null;

/**
* @param Closure():ReflectionClass|ReflectionProperty|ReflectionClassConstant|ReflectionMethod|ReflectionParameter|ReflectionFunction $reflectorSupplier
* @param int $index
*/
public function __construct(
callable $reflectorSupplier,
Closure $reflectorSupplier,
private readonly int $index
) {
$this->reflectorSupplier = $reflectorSupplier;
}

public function targetReflection() : ReflectionClass|ReflectionProperty|ReflectionClassConstant|ReflectionMethod|ReflectionParameter|ReflectionFunction {
if (!isset($this->reflection)) {
if ($this->reflection === null) {
$this->reflection = ($this->reflectorSupplier)();
}
return $this->reflection;
}

public function attributeReflection() : ReflectionAttribute {
if (!isset($this->reflectionAttribute)) {
if ($this->reflectionAttribute === null) {
$this->reflectionAttribute = $this->targetReflection()->getAttributes()[$this->index];
}
return $this->reflectionAttribute;
}

public function attributeInstance() : object {
if (!isset($this->attribute)) {
if ($this->attribute === null) {
$this->attribute = $this->attributeReflection()->newInstance();
}
return $this->attribute;
Expand Down
9 changes: 5 additions & 4 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ function parseAttributes(array|string $directories, array $filterAttributes = []
$parser = new PhpParserAnnotatedTargetParser();
$directories = is_string($directories) ? [$directories] : $directories;

$builder = AnnotatedTargetParserOptionsBuilder::scanDirectories($directories);
if ($filterAttributes !== []) {
$builder = $builder->filterAttributes($filterAttributes);
if ($filterAttributes === []) {
$options = AnnotatedTargetParserOptions::scanAllAttributes(...$directories);
} else {
$options = AnnotatedTargetParserOptions::scanForSpecificAttributes($directories, $filterAttributes);
}

return $parser->parse($builder->build());
return $parser->parse($options);
}
49 changes: 0 additions & 49 deletions tests/Unit/AnnotatedTargetParserOptionsBuilderTest.php

This file was deleted.

10 changes: 7 additions & 3 deletions tests/Unit/AnnotatedTargetParserTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Cspray\AnnotatedTarget\Unit;

use Cspray\AnnotatedTarget\AnnotatedTargetParserOptions;
use Cspray\AnnotatedTarget\AnnotatedTargetParserOptionsBuilder;
use Cspray\AnnotatedTarget\PhpParserAnnotatedTargetParser;
use Cspray\AnnotatedTargetFixture\Fixture;
Expand All @@ -21,11 +22,14 @@ public function getTargets() : array {
throw new \BadMethodCallException('Before running any assertions on this test case you must provide a Fixture to load.');
}
$paths = array_map(fn(Fixture $fixture) => $fixture->getPath(), $this->fixtures);
$builder = AnnotatedTargetParserOptionsBuilder::scanDirectories($paths);
$options = AnnotatedTargetParserOptions::scanAllAttributes(...$paths);
if (isset($this->attributes)) {
$builder = $builder->filterAttributes($this->attributes);
$options = AnnotatedTargetParserOptions::scanForSpecificAttributes(
$paths,
$this->attributes
);
}
return iterator_to_array($this->getSubject()->parse($builder->build()));
return iterator_to_array($this->getSubject()->parse($options));
}

public function withFixtures(Fixture... $fixtures) : self {
Expand Down

0 comments on commit 5bb1bde

Please sign in to comment.