From 52898c9004b61d8ba068ac141875ff9b8f66658f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=93=AD=E6=98=95?= <715557344@qq.com> Date: Tue, 18 Jun 2024 18:34:53 +0800 Subject: [PATCH] Fixed bug that GraphQL cannot work. (#6894) --- .github/workflows/test-components.yml | 1 + composer.json | 1 + phpstan.neon.dist | 3 +- src/graphql/composer.json | 2 +- .../src/Annotation/AnnotationTrait.php | 56 -- src/graphql/src/Annotation/ExtendType.php | 29 - src/graphql/src/Annotation/Factory.php | 29 - src/graphql/src/Annotation/FailWith.php | 26 - src/graphql/src/Annotation/Field.php | 29 - src/graphql/src/Annotation/Logged.php | 26 - src/graphql/src/Annotation/Mutation.php | 29 - src/graphql/src/Annotation/Query.php | 29 - src/graphql/src/Annotation/Right.php | 29 - src/graphql/src/Annotation/SourceField.php | 34 - src/graphql/src/Annotation/Type.php | 29 - src/graphql/src/AnnotationReader.php | 247 ----- src/graphql/src/ClassCollector.php | 32 - src/graphql/src/ConfigProvider.php | 23 +- src/graphql/src/FieldsBuilder.php | 799 ---------------- src/graphql/src/FieldsBuilderFactory.php | 92 -- src/graphql/src/GraphQLMiddleware.php | 10 +- src/graphql/src/InputTypeGenerator.php | 75 -- src/graphql/src/InputTypeUtils.php | 95 -- src/graphql/src/QueryProvider.php | 76 -- src/graphql/src/Reader.php | 55 ++ src/graphql/src/ReaderFactory.php | 33 - .../src/RecursiveTypeMapperFactory.php | 81 -- src/graphql/src/ResolvableInputObjectType.php | 90 -- src/graphql/src/SchemaFactory.php | 30 + src/graphql/src/TypeAnnotatedObjectType.php | 73 -- src/graphql/src/TypeGenerator.php | 116 --- src/graphql/src/TypeMapper.php | 865 ------------------ 32 files changed, 92 insertions(+), 3052 deletions(-) delete mode 100644 src/graphql/src/Annotation/AnnotationTrait.php delete mode 100644 src/graphql/src/Annotation/ExtendType.php delete mode 100644 src/graphql/src/Annotation/Factory.php delete mode 100644 src/graphql/src/Annotation/FailWith.php delete mode 100644 src/graphql/src/Annotation/Field.php delete mode 100644 src/graphql/src/Annotation/Logged.php delete mode 100644 src/graphql/src/Annotation/Mutation.php delete mode 100644 src/graphql/src/Annotation/Query.php delete mode 100644 src/graphql/src/Annotation/Right.php delete mode 100644 src/graphql/src/Annotation/SourceField.php delete mode 100644 src/graphql/src/Annotation/Type.php delete mode 100644 src/graphql/src/AnnotationReader.php delete mode 100644 src/graphql/src/ClassCollector.php delete mode 100644 src/graphql/src/FieldsBuilder.php delete mode 100644 src/graphql/src/FieldsBuilderFactory.php delete mode 100644 src/graphql/src/InputTypeGenerator.php delete mode 100644 src/graphql/src/InputTypeUtils.php delete mode 100644 src/graphql/src/QueryProvider.php create mode 100644 src/graphql/src/Reader.php delete mode 100644 src/graphql/src/ReaderFactory.php delete mode 100644 src/graphql/src/RecursiveTypeMapperFactory.php delete mode 100644 src/graphql/src/ResolvableInputObjectType.php create mode 100644 src/graphql/src/SchemaFactory.php delete mode 100644 src/graphql/src/TypeAnnotatedObjectType.php delete mode 100644 src/graphql/src/TypeGenerator.php delete mode 100644 src/graphql/src/TypeMapper.php diff --git a/.github/workflows/test-components.yml b/.github/workflows/test-components.yml index 4b3e0647e..e62cd36c9 100644 --- a/.github/workflows/test-components.yml +++ b/.github/workflows/test-components.yml @@ -317,6 +317,7 @@ jobs: - name: Run Test Cases run: | ./.travis/requirement.install.sh + composer remove thecodingmachine/graphqlite --dev -W composer require "psr/simple-cache:${{ matrix.psr-version }}" composer update -oW composer analyse src/cache diff --git a/composer.json b/composer.json index e755a72d3..9427c18af 100644 --- a/composer.json +++ b/composer.json @@ -91,6 +91,7 @@ "symfony/serializer": "^5.0 || ^6.0", "symfony/uid": "^5.0 || ^6.0", "symfony/var-dumper": "^5.0 || ^6.0", + "thecodingmachine/graphqlite": "^7.0", "twig/twig": "^3.0", "vlucas/phpdotenv": "^5.0", "zircote/swagger-php": "^4.6" diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 2b32e822c..9a856a37c 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -24,7 +24,6 @@ parameters: - %currentWorkingDirectory%/src/database-pgsql/* - %currentWorkingDirectory%/src/db/src/PgSQL/* - %currentWorkingDirectory%/src/filesystem/* - - %currentWorkingDirectory%/src/graphql/* - %currentWorkingDirectory%/src/grpc/* - %currentWorkingDirectory%/src/grpc-server/* - %currentWorkingDirectory%/src/ide-helper/* @@ -84,4 +83,4 @@ parameters: - message: '#Method .* should return .* but returns#' path: src/collection/src/*.php - message: '#has no type specified#' - path: src/nacos/src/Protobuf/*.php \ No newline at end of file + path: src/nacos/src/Protobuf/*.php diff --git a/src/graphql/composer.json b/src/graphql/composer.json index 11f3f21f0..6da08fa78 100644 --- a/src/graphql/composer.json +++ b/src/graphql/composer.json @@ -15,7 +15,7 @@ "hyperf/contract": "~3.1.0", "hyperf/di": "~3.1.0", "swow/psr7-plus": "^1.0", - "thecodingmachine/graphqlite": "^3.0" + "thecodingmachine/graphqlite": "^7.0" }, "suggest": { "hyperf/http-message": "Required to use GraphQLMiddleware.", diff --git a/src/graphql/src/Annotation/AnnotationTrait.php b/src/graphql/src/Annotation/AnnotationTrait.php deleted file mode 100644 index dd92ece5a..000000000 --- a/src/graphql/src/Annotation/AnnotationTrait.php +++ /dev/null @@ -1,56 +0,0 @@ -getProperties(ReflectionProperty::IS_PUBLIC); - $result = []; - foreach ($properties as $property) { - $result[$property->getName()] = $property->getValue($this); - } - return $result; - } - - public function collectClass(string $className): void - { - AnnotationCollector::collectClass($className, static::class, $this); - ClassCollector::collect($className); - } - - public function collectMethod(string $className, ?string $target): void - { - AnnotationCollector::collectMethod($className, $target, static::class, $this); - ClassCollector::collect($className); - } - - public function collectProperty(string $className, ?string $target): void - { - AnnotationCollector::collectProperty($className, $target, static::class, $this); - ClassCollector::collect($className); - } - - protected function bindMainProperty(string $key, array $value) - { - if (isset($value['value'])) { - $this->{$key} = $value['value']; - } - } -} diff --git a/src/graphql/src/Annotation/ExtendType.php b/src/graphql/src/Annotation/ExtendType.php deleted file mode 100644 index 325cf1948..000000000 --- a/src/graphql/src/Annotation/ExtendType.php +++ /dev/null @@ -1,29 +0,0 @@ -reader = $reader; - if (! in_array($mode, [self::LAX_MODE, self::STRICT_MODE], true)) { - throw new InvalidArgumentException('The mode passed must be one of AnnotationReader::LAX_MODE, AnnotationReader::STRICT_MODE'); - } - $this->mode = $mode; - $this->strictNamespaces = $strictNamespaces; - } - - public function getTypeAnnotation(ReflectionClass $refClass): ?Type - { - try { - /** @var null|Type $type */ - $type = $this->getClassAnnotation($refClass, Type::class); - if ($type !== null && $type->isSelfType()) { - $type->setClass($refClass->getName()); - } - } catch (ClassNotFoundException $e) { - throw ClassNotFoundException::wrapException($e, $refClass->getName()); - } - return $type; - } - - public function getExtendTypeAnnotation(ReflectionClass $refClass): ?ExtendType - { - try { - /** @var null|ExtendType $extendType */ - $extendType = $this->getClassAnnotation($refClass, ExtendType::class); - } catch (ClassNotFoundException $e) { - throw ClassNotFoundException::wrapExceptionForExtendTag($e, $refClass->getName()); - } - return $extendType; - } - - public function getRequestAnnotation(ReflectionMethod $refMethod, string $annotationName): ?AbstractRequest - { - return $this->getMethodAnnotation($refMethod, $annotationName); - } - - public function getLoggedAnnotation(ReflectionMethod $refMethod): ?Logged - { - return $this->getMethodAnnotation($refMethod, Logged::class); - } - - public function getRightAnnotation(ReflectionMethod $refMethod): ?Right - { - return $this->getMethodAnnotation($refMethod, Right::class); - } - - public function getFailWithAnnotation(ReflectionMethod $refMethod): ?FailWith - { - return $this->getMethodAnnotation($refMethod, FailWith::class); - } - - /** - * @return SourceField[] - */ - public function getSourceFields(ReflectionClass $refClass): array - { - return $this->getClassAnnotations($refClass, SourceField::class); - } - - public function getFactoryAnnotation(ReflectionMethod $refMethod): ?Factory - { - return $this->getMethodAnnotation($refMethod, Factory::class); - } - - /** - * Returns the class annotations. Finds in the parents too. - * - * @return object[] - */ - public function getClassAnnotations(ReflectionClass $refClass, string $annotationClass): array - { - $toAddAnnotations = []; - do { - try { - $allAnnotations = $this->reader->getClassAnnotations($refClass); - $toAddAnnotations[] = array_filter($allAnnotations, function ($annotation) use ($annotationClass): bool { - return $annotation instanceof $annotationClass; - }); - } catch (AnnotationException $e) { - if ($this->mode === self::STRICT_MODE) { - throw $e; - } - if ($this->mode === self::LAX_MODE) { - if ($this->isErrorImportant($annotationClass, $refClass->getDocComment(), $refClass->getName())) { - throw $e; - } - } - } - $refClass = $refClass->getParentClass(); - } while ($refClass); - - if (! empty($toAddAnnotations)) { - return array_merge(...$toAddAnnotations); - } - return []; - } - - /** - * Returns a class annotation. Finds in the parents if not found in the main class. - * - * @return null|object - */ - private function getClassAnnotation(ReflectionClass $refClass, string $annotationClass) - { - do { - $type = null; - try { - $type = $this->reader->getClassAnnotation($refClass, $annotationClass); - } catch (AnnotationException $e) { - switch ($this->mode) { - case self::STRICT_MODE: - throw $e; - case self::LAX_MODE: - if ($this->isErrorImportant($annotationClass, $refClass->getDocComment(), $refClass->getName())) { - throw $e; - } - return null; - default: - throw new RuntimeException("Unexpected mode '{$this->mode}'."); // @codeCoverageIgnore - } - } - if ($type !== null) { - return $type; - } - $refClass = $refClass->getParentClass(); - } while ($refClass); - return null; - } - - /** - * Returns a method annotation and handles correctly errors. - * - * @return null|object - */ - private function getMethodAnnotation(ReflectionMethod $refMethod, string $annotationClass) - { - $cacheKey = $refMethod->getDeclaringClass()->getName() . '::' . $refMethod->getName() . '_' . $annotationClass; - if (isset($this->methodAnnotationCache[$cacheKey])) { - return $this->methodAnnotationCache[$cacheKey]; - } - - try { - return $this->methodAnnotationCache[$cacheKey] = $this->reader->getMethodAnnotation($refMethod, $annotationClass); - } catch (AnnotationException $e) { - switch ($this->mode) { - case self::STRICT_MODE: - throw $e; - case self::LAX_MODE: - if ($this->isErrorImportant($annotationClass, $refMethod->getDocComment(), $refMethod->getDeclaringClass()->getName())) { - throw $e; - } - return null; - default: - throw new RuntimeException("Unexpected mode '{$this->mode}'."); // @codeCoverageIgnore - } - } - } - - /** - * Returns true if the annotation class name is part of the docblock comment. - */ - private function isErrorImportant(string $annotationClass, string $docComment, string $className): bool - { - foreach ($this->strictNamespaces as $strictNamespace) { - if (strpos($className, $strictNamespace) === 0) { - return true; - } - } - $shortAnnotationClass = substr($annotationClass, strrpos($annotationClass, '\\') + 1); - return strpos($docComment, '@' . $shortAnnotationClass) !== false; - } -} diff --git a/src/graphql/src/ClassCollector.php b/src/graphql/src/ClassCollector.php deleted file mode 100644 index bee44682b..000000000 --- a/src/graphql/src/ClassCollector.php +++ /dev/null @@ -1,32 +0,0 @@ - [ - Schema::class => \TheCodingMachine\GraphQLite\Schema::class, - QueryProviderInterface::class => QueryProvider::class, - RecursiveTypeMapperInterface::class => RecursiveTypeMapperFactory::class, - Reader::class => ReaderFactory::class, - HydratorInterface::class => FactoryHydrator::class, - AuthenticationServiceInterface::class => FailAuthenticationService::class, - AuthorizationServiceInterface::class => FailAuthorizationService::class, - NamingStrategyInterface::class => NamingStrategy::class, + Schema::class => SchemaFactory::class, ], 'annotations' => [ 'scan' => [ 'paths' => [ __DIR__, ], - 'collectors' => [ - ClassCollector::class, - ], ], ], ]; diff --git a/src/graphql/src/FieldsBuilder.php b/src/graphql/src/FieldsBuilder.php deleted file mode 100644 index c9c00141d..000000000 --- a/src/graphql/src/FieldsBuilder.php +++ /dev/null @@ -1,799 +0,0 @@ -annotationReader = $annotationReader; - $this->typeMapper = $typeMapper; - $this->argumentResolver = $argumentResolver; - $this->authenticationService = $authenticationService; - $this->authorizationService = $authorizationService; - $this->typeResolver = $typeResolver; - $this->cachedDocBlockFactory = $cachedDocBlockFactory; - $this->namingStrategy = $namingStrategy; - } - - // TODO: Add RecursiveTypeMapper in the list of parameters for getQueries and REMOVE the ControllerQueryProviderFactory. - - /** - * @param object $controller - * @return QueryField[] - * @throws ReflectionException - */ - public function getQueries($controller): array - { - return $this->getFieldsByAnnotations($controller, Query::class, false); - } - - /** - * @param object $controller - * @return QueryField[] - * @throws ReflectionException - */ - public function getMutations($controller): array - { - return $this->getFieldsByAnnotations($controller, Mutation::class, false); - } - - /** - * @param mixed $controller - * @return array queryField indexed by name - */ - public function getFields($controller): array - { - $fieldAnnotations = $this->getFieldsByAnnotations($controller, Field::class, true); - - $refClass = new ReflectionClass($controller); - - /** @var SourceField[] $sourceFields */ - $sourceFields = $this->annotationReader->getSourceFields($refClass); - - if ($controller instanceof FromSourceFieldsInterface) { - $sourceFields = array_merge($sourceFields, $controller->getSourceFields()); - } - - $fieldsFromSourceFields = $this->getQueryFieldsFromSourceFields($sourceFields, $refClass); - - $fields = []; - foreach ($fieldAnnotations as $field) { - $fields[$field->name] = $field; - } - foreach ($fieldsFromSourceFields as $field) { - $fields[$field->name] = $field; - } - - return $fields; - } - - /** - * Track Field annotation in a self targeted type. - * - * @return array queryField indexed by name - */ - public function getSelfFields(string $className): array - { - $fieldAnnotations = $this->getFieldsByAnnotations(null, Field::class, false, $className); - - $refClass = new ReflectionClass($className); - - /** @var SourceField[] $sourceFields */ - $sourceFields = $this->annotationReader->getSourceFields($refClass); - - $fieldsFromSourceFields = $this->getQueryFieldsFromSourceFields($sourceFields, $refClass); - - $fields = []; - foreach ($fieldAnnotations as $field) { - $fields[$field->name] = $field; - } - foreach ($fieldsFromSourceFields as $field) { - $fields[$field->name] = $field; - } - - return $fields; - } - - /** - * @param ReflectionMethod $refMethod a method annotated with a Factory annotation - * @return array> returns an array of fields as accepted by the InputObjectType constructor - */ - public function getInputFields(ReflectionMethod $refMethod): array - { - $docBlockObj = $this->cachedDocBlockFactory->getDocBlock($refMethod); - // $docBlockComment = $docBlockObj->getSummary()."\n".$docBlockObj->getDescription()->render(); - - $parameters = $refMethod->getParameters(); - - return $this->mapParameters($parameters, $docBlockObj); - } - - /** - * @param object $controller - * @param bool $injectSource whether to inject the source object or not as the first argument - * @return QueryField[] - * @throws CannotMapTypeExceptionInterface - * @throws ReflectionException - */ - private function getFieldsByAnnotations($controller, string $annotationName, bool $injectSource, ?string $sourceClassName = null): array - { - if ($sourceClassName !== null) { - $refClass = new ReflectionClass($sourceClassName); - } else { - $refClass = new ReflectionClass($controller); - } - - $queryList = []; - - $oldDeclaringClass = null; - $context = null; - - $closestMatchingTypeClass = null; - if ($annotationName === Field::class) { - $parent = get_parent_class($refClass->getName()); - if ($parent !== null) { - $closestMatchingTypeClass = $this->typeMapper->findClosestMatchingParent((string) $parent); - } - } - - foreach ($refClass->getMethods() as $refMethod) { - if ($closestMatchingTypeClass !== null && $closestMatchingTypeClass === $refMethod->getDeclaringClass()->getName()) { - // Optimisation: no need to fetch annotations from parent classes that are ALREADY GraphQL types. - // We will merge the fields anyway. - break; - } - - // First, let's check the "Query" or "Mutation" or "Field" annotation - $queryAnnotation = $this->annotationReader->getRequestAnnotation($refMethod, $annotationName); - - if ($queryAnnotation !== null) { - $unauthorized = false; - if (! $this->isAuthorized($refMethod)) { - $failWith = $this->annotationReader->getFailWithAnnotation($refMethod); - if ($failWith === null) { - continue; - } - $unauthorized = true; - } - - $docBlockObj = $this->cachedDocBlockFactory->getDocBlock($refMethod); - $docBlockComment = $docBlockObj->getSummary() . "\n" . $docBlockObj->getDescription()->render(); - - $methodName = $refMethod->getName(); - $name = $queryAnnotation->getName() ?: $this->namingStrategy->getFieldNameFromMethodName($methodName); - - $parameters = $refMethod->getParameters(); - if ($injectSource === true) { - $first_parameter = array_shift($parameters); - // TODO: check that $first_parameter type is correct. - } - - $args = $this->mapParameters($parameters, $docBlockObj); - - if ($queryAnnotation->getOutputType()) { - try { - $type = $this->typeResolver->mapNameToOutputType($queryAnnotation->getOutputType()); - } catch (CannotMapTypeExceptionInterface $e) { - throw CannotMapTypeException::wrapWithReturnInfo($e, $refMethod); - } - } else { - $type = $this->mapReturnType($refMethod, $docBlockObj); - } - - if ($unauthorized) { - $failWithValue = $failWith->getValue(); - $callable = function () use ($failWithValue) { - return $failWithValue; - }; - if ($failWithValue === null && $type instanceof NonNull) { - $type = $type->getWrappedType(); - } - $queryList[] = new QueryField($name, $type, $args, $callable, null, $this->argumentResolver, $docBlockComment, $injectSource); - } else { - $callable = [$controller, $methodName]; - if ($sourceClassName !== null) { - $queryList[] = new QueryField($name, $type, $args, null, $callable[1], $this->argumentResolver, $docBlockComment, $injectSource); - } else { - $queryList[] = new QueryField($name, $type, $args, $callable, null, $this->argumentResolver, $docBlockComment, $injectSource); - } - } - } - } - - return $queryList; - } - - /** - * @return GraphQLType|OutputType - */ - private function mapReturnType(ReflectionMethod $refMethod, DocBlock $docBlockObj): GraphQLType - { - $returnType = $refMethod->getReturnType(); - if ($returnType !== null) { - $typeResolver = new \phpDocumentor\Reflection\TypeResolver(); - $phpdocType = $typeResolver->resolve((string) $returnType); - $phpdocType = $this->resolveSelf($phpdocType, $refMethod->getDeclaringClass()); - } else { - $phpdocType = new Mixed_(); - } - - $docBlockReturnType = $this->getDocBlocReturnType($docBlockObj, $refMethod); - - try { - /** @var GraphQLType|OutputType $type */ - $type = $this->mapType($phpdocType, $docBlockReturnType, $returnType ? $returnType->allowsNull() : false, false); - } catch (TypeMappingException $e) { - throw TypeMappingException::wrapWithReturnInfo($e, $refMethod); - } catch (CannotMapTypeExceptionInterface $e) { - throw CannotMapTypeException::wrapWithReturnInfo($e, $refMethod); - } - return $type; - } - - private function getDocBlocReturnType(DocBlock $docBlock, ReflectionMethod $refMethod): ?Type - { - /** @var Return_[] $returnTypeTags */ - $returnTypeTags = $docBlock->getTagsByName('return'); - if (count($returnTypeTags) > 1) { - throw InvalidDocBlockException::tooManyReturnTags($refMethod); - } - $docBlockReturnType = null; - if (isset($returnTypeTags[0])) { - $docBlockReturnType = $returnTypeTags[0]->getType(); - } - return $docBlockReturnType; - } - - /** - * @param array $sourceFields - * @return QueryField[] - * @throws CannotMapTypeException - * @throws CannotMapTypeExceptionInterface - * @throws ReflectionException - */ - private function getQueryFieldsFromSourceFields(array $sourceFields, ReflectionClass $refClass): array - { - if (empty($sourceFields)) { - return []; - } - - $typeField = $this->annotationReader->getTypeAnnotation($refClass); - $extendTypeField = $this->annotationReader->getExtendTypeAnnotation($refClass); - - if ($typeField !== null) { - $objectClass = $typeField->getClass(); - } elseif ($extendTypeField !== null) { - $objectClass = $extendTypeField->getClass(); - } else { - throw MissingAnnotationException::missingTypeExceptionToUseSourceField(); - } - - $objectRefClass = new ReflectionClass($objectClass); - - $oldDeclaringClass = null; - $context = null; - $queryList = []; - - foreach ($sourceFields as $sourceField) { - // Ignore the field if we must be logged. - $right = $sourceField->getRight(); - $unauthorized = false; - if (($sourceField->isLogged() && ! $this->authenticationService->isLogged()) - || ($right !== null && ! $this->authorizationService->isAllowed($right->getName()))) { - if (! $sourceField->canFailWith()) { - continue; - } - $unauthorized = true; - } - - try { - $refMethod = $this->getMethodFromPropertyName($objectRefClass, $sourceField->getName()); - } catch (FieldNotFoundException $e) { - throw FieldNotFoundException::wrapWithCallerInfo($e, $refClass->getName()); - } - - $methodName = $refMethod->getName(); - - $docBlockObj = $this->cachedDocBlockFactory->getDocBlock($refMethod); - $docBlockComment = $docBlockObj->getSummary() . "\n" . $docBlockObj->getDescription()->render(); - - $args = $this->mapParameters($refMethod->getParameters(), $docBlockObj); - - if ($sourceField->isId()) { - $type = GraphQLType::id(); - if (! $refMethod->getReturnType()->allowsNull()) { - $type = GraphQLType::nonNull($type); - } - } elseif ($sourceField->getOutputType()) { - try { - $type = $this->typeResolver->mapNameToOutputType($sourceField->getOutputType()); - } catch (CannotMapTypeExceptionInterface $e) { - throw CannotMapTypeException::wrapWithSourceField($e, $refClass, $sourceField); - } - } else { - $type = $this->mapReturnType($refMethod, $docBlockObj); - } - - if (! $unauthorized) { - $queryList[] = new QueryField($sourceField->getName(), $type, $args, null, $methodName, $this->argumentResolver, $docBlockComment, false); - } else { - $failWithValue = $sourceField->getFailWith(); - $callable = function () use ($failWithValue) { - return $failWithValue; - }; - if ($failWithValue === null && $type instanceof NonNull) { - $type = $type->getWrappedType(); - } - $queryList[] = new QueryField($sourceField->getName(), $type, $args, $callable, null, $this->argumentResolver, $docBlockComment, false); - } - } - return $queryList; - } - - private function getMethodFromPropertyName(ReflectionClass $reflectionClass, string $propertyName): ReflectionMethod - { - if ($reflectionClass->hasMethod($propertyName)) { - $methodName = $propertyName; - } else { - $upperCasePropertyName = ucfirst($propertyName); - if ($reflectionClass->hasMethod('get' . $upperCasePropertyName)) { - $methodName = 'get' . $upperCasePropertyName; - } elseif ($reflectionClass->hasMethod('is' . $upperCasePropertyName)) { - $methodName = 'is' . $upperCasePropertyName; - } else { - throw FieldNotFoundException::missingField($reflectionClass->getName(), $propertyName); - } - } - - return $reflectionClass->getMethod($methodName); - } - - /** - * Checks the Logged and Right annotations. - */ - private function isAuthorized(ReflectionMethod $reflectionMethod): bool - { - $loggedAnnotation = $this->annotationReader->getLoggedAnnotation($reflectionMethod); - - if ($loggedAnnotation !== null && ! $this->authenticationService->isLogged()) { - return false; - } - - $rightAnnotation = $this->annotationReader->getRightAnnotation($reflectionMethod); - - if ($rightAnnotation !== null && ! $this->authorizationService->isAllowed($rightAnnotation->getName())) { - return false; - } - - return true; - } - - /** - * Note: there is a bug in $refMethod->allowsNull that forces us to use $standardRefMethod->allowsNull instead. - * - * @param ReflectionParameter[] $refParameters - * @return array[] An array of ['type'=>Type, 'defaultValue'=>val] - * @throws MissingTypeHintException - */ - private function mapParameters(array $refParameters, DocBlock $docBlock): array - { - $args = []; - - $typeResolver = new \phpDocumentor\Reflection\TypeResolver(); - - foreach ($refParameters as $parameter) { - $parameterType = $parameter->getType(); - $allowsNull = $parameterType === null ? true : $parameterType->allowsNull(); - - $type = (string) $parameterType; - if ($type === '') { - throw MissingTypeHintException::missingTypeHint($parameter); - } - $phpdocType = $typeResolver->resolve($type); - $phpdocType = $this->resolveSelf($phpdocType, $parameter->getDeclaringClass()); - - /** @var DocBlock\Tags\Param[] $paramTags */ - $paramTags = $docBlock->getTagsByName('param'); - $docBlockType = null; - foreach ($paramTags as $paramTag) { - if ($paramTag->getVariableName() === $parameter->getName()) { - $docBlockType = $paramTag->getType(); - break; - } - } - - try { - $arr = [ - 'type' => $this->mapType($phpdocType, $docBlockType, $allowsNull || $parameter->isDefaultValueAvailable(), true), - ]; - } catch (TypeMappingException $e) { - throw TypeMappingException::wrapWithParamInfo($e, $parameter); - } catch (CannotMapTypeExceptionInterface $e) { - throw CannotMapTypeException::wrapWithParamInfo($e, $parameter); - } - - if ($parameter->allowsNull()) { - $arr['defaultValue'] = null; - } - if ($parameter->isDefaultValueAvailable()) { - $arr['defaultValue'] = $parameter->getDefaultValue(); - } - - $args[$parameter->getName()] = $arr; - } - - return $args; - } - - private function mapType(Type $type, ?Type $docBlockType, bool $isNullable, bool $mapToInputType): GraphQLType - { - $graphQlType = null; - - if ($type instanceof Array_ || $type instanceof Iterable_ || $type instanceof Mixed_) { - $graphQlType = $this->mapDocBlockType($type, $docBlockType, $isNullable, $mapToInputType); - } else { - try { - $graphQlType = $this->toGraphQlType($type, null, $mapToInputType); - if (! $isNullable) { - $graphQlType = GraphQLType::nonNull($graphQlType); - } - } catch (CannotMapTypeExceptionInterface|TypeMappingException $e) { - // Is the type iterable? If yes, let's analyze the docblock - // TODO: it would be better not to go through an exception for this. - if ($type instanceof Object_) { - $fqcn = (string) $type->getFqsen(); - $refClass = new ReflectionClass($fqcn); - // Note : $refClass->isIterable() is only accessible in PHP 7.2 - if ($refClass->implementsInterface(Iterator::class) || $refClass->implementsInterface(IteratorAggregate::class)) { - $graphQlType = $this->mapIteratorDocBlockType($type, $docBlockType, $isNullable); - } else { - throw $e; - } - } else { - throw $e; - } - } - } - - return $graphQlType; - } - - private function mapDocBlockType(Type $type, ?Type $docBlockType, bool $isNullable, bool $mapToInputType): GraphQLType - { - if ($docBlockType === null) { - throw TypeMappingException::createFromType($type); - } - if (! $isNullable) { - // Let's check a "null" value in the docblock - $isNullable = $this->isNullable($docBlockType); - } - - $filteredDocBlockTypes = $this->typesWithoutNullable($docBlockType); - if (empty($filteredDocBlockTypes)) { - throw TypeMappingException::createFromType($type); - } - - $unionTypes = []; - $lastException = null; - foreach ($filteredDocBlockTypes as $singleDocBlockType) { - try { - $unionTypes[] = $this->toGraphQlType($this->dropNullableType($singleDocBlockType), null, $mapToInputType); - } catch (CannotMapTypeExceptionInterface|TypeMappingException $e) { - // We have several types. It is ok not to be able to match one. - $lastException = $e; - } - } - - if (empty($unionTypes) && $lastException !== null) { - throw $lastException; - } - - if (count($unionTypes) === 1) { - $graphQlType = $unionTypes[0]; - } else { - $graphQlType = new UnionType($unionTypes, $this->typeMapper); - } - - /* elseif (count($filteredDocBlockTypes) === 1) { - $graphQlType = $this->toGraphQlType($filteredDocBlockTypes[0], $mapToInputType); - } else { - throw new GraphQLException('Union types are not supported (yet)'); - //$graphQlTypes = array_map([$this, 'toGraphQlType'], $filteredDocBlockTypes); - //$$graphQlType = new UnionType($graphQlTypes); - }*/ - - if (! $isNullable) { - $graphQlType = GraphQLType::nonNull($graphQlType); - } - return $graphQlType; - } - - /** - * Maps a type where the main PHP type is an iterator. - */ - private function mapIteratorDocBlockType(Type $type, ?Type $docBlockType, bool $isNullable): GraphQLType - { - if ($docBlockType === null) { - throw TypeMappingException::createFromType($type); - } - if (! $isNullable) { - // Let's check a "null" value in the docblock - $isNullable = $this->isNullable($docBlockType); - } - - $filteredDocBlockTypes = $this->typesWithoutNullable($docBlockType); - if (empty($filteredDocBlockTypes)) { - throw TypeMappingException::createFromType($type); - } - - $unionTypes = []; - $lastException = null; - foreach ($filteredDocBlockTypes as $singleDocBlockType) { - try { - $singleDocBlockType = $this->getTypeInArray($singleDocBlockType); - if ($singleDocBlockType !== null) { - $subGraphQlType = $this->toGraphQlType($singleDocBlockType, null, false); - } else { - $subGraphQlType = null; - } - - $unionTypes[] = $this->toGraphQlType($type, $subGraphQlType, false); - - // TODO: add here a scan of the $type variable and do stuff if it is iterable. - // TODO: remove the iterator type if specified in the docblock (@return Iterator|User[]) - // TODO: check there is at least one array (User[]) - } catch (CannotMapTypeExceptionInterface|TypeMappingException $e) { - // We have several types. It is ok not to be able to match one. - $lastException = $e; - } - } - - if (empty($unionTypes) && $lastException !== null) { - // We have an issue, let's try without the subType - return $this->mapDocBlockType($type, $docBlockType, $isNullable, false); - } - - if (count($unionTypes) === 1) { - $graphQlType = $unionTypes[0]; - } else { - $graphQlType = new UnionType($unionTypes, $this->typeMapper); - } - - if (! $isNullable) { - $graphQlType = GraphQLType::nonNull($graphQlType); - } - return $graphQlType; - } - - /** - * Casts a Type to a GraphQL type. - * Does not deal with nullable. - * - * @return GraphQLType (GraphQLType&InputType)|(GraphQLType&OutputType) - * @throws CannotMapTypeExceptionInterface - */ - private function toGraphQlType(Type $type, ?GraphQLType $subType, bool $mapToInputType): GraphQLType - { - if ($type instanceof Integer) { - return GraphQLType::int(); - } - if ($type instanceof String_) { - return GraphQLType::string(); - } - if ($type instanceof Boolean) { - return GraphQLType::boolean(); - } - if ($type instanceof Float_) { - return GraphQLType::float(); - } - if ($type instanceof Object_) { - $fqcn = (string) $type->getFqsen(); - switch ($fqcn) { - case '\DateTimeImmutable': - case '\DateTimeInterface': - return DateTimeType::getInstance(); - case '\\' . UploadedFileInterface::class: - return CustomTypesRegistry::getUploadType(); - case '\DateTime': - throw new GraphQLException('Type-hinting a parameter against DateTime is not allowed. Please use the DateTimeImmutable type instead.'); - case '\\' . ID::class: - return GraphQLType::id(); - default: - $className = ltrim((string) $type->getFqsen(), '\\'); - if ($mapToInputType) { - return $this->typeMapper->mapClassToInputType($className); - } - return $this->typeMapper->mapClassToInterfaceOrType($className, $subType); - } - } elseif ($type instanceof Array_) { - return GraphQLType::listOf(GraphQLType::nonNull($this->toGraphQlType($type->getValueType(), $subType, $mapToInputType))); - } else { - throw TypeMappingException::createFromType($type); - } - } - - /** - * Removes "null" from the type (if it is compound). Return an array of types (not a Compound type). - */ - private function typesWithoutNullable(Type $docBlockTypeHint): array - { - if ($docBlockTypeHint instanceof Compound) { - $docBlockTypeHints = iterator_to_array($docBlockTypeHint); - } else { - $docBlockTypeHints = [$docBlockTypeHint]; - } - return array_filter($docBlockTypeHints, function ($item) { - return ! $item instanceof Null_; - }); - } - - /** - * Drops "Nullable" types and return the core type. - */ - private function dropNullableType(Type $typeHint): Type - { - if ($typeHint instanceof Nullable) { - return $typeHint->getActualType(); - } - return $typeHint; - } - - /** - * Resolves a list type. - */ - private function getTypeInArray(Type $typeHint): ?Type - { - $typeHint = $this->dropNullableType($typeHint); - - if (! $typeHint instanceof Array_) { - return null; - } - - return $this->dropNullableType($typeHint->getValueType()); - } - - private function isNullable(Type $docBlockTypeHint): bool - { - if ($docBlockTypeHint instanceof Null_) { - return true; - } - if ($docBlockTypeHint instanceof Compound) { - foreach ($docBlockTypeHint as $type) { - if ($type instanceof Null_) { - return true; - } - } - } - - return false; - } - - /** - * Resolves "self" types into the class type. - */ - private function resolveSelf(Type $type, ReflectionClass $reflectionClass): Type - { - if ($type instanceof Self_) { - return new Object_(new Fqsen('\\' . $reflectionClass->getName())); - } - return $type; - } -} diff --git a/src/graphql/src/FieldsBuilderFactory.php b/src/graphql/src/FieldsBuilderFactory.php deleted file mode 100644 index 4003feff8..000000000 --- a/src/graphql/src/FieldsBuilderFactory.php +++ /dev/null @@ -1,92 +0,0 @@ -annotationReader = $annotationReader; - $this->hydrator = $hydrator; - $this->authenticationService = $authenticationService; - $this->authorizationService = $authorizationService; - $this->typeResolver = $typeResolver; - $this->cachedDocBlockFactory = $cachedDocBlockFactory; - $this->namingStrategy = $namingStrategy; - } - - public function buildFieldsBuilder(RecursiveTypeMapperInterface $typeMapper): FieldsBuilder - { - return new FieldsBuilder( - $this->annotationReader, - $typeMapper, - new ArgumentResolver($this->hydrator), - $this->authenticationService, - $this->authorizationService, - $this->typeResolver, - $this->cachedDocBlockFactory, - $this->namingStrategy - ); - } -} diff --git a/src/graphql/src/GraphQLMiddleware.php b/src/graphql/src/GraphQLMiddleware.php index 79e6d21f4..2694c6f9a 100644 --- a/src/graphql/src/GraphQLMiddleware.php +++ b/src/graphql/src/GraphQLMiddleware.php @@ -24,14 +24,8 @@ use Psr\Http\Server\RequestHandlerInterface; class GraphQLMiddleware implements MiddlewareInterface { - /** - * @var Schema - */ - protected $schema; - - public function __construct(Schema $schema) + public function __construct(protected Schema $schema) { - $this->schema = $schema; } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface @@ -42,7 +36,7 @@ class GraphQLMiddleware implements MiddlewareInterface $input = $request->getParsedBody(); $query = $input['query']; - $variableValues = isset($input['variables']) ? $input['variables'] : null; + $variableValues = $input['variables'] ?? null; $result = GraphQL::executeQuery($this->schema, $query, null, null, $variableValues); return ResponseContext::get()->setBody(new SwooleStream(Json::encode($result))); diff --git a/src/graphql/src/InputTypeGenerator.php b/src/graphql/src/InputTypeGenerator.php deleted file mode 100644 index 2f91a128c..000000000 --- a/src/graphql/src/InputTypeGenerator.php +++ /dev/null @@ -1,75 +0,0 @@ - - */ - private $cache = []; - - /** - * @var ArgumentResolver - */ - private $argumentResolver; - - /** - * @var InputTypeUtils - */ - private $inputTypeUtils; - - public function __construct( - InputTypeUtils $inputTypeUtils, - FieldsBuilderFactory $fieldsBuilderFactory, - ArgumentResolver $argumentResolver - ) { - $this->inputTypeUtils = $inputTypeUtils; - $this->fieldsBuilderFactory = $fieldsBuilderFactory; - $this->argumentResolver = $argumentResolver; - } - - public function mapFactoryMethod(string $factory, string $methodName, RecursiveTypeMapperInterface $recursiveTypeMapper, ContainerInterface $container): InputObjectType - { - $method = new ReflectionMethod($factory, $methodName); - - if ($method->isStatic()) { - $object = $factory; - } else { - $object = $container->get($factory); - } - - [$inputName, $className] = $this->inputTypeUtils->getInputTypeNameAndClassName($method); - - if (! isset($this->cache[$inputName])) { - // TODO: add comment argument. - $this->cache[$inputName] = new ResolvableInputObjectType($inputName, $this->fieldsBuilderFactory, $recursiveTypeMapper, $object, $methodName, $this->argumentResolver, null); - } - - return $this->cache[$inputName]; - } -} diff --git a/src/graphql/src/InputTypeUtils.php b/src/graphql/src/InputTypeUtils.php deleted file mode 100644 index eb60a6a0c..000000000 --- a/src/graphql/src/InputTypeUtils.php +++ /dev/null @@ -1,95 +0,0 @@ -annotationReader = $annotationReader; - $this->namingStrategy = $namingStrategy; - } - - /** - * Returns an array with 2 elements: [ $inputName, $className ]. - * - * @return string[] - */ - public function getInputTypeNameAndClassName(ReflectionMethod $method): array - { - $fqsen = ltrim((string) $this->validateReturnType($method), '\\'); - $factory = $this->annotationReader->getFactoryAnnotation($method); - if ($factory === null) { - throw new RuntimeException($method->getDeclaringClass()->getName() . '::' . $method->getName() . ' has no @Factory annotation.'); - } - return [$this->namingStrategy->getInputTypeName($fqsen, $factory), $fqsen]; - } - - private function validateReturnType(ReflectionMethod $refMethod): Fqsen - { - $returnType = $refMethod->getReturnType(); - if ($returnType === null) { - throw MissingTypeHintException::missingReturnType($refMethod); - } - - if ($returnType->allowsNull()) { - throw MissingTypeHintException::nullableReturnType($refMethod); - } - - $type = (string) $returnType; - - $typeResolver = new TypeResolver(); - - $phpdocType = $typeResolver->resolve($type); - $phpdocType = $this->resolveSelf($phpdocType, $refMethod->getDeclaringClass()); - if (! $phpdocType instanceof Object_) { - throw MissingTypeHintException::invalidReturnType($refMethod); - } - - return $phpdocType->getFqsen(); - } - - /** - * Resolves "self" types into the class type. - */ - private function resolveSelf(Type $type, ReflectionClass $reflectionClass): Type - { - if ($type instanceof Self_) { - return new Object_(new Fqsen('\\' . $reflectionClass->getName())); - } - return $type; - } -} diff --git a/src/graphql/src/QueryProvider.php b/src/graphql/src/QueryProvider.php deleted file mode 100644 index b779c4525..000000000 --- a/src/graphql/src/QueryProvider.php +++ /dev/null @@ -1,76 +0,0 @@ -fieldsBuilderFactory = $fieldsBuilderFactory; - $this->recursiveTypeMapper = $recursiveTypeMapper; - $this->container = $container; - } - - /** - * @return QueryField[] - */ - public function getQueries(): array - { - $queryList = []; - $classes = AnnotationCollector::getMethodsByAnnotation(Query::class); - $classes = array_unique(array_column($classes, 'class')); - foreach ($classes as $className) { - $fieldsBuilder = $this->fieldsBuilderFactory->buildFieldsBuilder($this->recursiveTypeMapper); - $queryList = array_merge($queryList, $fieldsBuilder->getQueries($this->container->get($className))); - } - return $queryList; - } - - /** - * @return QueryField[] - */ - public function getMutations(): array - { - $mutationList = []; - $classes = AnnotationCollector::getMethodsByAnnotation(Mutation::class); - $classes = array_unique(array_column($classes, 'class')); - foreach ($classes as $className) { - $fieldsBuilder = $this->fieldsBuilderFactory->buildFieldsBuilder($this->recursiveTypeMapper); - $mutationList = array_merge($mutationList, $fieldsBuilder->getMutations($this->container->get($className))); - } - return $mutationList; - } -} diff --git a/src/graphql/src/Reader.php b/src/graphql/src/Reader.php new file mode 100644 index 000000000..54e1dd591 --- /dev/null +++ b/src/graphql/src/Reader.php @@ -0,0 +1,55 @@ +cache = $cache; - $this->container = $container; - $this->namingStrategy = $namingStrategy; - $this->typeRegistry = $typeRegistry; - } - - public function __invoke() - { - $annotationReader = new AnnotationReader($this->container->get(Reader::class), AnnotationReader::LAX_MODE); - $typeGenerator = $this->container->get(TypeGenerator::class); - $inputTypeGenerator = $this->container->get(InputTypeGenerator::class); - $inputTypeUtils = $this->container->get(InputTypeUtils::class); - $namingStrategy = $this->container->get(NamingStrategyInterface::class); - $lockStore = new FlockStore(sys_get_temp_dir()); - $lockFactory = new LockFactory($lockStore); - - $typeMappers[] = new TypeMapper( - 'app', - $typeGenerator, - $inputTypeGenerator, - $inputTypeUtils, - $this->container, - $annotationReader, - $namingStrategy, - $lockFactory, - $this->cache - ); - $typeMappers[] = new PorpaginasTypeMapper(); - $compositeTypeMapper = new CompositeTypeMapper($typeMappers); - return new RecursiveTypeMapper($compositeTypeMapper, $this->namingStrategy, $this->cache, $this->typeRegistry); - } -} diff --git a/src/graphql/src/ResolvableInputObjectType.php b/src/graphql/src/ResolvableInputObjectType.php deleted file mode 100644 index 835618f4f..000000000 --- a/src/graphql/src/ResolvableInputObjectType.php +++ /dev/null @@ -1,90 +0,0 @@ -|callable - */ - private $resolve; - - /** - * QueryField constructor. - * @param object|string $factory - */ - public function __construct(string $name, FieldsBuilderFactory $controllerQueryProviderFactory, RecursiveTypeMapperInterface $recursiveTypeMapper, $factory, string $methodName, ArgumentResolver $argumentResolver, ?string $comment, array $additionalConfig = []) - { - $this->argumentResolver = $argumentResolver; - $this->resolve = [$factory, $methodName]; - - $fields = function () use ($controllerQueryProviderFactory, $factory, $methodName, $recursiveTypeMapper) { - $method = new ReflectionMethod($factory, $methodName); - $fieldProvider = $controllerQueryProviderFactory->buildFieldsBuilder($recursiveTypeMapper); - return $fieldProvider->getInputFields($method); - }; - - $config = [ - 'name' => $name, - 'fields' => $fields, - ]; - if ($comment) { - $config['description'] = $comment; - } - - $config += $additionalConfig; - InputObjectType::__construct($config); - } - - /** - * @return object - */ - public function resolve(array $args) - { - $toPassArgs = []; - foreach ($this->getFields() as $name => $field) { - $type = $field->getType(); - if (isset($args[$name])) { - $val = $this->argumentResolver->resolve($args[$name], $type); - } elseif ($field->defaultValueExists()) { - $val = $field->defaultValue; - } else { - throw new GraphQLException("Expected argument '{$name}' was not provided in GraphQL input type '" . $this->name . "' used in factory '" . get_class($this->resolve[0]) . '::' . $this->resolve[1] . "()'"); - } - - $toPassArgs[] = $val; - } - - $resolve = $this->resolve; - - return $resolve(...$toPassArgs); - } -} diff --git a/src/graphql/src/SchemaFactory.php b/src/graphql/src/SchemaFactory.php new file mode 100644 index 000000000..8661be1e7 --- /dev/null +++ b/src/graphql/src/SchemaFactory.php @@ -0,0 +1,30 @@ +get(CacheInterface::class), $container); + $factory->addTypeNamespace('App'); + $factory->addControllerNamespace('App'); + $factory->setDoctrineAnnotationReader($container->get(Reader::class)); + + return $factory->createSchema(); + } +} diff --git a/src/graphql/src/TypeAnnotatedObjectType.php b/src/graphql/src/TypeAnnotatedObjectType.php deleted file mode 100644 index 1319a1b6b..000000000 --- a/src/graphql/src/TypeAnnotatedObjectType.php +++ /dev/null @@ -1,73 +0,0 @@ -className = $className; - - parent::__construct($config); - } - - public static function createFromAnnotatedClass(string $typeName, string $className, $annotatedObject, FieldsBuilderFactory $fieldsBuilderFactory, RecursiveTypeMapperInterface $recursiveTypeMapper): self - { - return new self($className, [ - 'name' => $typeName, - 'fields' => function () use ($annotatedObject, $recursiveTypeMapper, $className, $fieldsBuilderFactory) { - $parentClass = get_parent_class($className); - $parentType = null; - if ($parentClass !== false) { - if ($recursiveTypeMapper->canMapClassToType($parentClass)) { - $parentType = $recursiveTypeMapper->mapClassToType($parentClass, null); - } - } - - $fieldProvider = $fieldsBuilderFactory->buildFieldsBuilder($recursiveTypeMapper); - if ($annotatedObject !== null) { - $fields = $fieldProvider->getFields($annotatedObject); - } else { - $fields = $fieldProvider->getSelfFields($className); - } - if ($parentType !== null) { - $finalFields = $parentType->getFields(); - foreach ($fields as $name => $field) { - $finalFields[$name] = $field; - } - return $finalFields; - } - return $fields; - }, - 'interfaces' => function () use ($className, $recursiveTypeMapper) { - return $recursiveTypeMapper->findInterfaces($className); - }, - ]); - } - - public function getMappedClassName(): string - { - return $this->className; - } -} diff --git a/src/graphql/src/TypeGenerator.php b/src/graphql/src/TypeGenerator.php deleted file mode 100644 index aa3b5956c..000000000 --- a/src/graphql/src/TypeGenerator.php +++ /dev/null @@ -1,116 +0,0 @@ -annotationReader = $annotationReader; - $this->fieldsBuilderFactory = $fieldsBuilderFactory; - $this->namingStrategy = $namingStrategy; - $this->typeRegistry = $typeRegistry; - $this->container = $container; - } - - /** - * @param string $annotatedObjectClassName the FQCN of an object with a Type annotation - * @throws ReflectionException - */ - public function mapAnnotatedObject(string $annotatedObjectClassName, RecursiveTypeMapperInterface $recursiveTypeMapper): MutableObjectType - { - $refTypeClass = new ReflectionClass($annotatedObjectClassName); - - $typeField = $this->annotationReader->getTypeAnnotation($refTypeClass); - - if ($typeField === null) { - throw MissingAnnotationException::missingTypeException(); - } - - $typeName = $this->namingStrategy->getOutputTypeName($refTypeClass->getName(), $typeField); - - if ($this->typeRegistry->hasType($typeName)) { - return $this->typeRegistry->getMutableObjectType($typeName); - } - - if (! $typeField->isSelfType()) { - $annotatedObject = $this->container->get($annotatedObjectClassName); - } else { - $annotatedObject = null; - } - - return TypeAnnotatedObjectType::createFromAnnotatedClass($typeName, $typeField->getClass(), $annotatedObject, $this->fieldsBuilderFactory, $recursiveTypeMapper); - } - - /** - * @param object $annotatedObject an object with a ExtendType annotation - */ - public function extendAnnotatedObject($annotatedObject, MutableObjectType $type, RecursiveTypeMapperInterface $recursiveTypeMapper) - { - $refTypeClass = new ReflectionClass($annotatedObject); - - $extendTypeAnnotation = $this->annotationReader->getExtendTypeAnnotation($refTypeClass); - - if ($extendTypeAnnotation === null) { - throw MissingAnnotationException::missingExtendTypeException(); - } - - $type->addFields(function () use ($annotatedObject, $recursiveTypeMapper) { - $fieldProvider = $this->fieldsBuilderFactory->buildFieldsBuilder($recursiveTypeMapper); - return $fieldProvider->getFields($annotatedObject); - }); - } -} diff --git a/src/graphql/src/TypeMapper.php b/src/graphql/src/TypeMapper.php deleted file mode 100644 index cd646e138..000000000 --- a/src/graphql/src/TypeMapper.php +++ /dev/null @@ -1,865 +0,0 @@ - Maps a domain class to the GraphQL type annotated class - */ - private $mapClassToTypeArray = []; - - /** - * @var array> Maps a domain class to one or many type extenders (with the ExtendType annotation) The array of type extenders has a key and value equals to FQCN - */ - private $mapClassToExtendTypeArray = []; - - /** - * @var array Maps a GraphQL type name to the GraphQL type annotated class - */ - private $mapNameToType = []; - - /** - * @var array> Maps a GraphQL type name to one or many type extenders (with the ExtendType annotation) The array of type extenders has a key and value equals to FQCN - */ - private $mapNameToExtendType = []; - - /** - * @var array Maps a domain class to the factory method that creates the input type in the form [classname, methodname] - */ - private $mapClassToFactory = []; - - /** - * @var array Maps a GraphQL input type name to the factory method that creates the input type in the form [classname, methodname] - */ - private $mapInputNameToFactory = []; - - /** - * @var ContainerInterface - */ - private $container; - - /** - * @var TypeGenerator - */ - private $typeGenerator; - - /** - * @var null|int - */ - private $mapTtl; - - /** - * @var bool - */ - private $fullMapComputed = false; - - /** - * @var bool - */ - private $fullMapClassToExtendTypeArrayComputed = false; - - /** - * @var bool - */ - private $fullMapNameToExtendTypeArrayComputed = false; - - /** - * @var NamingStrategyInterface - */ - private $namingStrategy; - - /** - * @var InputTypeGenerator - */ - private $inputTypeGenerator; - - /** - * @var InputTypeUtils - */ - private $inputTypeUtils; - - /** - * The array of globbed classes. - * Only instantiable classes are returned. - * Key: fully qualified class name. - * - * @var array - */ - private $classes; - - /** - * @var bool - */ - private $recursive; - - /** - * @var LockFactory - */ - private $lockFactory; - - /** - * @param string $namespace The namespace that contains the GraphQL types (they must have a `@Type` annotation) - */ - public function __construct(string $namespace, TypeGenerator $typeGenerator, InputTypeGenerator $inputTypeGenerator, InputTypeUtils $inputTypeUtils, ContainerInterface $container, AnnotationReader $annotationReader, NamingStrategyInterface $namingStrategy, LockFactory $lockFactory, CacheInterface $cache, ?int $globTtl = 2, ?int $mapTtl = null, bool $recursive = true) - { - $this->namespace = $namespace; - $this->typeGenerator = $typeGenerator; - $this->container = $container; - $this->annotationReader = $annotationReader; - $this->namingStrategy = $namingStrategy; - $this->cache = $cache; - $this->globTtl = $globTtl; - $this->mapTtl = $mapTtl; - $this->inputTypeGenerator = $inputTypeGenerator; - $this->inputTypeUtils = $inputTypeUtils; - $this->recursive = $recursive; - $this->lockFactory = $lockFactory; - } - - /** - * Returns true if this type mapper can map the $className FQCN to a GraphQL type. - */ - public function canMapClassToType(string $className): bool - { - $typeClassName = $this->getTypeFromCacheByObjectClass($className); - - if ($typeClassName !== null) { - return true; - } - - $map = $this->getMapClassToType(); - - return isset($map[$className]); - } - - /** - * Maps a PHP fully qualified class name to a GraphQL type. - * - * @param string $className the exact class name to look for (this function does not look into parent classes) - * @param null|OutputType $subType an optional sub-type if the main class is an iterator that needs to be typed - * @throws CannotMapTypeExceptionInterface - */ - public function mapClassToType(string $className, ?OutputType $subType, RecursiveTypeMapperInterface $recursiveTypeMapper): MutableObjectType - { - $typeClassName = $this->getTypeFromCacheByObjectClass($className); - - if ($typeClassName === null) { - $map = $this->getMapClassToType(); - if (! isset($map[$className])) { - throw CannotMapTypeException::createForType($className); - } - $typeClassName = $map[$className]; - } - - return $this->typeGenerator->mapAnnotatedObject($typeClassName, $recursiveTypeMapper); - } - - /** - * Returns the list of classes that have matching input GraphQL types. - * - * @return string[] - */ - public function getSupportedClasses(): array - { - return array_keys($this->getMapClassToType()); - } - - /** - * Returns true if this type mapper can map the $className FQCN to a GraphQL input type. - */ - public function canMapClassToInputType(string $className): bool - { - $factory = $this->getFactoryFromCacheByObjectClass($className); - - if ($factory !== null) { - return true; - } - $map = $this->getMapClassToFactory(); - return isset($map[$className]); - } - - /** - * Maps a PHP fully qualified class name to a GraphQL input type. - * - * @throws CannotMapTypeExceptionInterface - */ - public function mapClassToInputType(string $className, RecursiveTypeMapperInterface $recursiveTypeMapper): InputObjectType - { - $factory = $this->getFactoryFromCacheByObjectClass($className); - - if ($factory === null) { - $map = $this->getMapClassToFactory(); - if (! isset($map[$className])) { - throw CannotMapTypeException::createForInputType($className); - } - $factory = $map[$className]; - } - - return $this->inputTypeGenerator->mapFactoryMethod($factory[0], $factory[1], $recursiveTypeMapper, $this->container); - } - - /** - * Returns a GraphQL type by name (can be either an input or output type). - * - * @param string $typeName The name of the GraphQL type - * @return \GraphQL\Type\Definition\Type&(InputType|OutputType) - * @throws CannotMapTypeExceptionInterface - * @throws ReflectionException - */ - public function mapNameToType(string $typeName, RecursiveTypeMapperInterface $recursiveTypeMapper): \GraphQL\Type\Definition\Type - { - $typeClassName = $this->getTypeFromCacheByGraphQLTypeName($typeName); - if ($typeClassName === null) { - $factory = $this->getFactoryFromCacheByGraphQLInputTypeName($typeName); - if ($factory === null) { - $mapNameToType = $this->getMapNameToType(); - if (isset($mapNameToType[$typeName])) { - $typeClassName = $mapNameToType[$typeName]; - } else { - $mapInputNameToFactory = $this->getMapInputNameToFactory(); - if (isset($mapInputNameToFactory[$typeName])) { - $factory = $mapInputNameToFactory[$typeName]; - } - } - } - } - - if (isset($typeClassName)) { - return $this->typeGenerator->mapAnnotatedObject($typeClassName, $recursiveTypeMapper); - } - if (isset($factory)) { - return $this->inputTypeGenerator->mapFactoryMethod($factory[0], $factory[1], $recursiveTypeMapper, $this->container); - } - - throw CannotMapTypeException::createForName($typeName); - } - - /** - * Returns true if this type mapper can map the $typeName GraphQL name to a GraphQL type. - * - * @param string $typeName The name of the GraphQL type - */ - public function canMapNameToType(string $typeName): bool - { - $typeClassName = $this->getTypeFromCacheByGraphQLTypeName($typeName); - - if ($typeClassName !== null) { - return true; - } - - $factory = $this->getFactoryFromCacheByGraphQLInputTypeName($typeName); - if ($factory !== null) { - return true; - } - - $this->getMaps(); - - return isset($this->mapNameToType[$typeName]) || isset($this->mapInputNameToFactory[$typeName]); - } - - /** - * Returns true if this type mapper can extend an existing type for the $className FQCN. - */ - public function canExtendTypeForClass(string $className, MutableObjectType $type, RecursiveTypeMapperInterface $recursiveTypeMapper): bool - { - $extendTypeClassName = $this->getExtendTypesFromCacheByObjectClass($className); - - if ($extendTypeClassName === null) { - $map = $this->getMapClassToExtendTypeArray(); - } - - return isset($this->mapClassToExtendTypeArray[$className]); - } - - /** - * Extends the existing GraphQL type that is mapped to $className. - * - * @throws CannotMapTypeExceptionInterface - */ - public function extendTypeForClass(string $className, MutableObjectType $type, RecursiveTypeMapperInterface $recursiveTypeMapper): void - { - $extendTypeClassNames = $this->getExtendTypesFromCacheByObjectClass($className); - - if ($extendTypeClassNames === null) { - $this->getMapClassToExtendTypeArray(); - } - - if (! isset($this->mapClassToExtendTypeArray[$className])) { - throw CannotMapTypeException::createForExtendType($className, $type); - } - - foreach ($this->mapClassToExtendTypeArray[$className] as $extendedTypeClass) { - $this->typeGenerator->extendAnnotatedObject($this->container->get($extendedTypeClass), $type, $recursiveTypeMapper); - } - } - - /** - * Returns true if this type mapper can extend an existing type for the $typeName GraphQL type. - */ - public function canExtendTypeForName(string $typeName, MutableObjectType $type, RecursiveTypeMapperInterface $recursiveTypeMapper): bool - { - $typeClassNames = $this->getExtendTypesFromCacheByGraphQLTypeName($typeName); - - if ($typeClassNames !== null) { - return true; - } - - /*$factory = $this->getFactoryFromCacheByGraphQLInputTypeName($typeName); - if ($factory !== null) { - return true; - }*/ - - $map = $this->getMapNameToExtendType($recursiveTypeMapper); - - return isset($map[$typeName])/* || isset($this->mapInputNameToFactory[$typeName]) */; - } - - /** - * Extends the existing GraphQL type that is mapped to the $typeName GraphQL type. - * - * @throws CannotMapTypeExceptionInterface - */ - public function extendTypeForName(string $typeName, MutableObjectType $type, RecursiveTypeMapperInterface $recursiveTypeMapper): void - { - $extendTypeClassNames = $this->getExtendTypesFromCacheByGraphQLTypeName($typeName); - if ($extendTypeClassNames === null) { - /*$factory = $this->getFactoryFromCacheByGraphQLInputTypeName($typeName); - if ($factory === null) {*/ - $map = $this->getMapNameToExtendType($recursiveTypeMapper); - if (! isset($map[$typeName])) { - throw CannotMapTypeException::createForExtendName($typeName, $type); - } - $extendTypeClassNames = $map[$typeName]; - - // } - } - - foreach ($extendTypeClassNames as $extendedTypeClass) { - $this->typeGenerator->extendAnnotatedObject($this->container->get($extendedTypeClass), $type, $recursiveTypeMapper); - } - - /*if (isset($this->mapInputNameToFactory[$typeName])) { - $factory = $this->mapInputNameToFactory[$typeName]; - return $this->inputTypeGenerator->mapFactoryMethod($this->container->get($factory[0]), $factory[1], $recursiveTypeMapper); - }*/ - } - - /** - * Returns an array of fully qualified class names. - * - * @return array> - */ - private function getMaps(): array - { - if ($this->fullMapComputed === false) { - $namespace = str_replace('\\', '_', $this->namespace); - $keyClassCache = 'globTypeMapper_' . $namespace; - $keyNameCache = 'globTypeMapper_names_' . $namespace; - $keyInputClassCache = 'globInputTypeMapper_' . $namespace; - $keyInputNameCache = 'globInputTypeMapper_names_' . $namespace; - $this->mapClassToTypeArray = $this->cache->get($keyClassCache); - $this->mapNameToType = $this->cache->get($keyNameCache); - $this->mapClassToFactory = $this->cache->get($keyInputClassCache); - $this->mapInputNameToFactory = $this->cache->get($keyInputNameCache); - if ($this->mapClassToTypeArray === null - || $this->mapNameToType === null - || $this->mapClassToFactory === null - || $this->mapInputNameToFactory === null - ) { - $lock = $this->lockFactory->createLock('buildmap_' . $this->namespace, 5); - if (! $lock->acquire()) { - // Lock is being held right now. Generation is happening. - // Let's wait and fetch the result from the cache. - $lock->acquire(true); - $lock->release(); - return $this->getMaps(); - } - try { - $this->buildMap(); - } finally { - $lock->release(); - } - // This is a very short lived cache. Useful to avoid overloading a server in case of heavy load. - // Defaults to 2 seconds. - $this->cache->set($keyClassCache, $this->mapClassToTypeArray, $this->globTtl); - $this->cache->set($keyNameCache, $this->mapNameToType, $this->globTtl); - $this->cache->set($keyInputClassCache, $this->mapClassToFactory, $this->globTtl); - $this->cache->set($keyInputNameCache, $this->mapInputNameToFactory, $this->globTtl); - } - $this->fullMapComputed = true; - } - return [ - 'mapClassToTypeArray' => $this->mapClassToTypeArray, - 'mapNameToType' => $this->mapNameToType, - 'mapClassToFactory' => $this->mapClassToFactory, - 'mapInputNameToFactory' => $this->mapInputNameToFactory, - ]; - } - - private function getMapClassToType(): array - { - return $this->getMaps()['mapClassToTypeArray']; - } - - private function getMapNameToType(): array - { - return $this->getMaps()['mapNameToType']; - } - - private function getMapClassToFactory(): array - { - return $this->getMaps()['mapClassToFactory']; - } - - private function getMapInputNameToFactory(): array - { - return $this->getMaps()['mapInputNameToFactory']; - } - - private function getMapClassToExtendTypeArray(): array - { - if ($this->fullMapClassToExtendTypeArrayComputed === false) { - $namespace = str_replace('\\', '_', $this->namespace); - $keyExtendClassCache = 'globTypeMapperExtend_' . $namespace; - $this->mapClassToExtendTypeArray = $this->cache->get($keyExtendClassCache); - if ($this->mapClassToExtendTypeArray === null) { - $lock = $this->lockFactory->createLock('buildmapclassextend_' . $this->namespace, 5); - if (! $lock->acquire()) { - // Lock is being held right now. Generation is happening. - // Let's wait and fetch the result from the cache. - $lock->acquire(true); - $lock->release(); - return $this->getMapClassToExtendTypeArray(); - } - $lock->acquire(true); - try { - $this->buildMapClassToExtendTypeArray($lock); - } finally { - $lock->release(); - } - // This is a very short lived cache. Useful to avoid overloading a server in case of heavy load. - // Defaults to 2 seconds. - $this->cache->set($keyExtendClassCache, $this->mapClassToExtendTypeArray, $this->globTtl); - } - $this->fullMapClassToExtendTypeArrayComputed = true; - } - return $this->mapClassToExtendTypeArray; - } - - private function getMapNameToExtendType(RecursiveTypeMapperInterface $recursiveTypeMapper): array - { - if ($this->fullMapNameToExtendTypeArrayComputed === false) { - $namespace = str_replace('\\', '_', $this->namespace); - $keyExtendNameCache = 'globTypeMapperExtend_names_' . $namespace; - $this->mapNameToExtendType = $this->cache->get($keyExtendNameCache); - if ($this->mapNameToExtendType === null) { - $lock = $this->lockFactory->createLock('buildmapnameextend_' . $this->namespace, 5); - if (! $lock->acquire()) { - // Lock is being held right now. Generation is happening. - // Let's wait and fetch the result from the cache. - $lock->acquire(true); - $lock->release(); - return $this->getMapNameToExtendType($recursiveTypeMapper); - } - $lock->acquire(true); - try { - $this->buildMapNameToExtendTypeArray($recursiveTypeMapper); - } finally { - $lock->release(); - } - // This is a very short lived cache. Useful to avoid overloading a server in case of heavy load. - // Defaults to 2 seconds. - $this->cache->set($keyExtendNameCache, $this->mapNameToExtendType, $this->globTtl); - } - $this->fullMapNameToExtendTypeArrayComputed = true; - } - return $this->mapNameToExtendType; - } - - /** - * Returns the array of globbed classes. - * Only instantiable classes are returned. - * - * @return array Key: fully qualified class name - */ - private function getClassList(): array - { - if ($this->classes === null) { - $this->classes = []; - $classes = ClassCollector::getClasses(); - foreach ($classes as $className) { - if (! class_exists($className)) { - continue; - } - $refClass = new ReflectionClass($className); - if (! $refClass->isInstantiable()) { - continue; - } - $this->classes[$className] = $refClass; - } - } - return $this->classes; - } - - private function buildMap(): void - { - $this->mapClassToTypeArray = []; - $this->mapNameToType = []; - $this->mapClassToFactory = []; - $this->mapInputNameToFactory = []; - - /** @var ReflectionClass[] $classes */ - $classes = $this->getClassList(); - foreach ($classes as $className => $refClass) { - $type = $this->annotationReader->getTypeAnnotation($refClass); - - if ($type !== null) { - if (isset($this->mapClassToTypeArray[$type->getClass()])) { - throw DuplicateMappingException::createForType($type->getClass(), $this->mapClassToTypeArray[$type->getClass()], $className); - } - $this->storeTypeInCache($className, $type, $refClass->getFileName()); - } - - $isAbstract = $refClass->isAbstract(); - - foreach ($refClass->getMethods() as $method) { - if (! $method->isPublic() || ($isAbstract && ! $method->isStatic())) { - continue; - } - $factory = $this->annotationReader->getFactoryAnnotation($method); - if ($factory !== null) { - [$inputName, $className] = $this->inputTypeUtils->getInputTypeNameAndClassName($method); - - if (isset($this->mapClassToFactory[$className])) { - throw DuplicateMappingException::createForFactory($className, $this->mapClassToFactory[$className][0], $this->mapClassToFactory[$className][1], $refClass->getName(), $method->name); - } - $this->storeInputTypeInCache($method, $inputName, $className, $refClass->getFileName()); - } - } - } - } - - private function buildMapClassToExtendTypeArray(Lock $lock): void - { - $lock->acquire(true); - try { - $this->mapClassToExtendTypeArray = []; - $classes = $this->getClassList(); - foreach ($classes as $className => $refClass) { - $extendType = $this->annotationReader->getExtendTypeAnnotation($refClass); - - if ($extendType !== null) { - $this->storeExtendTypeMapperByClassInCache($className, $extendType, $refClass->getFileName()); - } - } - } finally { - $lock->release(); - } - } - - private function buildMapNameToExtendTypeArray(RecursiveTypeMapperInterface $recursiveTypeMapper): void - { - $this->mapNameToExtendType = []; - $classes = $this->getClassList(); - foreach ($classes as $className => $refClass) { - $extendType = $this->annotationReader->getExtendTypeAnnotation($refClass); - - if ($extendType !== null) { - $this->storeExtendTypeMapperByNameInCache($className, $extendType, $refClass->getFileName(), $recursiveTypeMapper); - } - } - } - - /** - * Stores in cache the mapping TypeClass <=> Object class <=> GraphQL type name. - */ - private function storeTypeInCache(string $typeClassName, Type $type, string $typeFileName): void - { - $objectClassName = $type->getClass(); - $this->mapClassToTypeArray[$objectClassName] = $typeClassName; - $this->cache->set('globTypeMapperByClass_' . str_replace('\\', '_', $this->namespace) . '_' . str_replace('\\', '_', $objectClassName), [ - 'filemtime' => filemtime($typeFileName), - 'fileName' => $typeFileName, - 'typeClass' => $typeClassName, - ], $this->mapTtl); - $typeName = $this->namingStrategy->getOutputTypeName($typeClassName, $type); - $this->mapNameToType[$typeName] = $typeClassName; - $this->cache->set('globTypeMapperByName_' . str_replace('\\', '_', $this->namespace) . '_' . $typeName, [ - 'filemtime' => filemtime($typeFileName), - 'fileName' => $typeFileName, - 'typeClass' => $typeClassName, - ], $this->mapTtl); - } - - /** - * Stores in cache the mapping between InputType name <=> Object class. - */ - private function storeInputTypeInCache(ReflectionMethod $refMethod, string $inputName, string $className, string $fileName): void - { - $refArray = [$refMethod->getDeclaringClass()->getName(), $refMethod->getName()]; - $this->mapClassToFactory[$className] = $refArray; - $this->cache->set('globInputTypeMapperByClass_' . str_replace('\\', '_', $this->namespace) . '_' . str_replace('\\', '_', $className), [ - 'filemtime' => filemtime($fileName), - 'fileName' => $fileName, - 'factory' => $refArray, - ], $this->mapTtl); - $this->mapInputNameToFactory[$inputName] = $refArray; - $this->cache->set('globInputTypeMapperByName_' . str_replace('\\', '_', $this->namespace) . '_' . $inputName, [ - 'filemtime' => filemtime($fileName), - 'fileName' => $fileName, - 'factory' => $refArray, - ], $this->mapTtl); - } - - /** - * Stores in cache the mapping ExtendTypeClass <=> Object class. - */ - private function storeExtendTypeMapperByClassInCache(string $extendTypeClassName, ExtendType $extendType, string $typeFileName): void - { - $objectClassName = $extendType->getClass(); - $this->mapClassToExtendTypeArray[$objectClassName][$extendTypeClassName] = $extendTypeClassName; - $this->cache->set('globExtendTypeMapperByClass_' . str_replace('\\', '_', $this->namespace) . '_' . str_replace('\\', '_', $objectClassName), [ - 'filemtime' => filemtime($typeFileName), - 'fileName' => $typeFileName, - 'extendTypeClasses' => $this->mapClassToExtendTypeArray[$objectClassName], - ], $this->mapTtl); - } - - /** - * Stores in cache the mapping ExtendTypeClass <=> name class. - */ - private function storeExtendTypeMapperByNameInCache(string $extendTypeClassName, ExtendType $extendType, string $typeFileName, RecursiveTypeMapperInterface $recursiveTypeMapper): void - { - $targetType = $recursiveTypeMapper->mapClassToType($extendType->getClass(), null); - $typeName = $targetType->name; - - $this->mapNameToExtendType[$typeName][$extendTypeClassName] = $extendTypeClassName; - $this->cache->set('globExtendTypeMapperByName_' . str_replace('\\', '_', $this->namespace) . '_' . $typeName, [ - 'filemtime' => filemtime($typeFileName), - 'fileName' => $typeFileName, - 'extendTypeClasses' => $this->mapNameToExtendType[$typeName], - ], $this->mapTtl); - } - - private function getTypeFromCacheByObjectClass(string $className): ?string - { - if (isset($this->mapClassToTypeArray[$className])) { - return $this->mapClassToTypeArray[$className]; - } - - // Let's try from the cache - $item = $this->cache->get('globTypeMapperByClass_' . str_replace('\\', '_', $this->namespace) . '_' . str_replace('\\', '_', $className)); - if ($item !== null) { - [ - 'filemtime' => $filemtime, - 'fileName' => $typeFileName, - 'typeClass' => $typeClassName - ] = $item; - - if ($filemtime === @filemtime($typeFileName)) { - $this->mapClassToTypeArray[$className] = $typeClassName; - return $typeClassName; - } - } - - // cache miss - return null; - } - - private function getTypeFromCacheByGraphQLTypeName(string $graphqlTypeName): ?string - { - if (isset($this->mapNameToType[$graphqlTypeName])) { - return $this->mapNameToType[$graphqlTypeName]; - } - - // Let's try from the cache - $item = $this->cache->get('globTypeMapperByName_' . str_replace('\\', '_', $this->namespace) . '_' . $graphqlTypeName); - if ($item !== null) { - [ - 'filemtime' => $filemtime, - 'fileName' => $typeFileName, - 'typeClass' => $typeClassName - ] = $item; - - if ($filemtime === @filemtime($typeFileName)) { - $this->mapNameToType[$graphqlTypeName] = $typeClassName; - return $typeClassName; - } - } - - // cache miss - return null; - } - - /** - * @return null|string[] A pointer to the factory [$className, $methodName] or null on cache miss - */ - private function getFactoryFromCacheByObjectClass(string $className): ?array - { - if (isset($this->mapClassToFactory[$className])) { - return $this->mapClassToFactory[$className]; - } - - // Let's try from the cache - $item = $this->cache->get('globInputTypeMapperByClass_' . str_replace('\\', '_', $this->namespace) . '_' . str_replace('\\', '_', $className)); - if ($item !== null) { - [ - 'filemtime' => $filemtime, - 'fileName' => $typeFileName, - 'factory' => $factory - ] = $item; - - if ($filemtime === @filemtime($typeFileName)) { - $this->mapClassToFactory[$className] = $factory; - return $factory; - } - } - - // cache miss - return null; - } - - /** - * @return null|array An array of classes with the ExtendType annotation (key and value = FQCN) - */ - private function getExtendTypesFromCacheByObjectClass(string $className): ?array - { - if (isset($this->mapClassToExtendTypeArray[$className])) { - return $this->mapClassToExtendTypeArray[$className]; - } - - // Let's try from the cache - $item = $this->cache->get('globExtendTypeMapperByClass_' . str_replace('\\', '_', $this->namespace) . '_' . str_replace('\\', '_', $className)); - if ($item !== null) { - [ - 'filemtime' => $filemtime, - 'fileName' => $typeFileName, - 'extendTypeClasses' => $extendTypeClassNames - ] = $item; - - if ($filemtime === @filemtime($typeFileName)) { - $this->mapClassToExtendTypeArray[$className] = $extendTypeClassNames; - return $extendTypeClassNames; - } - } - - // cache miss - return null; - } - - /** - * @return null|array An array of classes with the ExtendType annotation (key and value = FQCN) - */ - private function getExtendTypesFromCacheByGraphQLTypeName(string $graphqlTypeName): ?array - { - if (isset($this->mapNameToExtendType[$graphqlTypeName])) { - return $this->mapNameToExtendType[$graphqlTypeName]; - } - - // Let's try from the cache - $item = $this->cache->get('globExtendTypeMapperByName_' . str_replace('\\', '_', $this->namespace) . '_' . $graphqlTypeName); - if ($item !== null) { - [ - 'filemtime' => $filemtime, - 'fileName' => $typeFileName, - 'extendTypeClasses' => $extendTypeClassNames - ] = $item; - - if ($filemtime === @filemtime($typeFileName)) { - $this->mapNameToExtendType[$graphqlTypeName] = $extendTypeClassNames; - return $extendTypeClassNames; - } - } - - // cache miss - return null; - } - - /** - * @return null|string[] A pointer to the factory [$className, $methodName] or null on cache miss - */ - private function getFactoryFromCacheByGraphQLInputTypeName(string $graphqlTypeName): ?array - { - if (isset($this->mapInputNameToFactory[$graphqlTypeName])) { - return $this->mapInputNameToFactory[$graphqlTypeName]; - } - - // Let's try from the cache - $item = $this->cache->get('globInputTypeMapperByName_' . str_replace('\\', '_', $this->namespace) . '_' . $graphqlTypeName); - if ($item !== null) { - [ - 'filemtime' => $filemtime, - 'fileName' => $typeFileName, - 'factory' => $factory - ] = $item; - - if ($filemtime === @filemtime($typeFileName)) { - $this->mapInputNameToFactory[$graphqlTypeName] = $factory; - return $factory; - } - } - - // cache miss - return null; - } -}