Passed
Pull Request — master (#318)
by Christoffer
02:28
created

GraphQL::buildSchema()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 1
nop 3
dl 0
loc 11
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Digia\GraphQL;
4
5
use Digia\GraphQL\Error\Handler\CallableMiddleware;
6
use Digia\GraphQL\Error\Handler\ErrorHandler;
7
use Digia\GraphQL\Error\Handler\ErrorHandlerInterface;
8
use Digia\GraphQL\Error\InvariantException;
9
use Digia\GraphQL\Execution\ExecutionInterface;
10
use Digia\GraphQL\Execution\ExecutionProvider;
11
use Digia\GraphQL\Execution\ExecutionResult;
12
use Digia\GraphQL\Language\LanguageProvider;
13
use Digia\GraphQL\Language\Node\DocumentNode;
14
use Digia\GraphQL\Language\Node\NodeInterface;
15
use Digia\GraphQL\Language\Node\TypeNodeInterface;
16
use Digia\GraphQL\Language\Node\ValueNodeInterface;
17
use Digia\GraphQL\Language\NodePrinterInterface;
18
use Digia\GraphQL\Language\ParserInterface;
19
use Digia\GraphQL\Language\Source;
20
use Digia\GraphQL\Language\SyntaxErrorException;
21
use Digia\GraphQL\Schema\Building\SchemaBuilderInterface;
22
use Digia\GraphQL\Schema\Building\SchemaBuildingProvider;
23
use Digia\GraphQL\Schema\Extension\SchemaExtenderInterface;
24
use Digia\GraphQL\Schema\Extension\SchemaExtensionProvider;
25
use Digia\GraphQL\Schema\Resolver\ResolverRegistry;
26
use Digia\GraphQL\Schema\Resolver\ResolverRegistryInterface;
27
use Digia\GraphQL\Schema\Schema;
28
use Digia\GraphQL\Schema\Validation\SchemaValidationProvider;
29
use Digia\GraphQL\Schema\Validation\SchemaValidatorInterface;
30
use Digia\GraphQL\Type\CoercerProvider;
31
use Digia\GraphQL\Type\DirectivesProvider;
32
use Digia\GraphQL\Type\IntrospectionProvider;
33
use Digia\GraphQL\Type\ScalarTypesProvider;
34
use Digia\GraphQL\Validation\RulesProvider;
35
use Digia\GraphQL\Validation\ValidationProvider;
36
use Digia\GraphQL\Validation\ValidatorInterface;
37
use League\Container\Container;
38
39
class GraphQL
40
{
41
    public const BOOLEAN = 'GraphQLBoolean';
42
    public const FLOAT   = 'GraphQLFloat';
43
    public const INT     = 'GraphQLInt';
44
    public const ID      = 'GraphQLID';
45
    public const STRING  = 'GraphQLString';
46
47
    public const DEPRECATED_DIRECTIVE = 'GraphQLDeprecatedDirective';
48
    public const INCLUDE_DIRECTIVE    = 'GraphQLIncludeDirective';
49
    public const SKIP_DIRECTIVE       = 'GraphQLSkipDirective';
50
51
    public const SCHEMA_INTROSPECTION             = '__Schema';
52
    public const DIRECTIVE_INTROSPECTION          = '__Directive';
53
    public const DIRECTIVE_LOCATION_INTROSPECTION = '__DirectiveLocation';
54
    public const TYPE_INTROSPECTION               = '__Type';
55
    public const FIELD_INTROSPECTION              = '__Field';
56
    public const INPUT_VALUE_INTROSPECTION        = '__InputValue';
57
    public const ENUM_VALUE_INTROSPECTION         = '__EnumValue';
58
    public const TYPE_KIND_INTROSPECTION          = '__TypeKind';
59
60
    public const SCHEMA_META_FIELD_DEFINITION    = 'SchemaMetaFieldDefinition';
61
    public const TYPE_META_FIELD_DEFINITION      = 'TypeMetaFieldDefinition';
62
    public const TYPE_NAME_META_FIELD_DEFINITION = 'TypeNameMetaFieldDefinition';
63
64
    /**
65
     * @var array
66
     */
67
    private static $providers = [
68
        LanguageProvider::class,
69
        SchemaBuildingProvider::class,
70
        SchemaExtensionProvider::class,
71
        SchemaValidationProvider::class,
72
        CoercerProvider::class,
73
        IntrospectionProvider::class,
74
        ScalarTypesProvider::class,
75
        DirectivesProvider::class,
76
        RulesProvider::class,
77
        ValidationProvider::class,
78
        ExecutionProvider::class,
79
    ];
80
81
    /**
82
     * @var GraphQL
83
     */
84
    private static $instance;
85
86
    /**
87
     * @var Container
88
     */
89
    protected $container;
90
91
    /**
92
     * GraphQL constructor.
93
     */
94
    private function __construct()
95
    {
96
        $container = new Container();
97
98
        $this->registerProviders($container);
99
100
        $this->container = $container;
101
    }
102
103
    /**
104
     * @return GraphQL
105
     */
106
    public static function getInstance(): self
107
    {
108
        if (null === self::$instance) {
109
            self::$instance = new self();
110
        }
111
112
        return self::$instance;
113
    }
114
115
    /**
116
     * @param string $id
117
     * @return mixed
118
     */
119
    public static function make(string $id)
120
    {
121
        return static::getInstance()
122
            ->getContainer()
123
            ->get($id);
124
    }
125
126
    /**
127
     * @param Source                          $source
128
     * @param array|ResolverRegistryInterface $resolverRegistry
129
     * @param array                           $options
130
     * @return Schema
131
     */
132
    public static function buildSchema(Source $source, $resolverRegistry, array $options = []): Schema
133
    {
134
        /** @var SchemaBuilderInterface $schemaBuilder */
135
        $schemaBuilder = static::make(SchemaBuilderInterface::class);
136
137
        return $schemaBuilder->build(
138
            static::parse($source, $options),
139
            $resolverRegistry instanceof ResolverRegistryInterface
140
                ? $resolverRegistry
141
                : new ResolverRegistry($resolverRegistry),
142
            $options
143
        );
144
    }
145
146
    /**
147
     * @param Schema                          $schema
148
     * @param Source                          $source
149
     * @param array|ResolverRegistryInterface $resolverRegistry
150
     * @param array                           $options
151
     * @return Schema
152
     */
153
    public static function extendSchema(
154
        Schema $schema,
155
        Source $source,
156
        $resolverRegistry,
157
        array $options = []
158
    ): Schema {
159
        /** @var SchemaExtenderInterface $schemaExtender */
160
        $schemaExtender = static::make(SchemaExtenderInterface::class);
161
162
        return $schemaExtender->extend(
163
            $schema,
164
            static::parse($source, $options),
165
            $resolverRegistry instanceof ResolverRegistryInterface
166
                ? $resolverRegistry
167
                : new ResolverRegistry($resolverRegistry),
168
            $options
169
        );
170
    }
171
172
    /**
173
     * @param Schema $schema
174
     * @return array
175
     */
176
    public static function validateSchema(Schema $schema): array
177
    {
178
        /** @var SchemaValidatorInterface $schemaValidator */
179
        $schemaValidator = static::make(SchemaValidatorInterface::class);
180
181
        return $schemaValidator->validate($schema);
182
    }
183
184
    /**
185
     * @param Source $source
186
     * @param array  $options
187
     * @return DocumentNode
188
     */
189
    public static function parse(Source $source, array $options = []): DocumentNode
190
    {
191
        /** @var ParserInterface $parser */
192
        $parser = static::make(ParserInterface::class);
193
194
        return $parser->parse($source, $options);
195
    }
196
197
    /**
198
     * @param Source $source
199
     * @param array  $options
200
     * @return ValueNodeInterface
201
     */
202
    public static function parseValue(Source $source, array $options = []): ValueNodeInterface
203
    {
204
        /** @var ParserInterface $parser */
205
        $parser = static::make(ParserInterface::class);
206
207
        return $parser->parseValue($source, $options);
0 ignored issues
show
Bug introduced by
The method parseValue() does not exist on Digia\GraphQL\Language\ParserInterface. Did you maybe mean parse()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

207
        return $parser->/** @scrutinizer ignore-call */ parseValue($source, $options);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
208
    }
209
210
    /**
211
     * @param Source $source
212
     * @param array  $options
213
     * @return TypeNodeInterface
214
     */
215
    public static function parseType(Source $source, array $options = []): TypeNodeInterface
216
    {
217
        /** @var ParserInterface $parser */
218
        $parser = static::make(ParserInterface::class);
219
220
        return $parser->parseType($source, $options);
0 ignored issues
show
Bug introduced by
The method parseType() does not exist on Digia\GraphQL\Language\ParserInterface. Did you maybe mean parse()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

220
        return $parser->/** @scrutinizer ignore-call */ parseType($source, $options);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
221
    }
222
223
    /**
224
     * @param Schema       $schema
225
     * @param DocumentNode $document
226
     * @return array
227
     */
228
    public static function validate(Schema $schema, DocumentNode $document): array
229
    {
230
        /** @var ValidatorInterface $validator */
231
        $validator = static::make(ValidatorInterface::class);
232
233
        return $validator->validate($schema, $document);
234
    }
235
236
    /**
237
     * @param Schema                     $schema
238
     * @param DocumentNode               $document
239
     * @param mixed                      $rootValue
240
     * @param mixed                      $contextValue
241
     * @param array                      $variableValues
242
     * @param string|null                $operationName
243
     * @param callable|null              $fieldResolver
244
     * @param ErrorHandlerInterface|null $errorHandler
245
     * @return ExecutionResult
246
     */
247
    public static function execute(
248
        Schema $schema,
249
        DocumentNode $document,
250
        $rootValue = null,
251
        $contextValue = null,
252
        array $variableValues = [],
253
        $operationName = null,
254
        callable $fieldResolver = null,
255
        ?ErrorHandlerInterface $errorHandler = null
256
    ): ExecutionResult {
257
        /** @var ExecutionInterface $execution */
258
        $execution = static::make(ExecutionInterface::class);
259
260
        return $execution->execute(
261
            $schema,
262
            $document,
263
            $rootValue,
264
            $contextValue,
265
            $variableValues,
266
            $operationName,
267
            $fieldResolver,
268
            $errorHandler
269
        );
270
    }
271
272
    /**
273
     * @param Schema                     $schema
274
     * @param string                     $source
275
     * @param null                       $rootValue
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $rootValue is correct as it would always require null to be passed?
Loading history...
276
     * @param null                       $contextValue
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $contextValue is correct as it would always require null to be passed?
Loading history...
277
     * @param array                      $variableValues
278
     * @param null|string                $operationName
279
     * @param callable|null              $fieldResolver
280
     * @param ErrorHandlerInterface|null $errorHandler
281
     * @return ExecutionResult
282
     * @throws InvariantException
283
     * @throws SyntaxErrorException
284
     */
285
    public static function process(
286
        Schema $schema,
287
        string $source,
288
        $rootValue = null,
289
        $contextValue = null,
290
        array $variableValues = [],
291
        ?string $operationName = null,
292
        ?callable $fieldResolver = null,
293
        ?ErrorHandlerInterface $errorHandler = null
294
    ): ExecutionResult
295
    {
296
        $schemaValidationErrors = validateSchema($schema);
297
298
        if (!empty($schemaValidationErrors)) {
299
            if (null !== $errorHandler) {
300
                foreach ($schemaValidationErrors as $schemaValidationError) {
301
                    $errorHandler->handleError($schemaValidationError);
302
                }
303
            }
304
305
            return (new ExecutionResult(null, $schemaValidationErrors));
306
        }
307
308
        try {
309
            $document = parse($source);
310
        } catch (SyntaxErrorException $error) {
311
            if (null !== $errorHandler) {
312
                $errorHandler->handleError($error);
313
            }
314
315
            return (new ExecutionResult(null, [$error]));
316
        }
317
318
        $validationErrors = validate($schema, $document);
319
320
        if (!empty($validationErrors)) {
321
            if (null !== $errorHandler) {
322
                foreach ($validationErrors as $validationError) {
323
                    $errorHandler->handleError($validationError);
324
                }
325
            }
326
327
            return (new ExecutionResult(null, $validationErrors));
328
        }
329
330
        $result = execute(
331
            $schema,
332
            $document,
333
            $rootValue,
334
            $contextValue,
335
            $variableValues,
336
            $operationName,
337
            $fieldResolver,
338
            $errorHandler
339
        );
340
341
        return $result;
342
    }
343
344
    /**
345
     * @param NodeInterface $node
346
     * @return string
347
     */
348
    public static function print(NodeInterface $node): string
349
    {
350
        /** @var NodePrinterInterface $nodePrinter */
351
        $nodePrinter = static::make(NodePrinterInterface::class);
352
353
        return $nodePrinter->print($node);
354
    }
355
356
    /**
357
     * @return Container
358
     */
359
    public function getContainer(): Container
360
    {
361
        return $this->container;
362
    }
363
364
    /**
365
     * Registers the service provides with the container.
366
     *
367
     * @param Container $container
368
     */
369
    protected function registerProviders(Container $container): void
370
    {
371
        foreach (self::$providers as $className) {
372
            $container->addServiceProvider($className);
373
        }
374
    }
375
}
376