diff --git a/.php_cs.dist b/.php_cs.dist index c5b7f6e45..0869149d9 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -1,7 +1,7 @@ exclude('vendor') + ->exclude('Annotation') ->name('*.php') ->in([__DIR__.'/src', __DIR__.'/tests']) ; diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 47ab9f17c..62b60afa8 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -45,16 +45,6 @@ parameters: count: 1 path: src/Config/Parser/AnnotationParser.php - - - message: "#^Access to an undefined property object\\:\\:\\$name\\.$#" - count: 1 - path: src/Config/Parser/AnnotationParser.php - - - - message: "#^Offset 'type' does not exist on array\\|string\\.$#" - count: 2 - path: src/Config/Parser/AnnotationParser.php - - message: "#^Access to an undefined property GraphQL\\\\Language\\\\AST\\\\Node\\:\\:\\$description\\.$#" count: 1 @@ -175,11 +165,6 @@ parameters: count: 1 path: src/Relay/Connection/Output/Connection.php - - - message: "#^Cannot call method then\\(\\) on array\\|object\\.$#" - count: 1 - path: src/Relay/Connection/Paginator.php - - message: "#^Possibly invalid array key type \\(array\\|string\\|null\\)\\.$#" count: 1 @@ -195,11 +180,6 @@ parameters: count: 1 path: src/Relay/Mutation/PayloadDefinition.php - - - message: "#^Parameter \\#1 \\$schema of static method Overblog\\\\GraphQLBundle\\\\Event\\\\ExecutorArgumentsEvent\\:\\:create\\(\\) expects Overblog\\\\GraphQLBundle\\\\Definition\\\\Type\\\\ExtensibleSchema, GraphQL\\\\Type\\\\Schema given\\.$#" - count: 1 - path: src/Request/Executor.php - - message: "#^Parameter \\#1 \\$function of function call_user_func_array expects callable\\(\\)\\: mixed, array\\(mixed, mixed\\) given\\.$#" count: 1 @@ -210,11 +190,6 @@ parameters: count: 1 path: src/Resolver/TypeResolver.php - - - message: "#^If condition is always true\\.$#" - count: 1 - path: src/Transformer/ArgumentsTransformer.php - - message: "#^Array \\(array\\\\) does not accept Overblog\\\\GraphQLBundle\\\\Validator\\\\Mapping\\\\PropertyMetadata\\.$#" count: 1 diff --git a/src/Annotation/Access.php b/src/Annotation/Access.php index 0860e287e..baf7178ee 100644 --- a/src/Annotation/Access.php +++ b/src/Annotation/Access.php @@ -19,5 +19,5 @@ final class Access implements Annotation * * @var string */ - public $value; + public string $value; } diff --git a/src/Annotation/Arg.php b/src/Annotation/Arg.php index 8f3656cdb..ef64fdb42 100644 --- a/src/Annotation/Arg.php +++ b/src/Annotation/Arg.php @@ -19,14 +19,14 @@ final class Arg implements Annotation * * @var string */ - public $name; + public string $name; /** * Argument description. * * @var string */ - public $description; + public string $description; /** * Argument type. @@ -35,7 +35,7 @@ final class Arg implements Annotation * * @var string */ - public $type; + public string $type; /** * Default argument value. diff --git a/src/Annotation/Deprecated.php b/src/Annotation/Deprecated.php index 0f62c13b6..651c76979 100644 --- a/src/Annotation/Deprecated.php +++ b/src/Annotation/Deprecated.php @@ -19,5 +19,5 @@ final class Deprecated implements Annotation * * @var string */ - public $value; + public string $value; } diff --git a/src/Annotation/Description.php b/src/Annotation/Description.php index 49833e216..07967f4c2 100644 --- a/src/Annotation/Description.php +++ b/src/Annotation/Description.php @@ -19,5 +19,5 @@ final class Description implements Annotation * * @var string */ - public $value; + public string $value; } diff --git a/src/Annotation/Enum.php b/src/Annotation/Enum.php index ebe4b6e58..5ac03a5af 100644 --- a/src/Annotation/Enum.php +++ b/src/Annotation/Enum.php @@ -17,10 +17,10 @@ final class Enum implements Annotation * * @var string */ - public $name; + public string $name; /** * @var array<\Overblog\GraphQLBundle\Annotation\EnumValue> */ - public $values; + public array $values; } diff --git a/src/Annotation/EnumValue.php b/src/Annotation/EnumValue.php index 23bed0ed4..0cd08298b 100644 --- a/src/Annotation/EnumValue.php +++ b/src/Annotation/EnumValue.php @@ -15,15 +15,15 @@ final class EnumValue implements Annotation /** * @var string */ - public $name; + public string $name; /** * @var string */ - public $description; + public string $description; /** * @var string */ - public $deprecationReason; + public string $deprecationReason; } diff --git a/src/Annotation/Field.php b/src/Annotation/Field.php index a292147df..0c1e07e4f 100644 --- a/src/Annotation/Field.php +++ b/src/Annotation/Field.php @@ -17,7 +17,7 @@ class Field implements Annotation * * @var string */ - public $name; + public string $name; /** * Field Type. @@ -26,21 +26,21 @@ class Field implements Annotation * * @var string */ - public $type; + public string $type; /** * Field arguments. * * @var array<\Overblog\GraphQLBundle\Annotation\Arg> */ - public $args; + public array $args = []; /** * Resolver for this property. * * @var string */ - public $resolve; + public string $resolve; /** * Args builder. diff --git a/src/Annotation/FieldsBuilder.php b/src/Annotation/FieldsBuilder.php index 15be1cfdf..c65086e65 100644 --- a/src/Annotation/FieldsBuilder.php +++ b/src/Annotation/FieldsBuilder.php @@ -19,7 +19,7 @@ final class FieldsBuilder implements Annotation * * @var string */ - public $builder; + public string $builder; /** * The builder config. diff --git a/src/Annotation/Input.php b/src/Annotation/Input.php index 16ba0f073..1b1dfe119 100644 --- a/src/Annotation/Input.php +++ b/src/Annotation/Input.php @@ -17,12 +17,12 @@ final class Input implements Annotation * * @var string */ - public $name; + public string $name; /** * Is the type a relay input. * * @var bool */ - public $isRelay = false; + public bool $isRelay = false; } diff --git a/src/Annotation/IsPublic.php b/src/Annotation/IsPublic.php index 038ad523e..7aeb4912e 100644 --- a/src/Annotation/IsPublic.php +++ b/src/Annotation/IsPublic.php @@ -19,5 +19,5 @@ final class IsPublic implements Annotation * * @var string */ - public $value; + public string $value; } diff --git a/src/Annotation/Mutation.php b/src/Annotation/Mutation.php index eba1e91d9..febd82040 100644 --- a/src/Annotation/Mutation.php +++ b/src/Annotation/Mutation.php @@ -17,5 +17,5 @@ final class Mutation extends Field * * @var array */ - public $targetType; + public array $targetType; } diff --git a/src/Annotation/Provider.php b/src/Annotation/Provider.php index 4aa90cf1b..388b20770 100644 --- a/src/Annotation/Provider.php +++ b/src/Annotation/Provider.php @@ -17,5 +17,5 @@ final class Provider implements Annotation * * @var string */ - public $prefix; + public string $prefix; } diff --git a/src/Annotation/Query.php b/src/Annotation/Query.php index a50c5ffcd..993156c1c 100644 --- a/src/Annotation/Query.php +++ b/src/Annotation/Query.php @@ -17,5 +17,5 @@ final class Query extends Field * * @var array */ - public $targetType; + public array $targetType; } diff --git a/src/Annotation/Relay/Connection.php b/src/Annotation/Relay/Connection.php index fac3e2261..62657c39c 100644 --- a/src/Annotation/Relay/Connection.php +++ b/src/Annotation/Relay/Connection.php @@ -19,12 +19,12 @@ final class Connection extends Type * * @var string */ - public $edge; + public string $edge; /** * Connection Node type. * * @var string */ - public $node; + public string $node; } diff --git a/src/Annotation/Relay/Edge.php b/src/Annotation/Relay/Edge.php index 9889fb815..11a4b76ea 100644 --- a/src/Annotation/Relay/Edge.php +++ b/src/Annotation/Relay/Edge.php @@ -21,5 +21,5 @@ final class Edge extends Type * * @var string */ - public $node; + public string $node; } diff --git a/src/Annotation/Scalar.php b/src/Annotation/Scalar.php index e0aca82cb..89d697a13 100644 --- a/src/Annotation/Scalar.php +++ b/src/Annotation/Scalar.php @@ -13,16 +13,13 @@ final class Scalar implements Annotation { /** - * Scalar name. - * * @var string */ - public $name; + public string $name; + /** - * Scalar type. - * * @var string */ - public $scalarType; + public string $scalarType; } diff --git a/src/Annotation/Type.php b/src/Annotation/Type.php index a921d9c7f..6f7982ccc 100644 --- a/src/Annotation/Type.php +++ b/src/Annotation/Type.php @@ -17,40 +17,40 @@ class Type implements Annotation * * @var string */ - public $name; + public string $name; /** * Type inherited interfaces. * * @var string[] */ - public $interfaces; + public array $interfaces; /** * Is the type a relay payload. * * @var bool */ - public $isRelay = false; + public bool $isRelay = false; /** * Expression to a target fields resolver. * * @var string */ - public $resolveField; + public string $resolveField; /** * List of fields builder. * * @var array<\Overblog\GraphQLBundle\Annotation\FieldsBuilder> */ - public $builders = []; + public array $builders = []; /** * Expression to resolve type for interfaces. * * @var string */ - public $isTypeOf; + public string $isTypeOf; } diff --git a/src/Annotation/TypeInterface.php b/src/Annotation/TypeInterface.php index 7b740fc3d..f9637de44 100644 --- a/src/Annotation/TypeInterface.php +++ b/src/Annotation/TypeInterface.php @@ -17,7 +17,7 @@ final class TypeInterface implements Annotation * * @var string */ - public $name; + public string $name; /** * Resolver type for interface. @@ -26,5 +26,5 @@ final class TypeInterface implements Annotation * * @var string */ - public $resolveType; + public string $resolveType; } diff --git a/src/Annotation/Union.php b/src/Annotation/Union.php index 5a234f3f3..7af0ef6f0 100644 --- a/src/Annotation/Union.php +++ b/src/Annotation/Union.php @@ -17,19 +17,19 @@ final class Union implements Annotation * * @var string */ - public $name; + public string $name; /** * Union types. * * @var array */ - public $types; + public array $types; /** * Resolver type for union. * * @var string */ - public $resolveType; + public string $resolveType; } diff --git a/src/Config/Parser/Annotation/GraphClass.php b/src/Config/Parser/Annotation/GraphClass.php index 301b3ab9b..7eb80d2f8 100644 --- a/src/Config/Parser/Annotation/GraphClass.php +++ b/src/Config/Parser/Annotation/GraphClass.php @@ -7,9 +7,12 @@ use Doctrine\Common\Annotations\AnnotationException; use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Annotations\AnnotationRegistry; +use Overblog\GraphQLBundle\Annotation\Annotation; use ReflectionClass; +use ReflectionException; use ReflectionMethod; use ReflectionProperty; +use Reflector; use RuntimeException; use function class_exists; @@ -18,11 +21,12 @@ class GraphClass extends ReflectionClass private static ?AnnotationReader $annotationReader = null; protected array $annotations = []; - protected array $propertiesExtended = []; /** * @param mixed $className + * + * @throws ReflectionException */ public function __construct($className) { @@ -51,26 +55,24 @@ public function getPropertiesExtended() } /** - * @param ReflectionMethod|ReflectionProperty|null $from + * @phpstan-param ReflectionMethod|ReflectionProperty|null $from * - * @return array + * @return array + * + * @throws AnnotationException */ - public function getAnnotations(object $from = null) + public function getAnnotations(Reflector $from = null): array { - if (!$from) { - return $this->annotations; - } - - if ($from instanceof ReflectionMethod) { - return self::getAnnotationReader()->getMethodAnnotations($from); - } - - if ($from instanceof ReflectionProperty) { - return self::getAnnotationReader()->getPropertyAnnotations($from); + switch (true) { + case null === $from: + return $this->annotations; + case $from instanceof ReflectionMethod: + return self::getAnnotationReader()->getMethodAnnotations($from); + case $from instanceof ReflectionProperty: + return self::getAnnotationReader()->getPropertyAnnotations($from); + default: + throw new AnnotationException(sprintf('Unable to retrieve annotations from object of class "%s".', get_class($from))); } - - /** @phpstan-ignore-next-line */ - throw new AnnotationException(sprintf('Unable to retrieve annotations from object of class "%s".', get_class($from))); } private static function getAnnotationReader(): AnnotationReader diff --git a/src/Config/Parser/AnnotationParser.php b/src/Config/Parser/AnnotationParser.php index 01d3af6ab..2d2621de7 100644 --- a/src/Config/Parser/AnnotationParser.php +++ b/src/Config/Parser/AnnotationParser.php @@ -4,6 +4,7 @@ namespace Overblog\GraphQLBundle\Config\Parser; +use Doctrine\Common\Annotations\AnnotationException; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\ManyToMany; @@ -68,6 +69,7 @@ class AnnotationParser implements PreParserInterface * {@inheritdoc} * * @throws InvalidArgumentException + * @throws ReflectionException */ public static function preParse(SplFileInfo $file, ContainerBuilder $container, array $configs = []): void { @@ -76,6 +78,7 @@ public static function preParse(SplFileInfo $file, ContainerBuilder $container, /** * @throws InvalidArgumentException + * @throws ReflectionException */ public static function parse(SplFileInfo $file, ContainerBuilder $container, array $configs = []): array { @@ -95,8 +98,7 @@ public static function reset(): void /** * Process a file. * - * @throws InvalidArgumentException - * @throws ReflectionException + * @throws InvalidArgumentException|ReflectionException|AnnotationException */ private static function processFile(SplFileInfo $file, ContainerBuilder $container, array $configs, bool $preProcess): array { @@ -140,7 +142,7 @@ private static function classAnnotationsToGQLConfiguration( switch (true) { case $classAnnotation instanceof GQL\Type: $gqlType = self::GQL_TYPE; - $gqlName = $classAnnotation->name ?: $graphClass->getShortName(); + $gqlName = $classAnnotation->name ?? $graphClass->getShortName(); if (!$preProcess) { $gqlConfiguration = self::typeAnnotationToGQLConfiguration($graphClass, $classAnnotation, $gqlName, $configs); @@ -149,13 +151,13 @@ private static function classAnnotationsToGQLConfiguration( throw new InvalidArgumentException(sprintf('The annotation @Connection on class "%s" can only be used on class implementing the ConnectionInterface.', $graphClass->getName())); } - if (!($classAnnotation->edge xor $classAnnotation->node)) { - throw new InvalidArgumentException(sprintf('The annotation @Connection on class "%s" is invalid. You must define the "edge" OR the "node" attribute.', $graphClass->getName())); + if (!(isset($classAnnotation->edge) xor isset($classAnnotation->node))) { + throw new InvalidArgumentException(sprintf('The annotation @Connection on class "%s" is invalid. You must define either the "edge" OR the "node" attribute, but not both.', $graphClass->getName())); } - $edgeType = $classAnnotation->edge; + $edgeType = $classAnnotation->edge ?? false; if (!$edgeType) { - $edgeType = sprintf('%sEdge', $gqlName); + $edgeType = $gqlName.'Edge'; $gqlTypes[$edgeType] = [ 'type' => 'object', 'config' => [ @@ -165,9 +167,11 @@ private static function classAnnotationsToGQLConfiguration( ], ]; } + if (!isset($gqlConfiguration['config']['builders'])) { $gqlConfiguration['config']['builders'] = []; } + array_unshift($gqlConfiguration['config']['builders'], ['builder' => 'relay-connection', 'builderConfig' => ['edgeType' => $edgeType]]); } } @@ -175,7 +179,7 @@ private static function classAnnotationsToGQLConfiguration( case $classAnnotation instanceof GQL\Input: $gqlType = self::GQL_INPUT; - $gqlName = $classAnnotation->name ?: self::suffixName($graphClass->getShortName(), 'Input'); + $gqlName = $classAnnotation->name ?? self::suffixName($graphClass->getShortName(), 'Input'); if (!$preProcess) { $gqlConfiguration = self::inputAnnotationToGQLConfiguration($graphClass, $classAnnotation); } @@ -219,7 +223,7 @@ private static function classAnnotationsToGQLConfiguration( if (null !== $gqlType) { if (!$gqlName) { - $gqlName = $classAnnotation->name ?: $graphClass->getShortName(); + $gqlName = isset($classAnnotation->name) ? $classAnnotation->name : $graphClass->getShortName(); } if ($preProcess) { @@ -302,7 +306,7 @@ private static function graphQLTypeConfigFromAnnotation(GraphClass $graphClass, $typeConfiguration['fields'] = array_merge($fieldsFromProperties, $fieldsFromMethods); $typeConfiguration = self::getDescriptionConfiguration($graphClass->getAnnotations()) + $typeConfiguration; - if (null !== $typeAnnotation->interfaces) { + if (isset($typeAnnotation->interfaces)) { $typeConfiguration['interfaces'] = $typeAnnotation->interfaces; } else { $interfaces = array_keys(self::searchClassesMapBy(function ($gqlType, $configuration) use ($graphClass) { @@ -320,27 +324,27 @@ private static function graphQLTypeConfigFromAnnotation(GraphClass $graphClass, $typeConfiguration['interfaces'] = $interfaces; } - if ($typeAnnotation->resolveField) { + if (isset($typeAnnotation->resolveField)) { $typeConfiguration['resolveField'] = self::formatExpression($typeAnnotation->resolveField); } - if ($typeAnnotation->builders && !empty($typeAnnotation->builders)) { + if (isset($typeAnnotation->builders) && !empty($typeAnnotation->builders)) { $typeConfiguration['builders'] = array_map(function ($fieldsBuilderAnnotation) { return ['builder' => $fieldsBuilderAnnotation->builder, 'builderConfig' => $fieldsBuilderAnnotation->builderConfig]; }, $typeAnnotation->builders); } - if ($typeAnnotation->isTypeOf) { + if (isset($typeAnnotation->isTypeOf)) { $typeConfiguration['isTypeOf'] = $typeAnnotation->isTypeOf; } $publicAnnotation = self::getFirstAnnotationMatching($graphClass->getAnnotations(), GQL\IsPublic::class); - if ($publicAnnotation) { + if (null !== $publicAnnotation) { $typeConfiguration['fieldsDefaultPublic'] = self::formatExpression($publicAnnotation->value); } $accessAnnotation = self::getFirstAnnotationMatching($graphClass->getAnnotations(), GQL\Access::class); - if ($accessAnnotation) { + if (null !== $accessAnnotation) { $typeConfiguration['fieldsDefaultAccess'] = self::formatExpression($accessAnnotation->value); } @@ -384,7 +388,7 @@ private static function scalarAnnotationToGQLConfiguration(GraphClass $graphClas { $scalarConfiguration = []; - if ($scalarAnnotation->scalarType) { + if (isset($scalarAnnotation->scalarType)) { $scalarConfiguration['scalarType'] = self::formatExpression($scalarAnnotation->scalarType); } else { $scalarConfiguration = [ @@ -409,17 +413,15 @@ private static function enumAnnotationToGQLConfiguration(GraphClass $graphClass, $values = []; foreach ($graphClass->getConstants() as $name => $value) { - $valueAnnotation = current(array_filter($enumValues, function ($enumValueAnnotation) use ($name) { - return $enumValueAnnotation->name == $name; - })); + $valueAnnotation = current(array_filter($enumValues, fn ($enumValueAnnotation) => $enumValueAnnotation->name == $name)); $valueConfig = []; $valueConfig['value'] = $value; - if ($valueAnnotation && $valueAnnotation->description) { + if ($valueAnnotation && isset($valueAnnotation->description)) { $valueConfig['description'] = $valueAnnotation->description; } - if ($valueAnnotation && $valueAnnotation->deprecationReason) { + if ($valueAnnotation && isset($valueAnnotation->deprecationReason)) { $valueConfig['deprecationReason'] = $valueAnnotation->deprecationReason; } @@ -438,7 +440,7 @@ private static function enumAnnotationToGQLConfiguration(GraphClass $graphClass, private static function unionAnnotationToGQLConfiguration(GraphClass $graphClass, GQL\Union $unionAnnotation): array { $unionConfiguration = []; - if (null !== $unionAnnotation->types) { + if (isset($unionAnnotation->types)) { $unionConfiguration['types'] = $unionAnnotation->types; } else { $types = array_keys(self::searchClassesMapBy(function ($gqlType, $configuration) use ($graphClass) { @@ -457,7 +459,7 @@ private static function unionAnnotationToGQLConfiguration(GraphClass $graphClass $unionConfiguration = self::getDescriptionConfiguration($graphClass->getAnnotations()) + $unionConfiguration; - if ($unionAnnotation->resolveType) { + if (isset($unionAnnotation->resolveType)) { $unionConfiguration['resolveType'] = self::formatExpression($unionAnnotation->resolveType); } else { if ($graphClass->hasMethod('resolveType')) { @@ -476,9 +478,12 @@ private static function unionAnnotationToGQLConfiguration(GraphClass $graphClass } /** - * @param ReflectionMethod|ReflectionProperty $reflector + * @phpstan-param ReflectionMethod|ReflectionProperty $reflector + * @phpstan-param class-string $fieldAnnotationName + * + * @throws AnnotationException */ - private static function getTypeFieldConfigurationFromReflector(GraphClass $graphClass, Reflector $reflector, string $fieldAnnotationName = GQL\Field::class, string $currentValue = 'value'): array + private static function getTypeFieldConfigurationFromReflector(GraphClass $graphClass, Reflector $reflector, string $fieldAnnotationName, string $currentValue = 'value'): array { $annotations = $graphClass->getAnnotations($reflector); @@ -486,8 +491,8 @@ private static function getTypeFieldConfigurationFromReflector(GraphClass $graph $accessAnnotation = self::getFirstAnnotationMatching($annotations, GQL\Access::class); $publicAnnotation = self::getFirstAnnotationMatching($annotations, GQL\IsPublic::class); - if (!$fieldAnnotation) { - if ($accessAnnotation || $publicAnnotation) { + if (null === $fieldAnnotation) { + if (null !== $accessAnnotation || null !== $publicAnnotation) { throw new InvalidArgumentException(sprintf('The annotations "@Access" and/or "@Visible" defined on "%s" are only usable in addition of annotation "@Field"', $reflector->getName())); } @@ -499,22 +504,29 @@ private static function getTypeFieldConfigurationFromReflector(GraphClass $graph } $fieldName = $reflector->getName(); - $fieldType = $fieldAnnotation->type; $fieldConfiguration = []; - if ($fieldType) { - $fieldConfiguration['type'] = $fieldType; + + if (isset($fieldAnnotation->type)) { + $fieldConfiguration['type'] = $fieldAnnotation->type; } $fieldConfiguration = self::getDescriptionConfiguration($annotations, true) + $fieldConfiguration; $args = []; - if (!empty($fieldAnnotation->args)) { - foreach ($fieldAnnotation->args as $arg) { - $args[$arg->name] = ['type' => $arg->type] - + (null !== $arg->description ? ['description' => $arg->description] : []) - + (null !== $arg->default ? ['defaultValue' => $arg->default] : []); + + foreach ($fieldAnnotation->args as $arg) { + $args[$arg->name] = ['type' => $arg->type]; + + if (isset($arg->description)) { + $args[$arg->name]['description'] = $arg->description; } - } elseif ($reflector instanceof ReflectionMethod) { + + if (isset($arg->default)) { + $args[$arg->name]['defaultValue'] = $arg->default; + } + } + + if (empty($fieldAnnotation->args) && $reflector instanceof ReflectionMethod) { $args = self::guessArgs($reflector); } @@ -522,9 +534,9 @@ private static function getTypeFieldConfigurationFromReflector(GraphClass $graph $fieldConfiguration['args'] = $args; } - $fieldName = $fieldAnnotation->name ?: $fieldName; + $fieldName = $fieldAnnotation->name ?? $fieldName; - if ($fieldAnnotation->resolve) { + if (isset($fieldAnnotation->resolve)) { $fieldConfiguration['resolve'] = self::formatExpression($fieldAnnotation->resolve); } else { if ($reflector instanceof ReflectionMethod) { @@ -558,7 +570,7 @@ private static function getTypeFieldConfigurationFromReflector(GraphClass $graph throw new InvalidArgumentException(sprintf('The attribute "fieldBuilder" on GraphQL annotation "@%s" defined on "%s" must be a string or an array where first index is the builder name and the second is the config.', $fieldAnnotationName, $reflector->getName())); } } else { - if (!$fieldType) { + if (!isset($fieldAnnotation->type)) { if ($reflector instanceof ReflectionMethod) { /** @var ReflectionMethod $reflector */ if ($reflector->hasReturnType()) { @@ -600,6 +612,8 @@ private static function getTypeFieldConfigurationFromReflector(GraphClass $graph * Create GraphQL input fields configuration based on annotations. * * @param ReflectionProperty[] $reflectors + * + * @throws AnnotationException */ private static function getGraphQLInputFieldsFromAnnotations(GraphClass $graphClass, array $reflectors): array { @@ -607,10 +621,12 @@ private static function getGraphQLInputFieldsFromAnnotations(GraphClass $graphCl foreach ($reflectors as $reflector) { $annotations = $graphClass->getAnnotations($reflector); + + /** @var GQL\Field $fieldAnnotation */ $fieldAnnotation = self::getFirstAnnotationMatching($annotations, GQL\Field::class); // Ignore field with resolver when the type is an Input - if ($fieldAnnotation->resolve) { + if (isset($fieldAnnotation->resolve)) { return []; } @@ -618,7 +634,8 @@ private static function getGraphQLInputFieldsFromAnnotations(GraphClass $graphCl $fieldType = $fieldAnnotation->type; $fieldConfiguration = []; if ($fieldType) { - $resolvedType = self::resolveClassFromType($fieldType); + // Resolve a PHP class from a GraphQL type + $resolvedType = self::$classesMap[$fieldType] ?? null; // We found a type but it is not allowed if (null !== $resolvedType && !in_array($resolvedType['type'], self::VALID_INPUT_TYPES)) { throw new InvalidArgumentException(sprintf('The type "%s" on "%s" is a "%s" not valid on an Input @Field. Only Input, Scalar and Enum are allowed.', $fieldType, $reflector->getName(), $resolvedType['type'])); @@ -637,7 +654,11 @@ private static function getGraphQLInputFieldsFromAnnotations(GraphClass $graphCl /** * Create GraphQL type fields configuration based on annotations. * + * @phpstan-param class-string $fieldAnnotationName + * * @param ReflectionProperty[]|ReflectionMethod[] $reflectors + * + * @throws AnnotationException */ private static function getGraphQLTypeFieldsFromAnnotations(GraphClass $graphClass, array $reflectors, string $fieldAnnotationName = GQL\Field::class, string $currentValue = 'value'): array { @@ -651,6 +672,8 @@ private static function getGraphQLTypeFieldsFromAnnotations(GraphClass $graphCla } /** + * @phpstan-param class-string $expectedAnnotation + * * Return fields config from Provider methods. * Loop through configured provider and extract fields targeting the targetType. */ @@ -670,11 +693,11 @@ private static function getGraphQLFieldsFromProviders(GraphClass $graphClass, st $annotations = $providerMetadata->getAnnotations($method); $annotation = self::getFirstAnnotationMatching($annotations, [GQL\Mutation::class, GQL\Query::class]); - if (!$annotation) { + if (null === $annotation) { continue; } - $annotationTargets = $annotation->targetType; + $annotationTargets = $annotation->targetType ?? null; if (null === $annotationTargets) { if ($isDefaultTarget) { @@ -687,10 +710,6 @@ private static function getGraphQLFieldsFromProviders(GraphClass $graphClass, st } } - if (is_string($annotationTargets)) { - $annotationTargets = [$annotationTargets]; - } - if (!in_array($targetType, $annotationTargets)) { continue; } @@ -736,13 +755,13 @@ private static function getDescriptionConfiguration(array $annotations, bool $wi { $config = []; $descriptionAnnotation = self::getFirstAnnotationMatching($annotations, GQL\Description::class); - if ($descriptionAnnotation) { + if (null !== $descriptionAnnotation) { $config['description'] = $descriptionAnnotation->value; } if ($withDeprecation) { $deprecatedAnnotation = self::getFirstAnnotationMatching($annotations, GQL\Deprecated::class); - if ($deprecatedAnnotation) { + if (null !== $deprecatedAnnotation) { $config['deprecationReason'] = $deprecatedAnnotation->value; } } @@ -774,9 +793,13 @@ private static function formatNamespaceForExpression(string $namespace): string /** * Get the first annotation matching given class. * + * @phpstan-template T of object + * @phpstan-param class-string|class-string[] $annotationClass + * @phpstan-return T|null + * * @param string|array $annotationClass * - * @return mixed + * @return object|null */ private static function getFirstAnnotationMatching(array $annotations, $annotationClass) { @@ -792,7 +815,7 @@ private static function getFirstAnnotationMatching(array $annotations, $annotati } } - return false; + return null; } /** @@ -819,7 +842,7 @@ private static function suffixName(string $name, string $suffix): string private static function guessType(GraphClass $graphClass, array $annotations): string { $columnAnnotation = self::getFirstAnnotationMatching($annotations, Column::class); - if ($columnAnnotation) { + if (null !== $columnAnnotation) { $type = self::resolveTypeFromDoctrineType($columnAnnotation->type); $nullable = $columnAnnotation->nullable; if ($type) { @@ -837,7 +860,7 @@ private static function guessType(GraphClass $graphClass, array $annotations): s ]; $associationAnnotation = self::getFirstAnnotationMatching($annotations, array_keys($associationAnnotations)); - if ($associationAnnotation) { + if (null !== $associationAnnotation) { $target = self::fullyQualifiedClassName($associationAnnotation->targetEntity, $graphClass->getNamespaceName()); $type = self::resolveTypeFromClass($target, ['type']); @@ -848,7 +871,7 @@ private static function guessType(GraphClass $graphClass, array $annotations): s } else { $isNullable = false; $joinColumn = self::getFirstAnnotationMatching($annotations, JoinColumn::class); - if ($joinColumn) { + if (null !== $joinColumn) { $isNullable = $joinColumn->nullable; } @@ -969,26 +992,16 @@ private static function resolveTypeFromClass(string $className, array $wantedTyp return null; } - /** - * Resolve a PHP class from a GraphQL type. - * - * @return string|array|null - */ - private static function resolveClassFromType(string $type) - { - return self::$classesMap[$type] ?? null; - } - /** * Search the classes map for class by predicate. * * @return array */ - private static function searchClassesMapBy(callable $predicate, string $type = null) + private static function searchClassesMapBy(callable $predicate, string $type) { $classNames = []; foreach (self::$classesMap as $gqlType => $config) { - if ($type && $config['type'] !== $type) { + if ($config['type'] !== $type) { continue; }