Skip to content

Commit 834f54f

Browse files
committed
✨ [thecodingmachine#708] Re-introduce feature about supporting target method attributes for resolving parameter annotations
1 parent 0f1a112 commit 834f54f

5 files changed

+100
-1
lines changed

src/AnnotationReader.php

+25-1
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@
2424
use TheCodingMachine\GraphQLite\Annotations\Type;
2525
use TheCodingMachine\GraphQLite\Annotations\TypeInterface;
2626

27+
use function array_diff_key;
2728
use function array_filter;
2829
use function array_key_exists;
2930
use function array_map;
3031
use function array_merge;
3132
use function assert;
3233
use function count;
34+
use function get_class;
3335
use function is_a;
3436
use function reset;
3537

@@ -248,11 +250,33 @@ public function getParameterAnnotationsPerParameter(array $refParameters): array
248250
if (empty($refParameters)) {
249251
return [];
250252
}
251-
$firstParam = reset($refParameters);
252253

254+
/** @var array<string, array<int,ParameterAnnotationInterface>> $parameterAnnotationsPerParameter */
255+
$parameterAnnotationsPerParameter = [];
256+
257+
// resolve parameter annotations targeted to method
258+
$firstParam = reset($refParameters);
253259
$method = $firstParam->getDeclaringFunction();
254260
assert($method instanceof ReflectionMethod);
255261

262+
$parameterAnnotations = $this->getMethodAnnotations($method, ParameterAnnotationInterface::class);
263+
foreach ($parameterAnnotations as $parameterAnnotation) {
264+
$parameterAnnotationsPerParameter[$parameterAnnotation->getTarget()][] = $parameterAnnotation;
265+
}
266+
267+
// Let's check that the referenced parameters actually do exist:
268+
$parametersByKey = [];
269+
foreach ($refParameters as $refParameter) {
270+
$parametersByKey[$refParameter->getName()] = true;
271+
}
272+
$diff = array_diff_key($parameterAnnotationsPerParameter, $parametersByKey);
273+
if (count($diff) > 0) {
274+
foreach ($diff as $parameterName => $parameterAnnotations) {
275+
throw InvalidParameterException::parameterNotFound($parameterName, get_class($parameterAnnotations[0]), $method);
276+
}
277+
}
278+
279+
// resolve parameter annotations targeted to parameter
256280
foreach ($refParameters as $refParameter) {
257281
$attributes = $refParameter->getAttributes();
258282
$parameterAnnotationsPerParameter[$refParameter->getName()] = [...$parameterAnnotationsPerParameter[$refParameter->getName()] ??

src/Annotations/Exceptions/InvalidParameterException.php

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111

1212
class InvalidParameterException extends BadMethodCallException
1313
{
14+
public static function parameterNotFound(string $parameter, string $annotationClass, ReflectionMethod $reflectionMethod): self
15+
{
16+
return new self(sprintf('Parameter "%s" declared in annotation "%s" of method "%s::%s()" does not exist.', $parameter, $annotationClass, $reflectionMethod->getDeclaringClass()->getName(), $reflectionMethod->getName()));
17+
}
18+
1419
public static function parameterNotFoundFromSourceField(string $parameter, string $annotationClass, ReflectionMethod $reflectionMethod): self
1520
{
1621
return new self(sprintf('Could not find parameter "%s" declared in annotation "%s". This annotation is itself declared in a SourceField attribute targeting resolver "%s::%s()".', $parameter, $annotationClass, $reflectionMethod->getDeclaringClass()->getName(), $reflectionMethod->getName()));

tests/AnnotationReaderTest.php

+29
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@
99
use ReflectionMethod;
1010
use TheCodingMachine\GraphQLite\Annotations\Autowire;
1111
use TheCodingMachine\GraphQLite\Annotations\Exceptions\ClassNotFoundException;
12+
use TheCodingMachine\GraphQLite\Annotations\Exceptions\InvalidParameterException;
1213
use TheCodingMachine\GraphQLite\Annotations\Field;
1314
use TheCodingMachine\GraphQLite\Annotations\Security;
1415
use TheCodingMachine\GraphQLite\Annotations\Type;
1516
use TheCodingMachine\GraphQLite\Fixtures\Annotations\ClassWithInvalidClassAnnotation;
1617
use TheCodingMachine\GraphQLite\Fixtures\Annotations\ClassWithInvalidExtendTypeAnnotation;
1718
use TheCodingMachine\GraphQLite\Fixtures\Annotations\ClassWithInvalidTypeAnnotation;
19+
use TheCodingMachine\GraphQLite\Fixtures\Annotations\ClassWithTargetMethodParameterAnnotation;
20+
use TheCodingMachine\GraphQLite\Fixtures\Annotations\TargetMethodParameterAnnotation;
1821
use TheCodingMachine\GraphQLite\Fixtures\Attributes\TestType;
1922

2023
class AnnotationReaderTest extends TestCase
@@ -126,6 +129,32 @@ public function testPhp8AttributeParameterAnnotations(): void
126129
$this->assertInstanceOf(Autowire::class, $parameterAnnotations['dao']->getAnnotationByType(Autowire::class));
127130
}
128131

132+
/**
133+
* This functionality can be dropped with next major release (8.0) with added explicit deprecations before release.
134+
*/
135+
public function testPhp8AttributeParameterAnnotationsForTargetMethod(): void
136+
{
137+
$annotationReader = new AnnotationReader();
138+
139+
$parameterAnnotations = $annotationReader->getParameterAnnotationsPerParameter((new ReflectionMethod(ClassWithTargetMethodParameterAnnotation::class, 'method'))->getParameters());
140+
141+
$this->assertInstanceOf(TargetMethodParameterAnnotation::class, $parameterAnnotations['bar']->getAnnotationByType(TargetMethodParameterAnnotation::class));
142+
}
143+
144+
/**
145+
* This functionality can be dropped with next major release (8.0) with added explicit deprecations before release.
146+
*/
147+
public function testPhp8AttributeParameterAnnotationsForTargetMethodWithInvalidTargetParameter(): void
148+
{
149+
$annotationReader = new AnnotationReader();
150+
151+
$this->expectException(InvalidParameterException::class);
152+
$this->expectExceptionMessage('Parameter "unexistent" declared in annotation "TheCodingMachine\GraphQLite\Fixtures\Annotations\TargetMethodParameterAnnotation" of method "TheCodingMachine\GraphQLite\Fixtures\Annotations\ClassWithTargetMethodParameterAnnotation::methodWithInvalidAnnotation()" does not exist.');
153+
154+
$annotationReader->getParameterAnnotationsPerParameter((new ReflectionMethod(ClassWithTargetMethodParameterAnnotation::class, 'methodWithInvalidAnnotation'))->getParameters());
155+
}
156+
157+
/** @noinspection PhpUnusedPrivateMethodInspection Used in {@see testPhp8AttributeParameterAnnotations} */
129158
private function method1(
130159
#[Autowire('myService')]
131160
$dao,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TheCodingMachine\GraphQLite\Fixtures\Annotations;
6+
7+
use stdClass;
8+
9+
/** @internal */
10+
final class ClassWithTargetMethodParameterAnnotation
11+
{
12+
#[TargetMethodParameterAnnotation(target: 'bar')]
13+
public function method(stdClass $bar): void
14+
{
15+
}
16+
17+
#[TargetMethodParameterAnnotation(target: 'unexistent')]
18+
public function methodWithInvalidAnnotation(stdClass $bar): void
19+
{
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TheCodingMachine\GraphQLite\Fixtures\Annotations;
6+
7+
use TheCodingMachine\GraphQLite\Annotations\ParameterAnnotationInterface;
8+
9+
#[\Attribute(\Attribute::TARGET_METHOD)]
10+
final class TargetMethodParameterAnnotation implements ParameterAnnotationInterface
11+
{
12+
public function __construct(private readonly string $target)
13+
{
14+
}
15+
16+
public function getTarget(): string
17+
{
18+
return $this->target;
19+
}
20+
}

0 commit comments

Comments
 (0)