-
Notifications
You must be signed in to change notification settings - Fork 5
Implement ParameterAttributeCollector #33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -25,6 +25,7 @@ public function __construct( | |||||||||||||||
* array<TransientTargetClass>, | ||||||||||||||||
* array<TransientTargetMethod>, | ||||||||||||||||
* array<TransientTargetProperty>, | ||||||||||||||||
* array<array<TransientTargetMethodParameter>>, | ||||||||||||||||
* } | ||||||||||||||||
* | ||||||||||||||||
* @throws ReflectionException | ||||||||||||||||
|
@@ -34,7 +35,7 @@ public function collectAttributes(string $class): array | |||||||||||||||
$classReflection = new ReflectionClass($class); | ||||||||||||||||
|
||||||||||||||||
if (self::isAttribute($classReflection)) { | ||||||||||||||||
return [ [], [], [] ]; | ||||||||||||||||
return [ [], [], [], [] ]; | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
$classAttributes = []; | ||||||||||||||||
|
@@ -54,23 +55,15 @@ public function collectAttributes(string $class): array | |||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
$methodAttributes = []; | ||||||||||||||||
$methodParameterAttributes = []; | ||||||||||||||||
|
||||||||||||||||
foreach ($classReflection->getMethods() as $methodReflection) { | ||||||||||||||||
foreach ($methodReflection->getAttributes() as $attribute) { | ||||||||||||||||
if (self::isAttributeIgnored($attribute)) { | ||||||||||||||||
continue; | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
$method = $methodReflection->name; | ||||||||||||||||
|
||||||||||||||||
$this->io->debug("Found attribute {$attribute->getName()} on $class::$method"); | ||||||||||||||||
|
||||||||||||||||
$methodAttributes[] = new TransientTargetMethod( | ||||||||||||||||
$attribute->getName(), | ||||||||||||||||
$attribute->getArguments(), | ||||||||||||||||
$method, | ||||||||||||||||
); | ||||||||||||||||
} | ||||||||||||||||
$this->collectMethodAndParameterAttributes( | ||||||||||||||||
$class, | ||||||||||||||||
$methodReflection, | ||||||||||||||||
$methodAttributes, | ||||||||||||||||
$methodParameterAttributes, | ||||||||||||||||
); | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
$propertyAttributes = []; | ||||||||||||||||
|
@@ -94,7 +87,7 @@ public function collectAttributes(string $class): array | |||||||||||||||
} | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
return [ $classAttributes, $methodAttributes, $propertyAttributes ]; | ||||||||||||||||
return [ $classAttributes, $methodAttributes, $propertyAttributes, $methodParameterAttributes ]; | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
/** | ||||||||||||||||
|
@@ -124,4 +117,34 @@ private static function isAttributeIgnored(ReflectionAttribute $attribute): bool | |||||||||||||||
|
||||||||||||||||
return isset($ignored[$attribute->getName()]); // @phpstan-ignore offsetAccess.nonOffsetAccessible | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
/** | ||||||||||||||||
* @param array<TransientTargetMethod> $methodAttributes | ||||||||||||||||
* @param array<array<TransientTargetMethodParameter>> $methodParameterAttributes | ||||||||||||||||
* @return void | ||||||||||||||||
*/ | ||||||||||||||||
private function collectMethodAndParameterAttributes(string $class, \ReflectionMethod $methodReflection, array &$methodAttributes, array &$methodParameterAttributes): void | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's split this over multiple lines.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, I'd like it better if we were not using references… but I can live with it. |
||||||||||||||||
{ | ||||||||||||||||
$parameterAttributeCollector = new ParameterAttributeCollector($this->io); | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's initialize our dependencies in the construct function.
Suggested change
|
||||||||||||||||
foreach ($methodReflection->getAttributes() as $attribute) { | ||||||||||||||||
if (self::isAttributeIgnored($attribute)) { | ||||||||||||||||
continue; | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
$method = $methodReflection->name; | ||||||||||||||||
|
||||||||||||||||
$this->io->debug("Found attribute {$attribute->getName()} on $class::$method"); | ||||||||||||||||
|
||||||||||||||||
$methodAttributes[] = new TransientTargetMethod( | ||||||||||||||||
$attribute->getName(), | ||||||||||||||||
$attribute->getArguments(), | ||||||||||||||||
$method, | ||||||||||||||||
); | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
$parameterAttributes = $parameterAttributeCollector->collectAttributes($methodReflection); | ||||||||||||||||
if ($parameterAttributes !== []) { | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Simpler to read. |
||||||||||||||||
$methodParameterAttributes[] = $parameterAttributes; | ||||||||||||||||
} | ||||||||||||||||
} | ||||||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -23,12 +23,14 @@ class MemoizeAttributeCollector | |||||||||||||||||
* array<TransientTargetClass>, | ||||||||||||||||||
* array<TransientTargetMethod>, | ||||||||||||||||||
* array<TransientTargetProperty>, | ||||||||||||||||||
* array<array<TransientTargetMethodParameter>>, | ||||||||||||||||||
* }> | ||||||||||||||||||
* Where _key_ is a class and _value is an array where: | ||||||||||||||||||
* - `0` is a timestamp | ||||||||||||||||||
* - `1` is an array of class attributes | ||||||||||||||||||
* - `2` is an array of method attributes | ||||||||||||||||||
* - `3` is an array of property attributes | ||||||||||||||||||
* - `4` is an array of arrays. _key_ is a method name and _value_ parameter attributes | ||||||||||||||||||
*/ | ||||||||||||||||||
private array $state; | ||||||||||||||||||
|
||||||||||||||||||
|
@@ -59,7 +61,8 @@ public function collectAttributes(array $classMap): TransientCollection | |||||||||||||||||
$classAttributes, | ||||||||||||||||||
$methodAttributes, | ||||||||||||||||||
$propertyAttributes, | ||||||||||||||||||
] = $this->state[$class] ?? [ 0, [], [], [] ]; | ||||||||||||||||||
$methodParameterAttributes, | ||||||||||||||||||
] = $this->state[$class] ?? [ 0, [], [], [], [] ]; | ||||||||||||||||||
|
||||||||||||||||||
$mtime = filemtime($filepath); | ||||||||||||||||||
|
||||||||||||||||||
|
@@ -76,14 +79,15 @@ public function collectAttributes(array $classMap): TransientCollection | |||||||||||||||||
$classAttributes, | ||||||||||||||||||
$methodAttributes, | ||||||||||||||||||
$propertyAttributes, | ||||||||||||||||||
$methodParameterAttributes, | ||||||||||||||||||
] = $classAttributeCollector->collectAttributes($class); | ||||||||||||||||||
} catch (Throwable $e) { | ||||||||||||||||||
$this->io->error( | ||||||||||||||||||
"Attribute collection failed for $class: {$e->getMessage()}", | ||||||||||||||||||
); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
$this->state[$class] = [ time(), $classAttributes, $methodAttributes, $propertyAttributes ]; | ||||||||||||||||||
$this->state[$class] = [ time(), $classAttributes, $methodAttributes, $propertyAttributes, $methodParameterAttributes ]; | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's split this one.
Suggested change
|
||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
if (count($classAttributes)) { | ||||||||||||||||||
|
@@ -92,6 +96,9 @@ public function collectAttributes(array $classMap): TransientCollection | |||||||||||||||||
if (count($methodAttributes)) { | ||||||||||||||||||
$collector->addMethodAttributes($class, $methodAttributes); | ||||||||||||||||||
} | ||||||||||||||||||
if (count($methodParameterAttributes)) { | ||||||||||||||||||
$collector->addMethodParameterAttributes($class, $methodParameterAttributes); | ||||||||||||||||||
} | ||||||||||||||||||
if (count($propertyAttributes)) { | ||||||||||||||||||
$collector->addTargetProperties($class, $propertyAttributes); | ||||||||||||||||||
} | ||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,56 @@ | ||||||||||||
<?php | ||||||||||||
|
||||||||||||
namespace olvlvl\ComposerAttributeCollector; | ||||||||||||
|
||||||||||||
use Composer\IO\IOInterface; | ||||||||||||
use PhpParser\Node; | ||||||||||||
use PhpParser\Node\Stmt\ClassLike; | ||||||||||||
use PhpParser\NodeTraverser; | ||||||||||||
use PhpParser\NodeVisitorAbstract; | ||||||||||||
use PhpParser\Parser; | ||||||||||||
Comment on lines
+6
to
+10
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unused imports.
Suggested change
|
||||||||||||
|
||||||||||||
/** | ||||||||||||
* @internal | ||||||||||||
*/ | ||||||||||||
class ParameterAttributeCollector | ||||||||||||
{ | ||||||||||||
public function __construct( | ||||||||||||
private IOInterface $io | ||||||||||||
) { | ||||||||||||
} | ||||||||||||
|
||||||||||||
/** | ||||||||||||
* @return array<TransientTargetMethodParameter> | ||||||||||||
*/ | ||||||||||||
public function collectAttributes(\ReflectionFunctionAbstract $reflectionFunctionAbstract): array | ||||||||||||
{ | ||||||||||||
$funcParameterAttributes = []; | ||||||||||||
foreach ($reflectionFunctionAbstract->getParameters() as $parameter) { | ||||||||||||
$attributes = $parameter->getAttributes(); | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We initialize the variable here, but it's used much later. Besides, the variable is only used once, we could do without.
Suggested change
|
||||||||||||
$functionName = $reflectionFunctionAbstract->name; | ||||||||||||
$parameterName = $parameter->name; | ||||||||||||
assert($functionName !== ''); | ||||||||||||
assert($parameterName !== ''); | ||||||||||||
|
||||||||||||
$paramLabel = ''; | ||||||||||||
if ($reflectionFunctionAbstract instanceof \ReflectionMethod) { | ||||||||||||
$paramLabel = $reflectionFunctionAbstract->class . '::' . $functionName . '(' . $parameterName . ')'; | ||||||||||||
} elseif ($reflectionFunctionAbstract instanceof \ReflectionFunction) { | ||||||||||||
$paramLabel = $functionName . '(' . $parameterName . ')'; | ||||||||||||
} | ||||||||||||
|
||||||||||||
foreach ($attributes as $attribute) { | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
$this->io->debug("Found attribute {$attribute->getName()} on $paramLabel"); | ||||||||||||
|
||||||||||||
$funcParameterAttributes[] = new TransientTargetMethodParameter( | ||||||||||||
$attribute->getName(), | ||||||||||||
$attribute->getArguments(), | ||||||||||||
$functionName, | ||||||||||||
$parameterName | ||||||||||||
); | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
return $funcParameterAttributes; | ||||||||||||
} | ||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?php | ||
|
||
namespace olvlvl\ComposerAttributeCollector; | ||
|
||
/** | ||
* @readonly | ||
* | ||
* @template T of object | ||
*/ | ||
final class TargetMethodParameter | ||
{ | ||
/** | ||
* @param T $attribute | ||
* @param class-string $class | ||
* The name of the target class. | ||
* @param non-empty-string $name | ||
* The name of the target parameter. | ||
* @param non-empty-string $method | ||
* The name of the target method. | ||
*/ | ||
public function __construct( | ||
public object $attribute, | ||
public string $class, | ||
public string $name, | ||
public string $method | ||
) { | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove this one, it's already in the signature.