Issues (167)

src/GraphQL.php (2 issues)

Labels
Severity
1
<?php
2
3
namespace Digia\GraphQL;
4
5
use Digia\GraphQL\Error\Handler\ErrorHandlerInterface;
6
use Digia\GraphQL\Error\InvariantException;
7
use Digia\GraphQL\Execution\ExecutionInterface;
8
use Digia\GraphQL\Execution\ExecutionProvider;
9
use Digia\GraphQL\Execution\ExecutionResult;
10
use Digia\GraphQL\Language\LanguageProvider;
11
use Digia\GraphQL\Language\Node\DocumentNode;
12
use Digia\GraphQL\Language\Node\NodeInterface;
13
use Digia\GraphQL\Language\Node\TypeNodeInterface;
14
use Digia\GraphQL\Language\Node\ValueNodeInterface;
15
use Digia\GraphQL\Language\NodePrinterInterface;
16
use Digia\GraphQL\Language\ParserInterface;
17
use Digia\GraphQL\Language\Source;
18
use Digia\GraphQL\Language\SyntaxErrorException;
19
use Digia\GraphQL\Schema\Building\SchemaBuilderInterface;
20
use Digia\GraphQL\Schema\Building\SchemaBuildingProvider;
21
use Digia\GraphQL\Schema\Extension\SchemaExtenderInterface;
22
use Digia\GraphQL\Schema\Extension\SchemaExtensionProvider;
23
use Digia\GraphQL\Schema\Resolver\ResolverRegistry;
24
use Digia\GraphQL\Schema\Resolver\ResolverRegistryInterface;
25
use Digia\GraphQL\Schema\Schema;
26
use Digia\GraphQL\Schema\Validation\SchemaValidationProvider;
27
use Digia\GraphQL\Schema\Validation\SchemaValidatorInterface;
28
use Digia\GraphQL\Type\CoercerProvider;
29
use Digia\GraphQL\Type\DirectivesProvider;
30
use Digia\GraphQL\Type\IntrospectionProvider;
31
use Digia\GraphQL\Type\ScalarTypesProvider;
32
use Digia\GraphQL\Validation\RulesProvider;
33
use Digia\GraphQL\Validation\ValidationProvider;
34
use Digia\GraphQL\Validation\ValidatorInterface;
35
use League\Container\Container;
36
use React\Promise\PromiseInterface;
37
use function React\Promise\resolve;
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
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
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 PromiseInterface
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
    ): PromiseInterface {
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 mixed                      $rootValue
276
     * @param mixed                      $contextValue
277
     * @param array                      $variableValues
278
     * @param null|string                $operationName
279
     * @param callable|null              $fieldResolver
280
     * @param ErrorHandlerInterface|null $errorHandler
281
     * @return PromiseInterface
282
     * @throws InvariantException
283
     */
284
    public static function process(
285
        Schema $schema,
286
        string $source,
287
        $rootValue = null,
288
        $contextValue = null,
289
        array $variableValues = [],
290
        ?string $operationName = null,
291
        ?callable $fieldResolver = null,
292
        ?ErrorHandlerInterface $errorHandler = null
293
    ): PromiseInterface {
294
        $schemaValidationErrors = validateSchema($schema);
295
296
        if (!empty($schemaValidationErrors)) {
297
            if (null !== $errorHandler) {
298
                foreach ($schemaValidationErrors as $schemaValidationError) {
299
                    $errorHandler->handleError($schemaValidationError);
300
                }
301
            }
302
303
            return resolve(new ExecutionResult(null, $schemaValidationErrors));
304
        }
305
306
        try {
307
            $document = parse($source);
308
        } catch (SyntaxErrorException $error) {
309
            if (null !== $errorHandler) {
310
                $errorHandler->handleError($error);
311
            }
312
313
            return resolve(new ExecutionResult(null, [$error]));
314
        }
315
316
        $validationErrors = validate($schema, $document);
317
318
        if (!empty($validationErrors)) {
319
            if (null !== $errorHandler) {
320
                foreach ($validationErrors as $validationError) {
321
                    $errorHandler->handleError($validationError);
322
                }
323
            }
324
325
            return resolve(new ExecutionResult(null, $validationErrors));
326
        }
327
328
        return executeAsync(
329
            $schema,
330
            $document,
331
            $rootValue,
332
            $contextValue,
333
            $variableValues,
334
            $operationName,
335
            $fieldResolver,
336
            $errorHandler
337
        );
338
    }
339
340
    /**
341
     * @param NodeInterface $node
342
     * @return string
343
     */
344
    public static function print(NodeInterface $node): string
345
    {
346
        /** @var NodePrinterInterface $nodePrinter */
347
        $nodePrinter = static::make(NodePrinterInterface::class);
348
349
        return $nodePrinter->print($node);
350
    }
351
352
    /**
353
     * @return Container
354
     */
355
    public function getContainer(): Container
356
    {
357
        return $this->container;
358
    }
359
360
    /**
361
     * Registers the service provides with the container.
362
     *
363
     * @param Container $container
364
     */
365
    protected function registerProviders(Container $container): void
366
    {
367
        foreach (self::$providers as $className) {
368
            $container->addServiceProvider($className);
369
        }
370
    }
371
}
372