6 declare(strict_types=1);
28 private $fileResolver;
43 private $defaultScope;
53 TypeReaderComposite $typeReader,
54 $fileName = self::GRAPHQL_SCHEMA_FILE,
55 $defaultScope =
'global' 57 $this->fileResolver = $fileResolver;
58 $this->typeReader = $typeReader;
59 $this->defaultScope = $defaultScope;
60 $this->fileName = $fileName;
66 public function read($scope =
null) : array
69 $scope = $scope ?: $this->defaultScope;
70 $schemaFiles = $this->fileResolver->get($this->fileName, $scope);
71 if (!count($schemaFiles)) {
79 foreach ($schemaFiles as $partialSchemaContent) {
80 $partialSchemaTypes = $this->parseTypes($partialSchemaContent);
83 $knownTypes = $partialSchemaTypes + $knownTypes;
84 $schemaContent = implode(
"\n", $knownTypes);
86 $partialResults = $this->readPartialTypes($schemaContent);
101 private function readPartialTypes(
string $graphQlSchemaContent) : array
103 $partialResults = [];
105 $graphQlSchemaContent = $this->addPlaceHolderInSchema($graphQlSchemaContent);
107 $schema = \GraphQL\Utils\BuildSchema::build($graphQlSchemaContent);
109 foreach (
$schema->getTypeMap() as $typeName => $typeMeta) {
111 if ((strpos($typeName,
'__') !== 0 && (!$typeMeta instanceof \GraphQL\Type\Definition\ScalarType))) {
112 $type = $this->typeReader->read($typeMeta);
114 $partialResults[$typeName] =
$type;
116 throw new \LogicException(
"'{$typeName}' cannot be processed.");
121 $partialResults = $this->removePlaceholderFromResults($partialResults);
123 return $partialResults;
132 private function parseTypes(
string $graphQlSchemaContent) : array
134 $typeKindsPattern =
'(type|interface|union|enum|input)';
135 $typeNamePattern =
'([_A-Za-z][_0-9A-Za-z]+)';
136 $typeDefinitionPattern =
'([^\{]*)(\{[^\}]*\})';
137 $spacePattern =
'[\s\t\n\r]+';
140 "/{$typeKindsPattern}{$spacePattern}{$typeNamePattern}{$spacePattern}{$typeDefinitionPattern}/i",
141 $graphQlSchemaContent,
147 if (!empty($matches)) {
148 foreach ($matches[0] as $matchKey => $matchValue) {
149 $matches[0][$matchKey] = $this->convertInterfacesToAnnotations($matchValue);
156 $parsedTypes = array_combine($matches[2], $matches[0]);
167 private function copyInterfaceFieldsToConcreteTypes(array
$source): array
169 foreach (
$source as $interface) {
170 if ($interface[
'type'] ==
'graphql_interface') {
172 if (isset(
$type[
'implements'])
173 && isset(
$type[
'implements'][$interface[
'name']])
174 && isset(
$type[
'implements'][$interface[
'name']][
'copyFields'])
175 &&
$type[
'implements'][$interface[
'name']][
'copyFields'] ===
true 178 ? array_replace($interface[
'fields'],
$type[
'fields']) : $interface[
'fields'];
193 private function convertInterfacesToAnnotations(
string $graphQlSchemaContent): string
195 $implementsKindsPattern =
'implements';
196 $typeNamePattern =
'([_A-Za-z][_0-9A-Za-z]+)';
197 $spacePattern =
'([\s\t\n\r]+)';
198 $spacePatternNotMandatory =
'[\s\t\n\r]*';
200 "/{$spacePattern}{$implementsKindsPattern}{$spacePattern}{$typeNamePattern}" 201 .
"(,{$spacePatternNotMandatory}$typeNamePattern)*/im",
202 $graphQlSchemaContent,
203 $allMatchesForImplements
206 if (!empty($allMatchesForImplements)) {
207 foreach (array_unique($allMatchesForImplements[0]) as $implementsString) {
208 $implementsStatementString = preg_replace(
209 "/{$spacePattern}{$implementsKindsPattern}{$spacePattern}/m",
214 "/{$typeNamePattern}+/im",
215 $implementsStatementString,
216 $implementationsMatches
219 if (!empty($implementationsMatches)) {
220 $annotationString =
' @implements(interfaces: [';
221 foreach ($implementationsMatches[0] as $interfaceName) {
222 $annotationString.=
"\"{$interfaceName}\", ";
224 $annotationString = rtrim($annotationString,
', ');
225 $annotationString .=
']) ';
226 $graphQlSchemaContent = str_replace($implementsString, $annotationString, $graphQlSchemaContent);
231 return $graphQlSchemaContent;
242 private function addPlaceHolderInSchema(
string $graphQlSchemaContent) :string
245 $typesKindsPattern =
'(type|interface|input)';
246 $enumKindsPattern =
'(enum)';
247 $typeNamePattern =
'([_A-Za-z][_0-9A-Za-z]+)';
248 $typeDefinitionPattern =
'([^\{]*)(\{[\s\t\n\r^\}]*\})';
249 $spacePattern =
'([\s\t\n\r]+)';
252 $graphQlSchemaContent = preg_replace(
253 "/{$typesKindsPattern}{$spacePattern}{$typeNamePattern}{$spacePattern}{$typeDefinitionPattern}/im",
254 "\$1\$2\$3\$4\$5{\n{$placeholderField}: String\n}",
255 $graphQlSchemaContent
259 $graphQlSchemaContent = preg_replace(
260 "/{$enumKindsPattern}{$spacePattern}{$typeNamePattern}{$spacePattern}{$typeDefinitionPattern}/im",
261 "\$1\$2\$3\$4\$5{\n{$placeholderField}\n}",
262 $graphQlSchemaContent
264 return $graphQlSchemaContent;
273 private function removePlaceholderFromResults(array $partialResults) : array
277 foreach ($partialResults as $typeKeyName => $partialResultTypeArray) {
278 if (isset($partialResultTypeArray[
'fields'][$placeholderField])) {
280 unset($partialResults[$typeKeyName][
'fields'][$placeholderField]);
281 }
elseif (isset($partialResultTypeArray[
'items'][$placeholderField])) {
283 unset($partialResults[$typeKeyName][
'items'][$placeholderField]);
286 return $partialResults;
__construct(FileResolverInterface $fileResolver, TypeReaderComposite $typeReader, $fileName=self::GRAPHQL_SCHEMA_FILE, $defaultScope='global')
elseif(isset( $params[ 'redirect_parent']))
const GRAPHQL_PLACEHOLDER_FIELD_NAME
const GRAPHQL_SCHEMA_FILE