Passed
Push — master ( 625be7...d965a1 )
by Bruno
08:58
created

Parser::processAst()   B

Complexity

Conditions 6
Paths 1

Size

Total Lines 58
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 25
c 0
b 0
f 0
dl 0
loc 58
rs 8.8977
ccs 5
cts 5
cp 1
cc 6
nc 1
nop 0
crap 6

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php declare(strict_types=1);
2
3
namespace Modelarium;
4
5
use GraphQL\Language\AST\DocumentNode;
6
use GraphQL\Language\AST\Node;
7
use GraphQL\Language\AST\NodeKind;
8
use GraphQL\Language\AST\ScalarTypeDefinitionNode;
9
use GraphQL\Language\Visitor;
10
use GraphQL\Type\Definition\ObjectType;
11
use GraphQL\Utils\AST;
12
use GraphQL\Type\Definition\Type;
13
use GraphQL\Type\Schema;
14
use GraphQL\Utils\SchemaExtender;
15
use Modelarium\Exception\ScalarNotFoundException;
16
17
class Parser
18
{
19
    /**
20
     * @var \GraphQL\Language\AST\DocumentNode
21
     */
22
    protected $ast;
23
24 22
    /**
25
     * @var \GraphQL\Type\Schema
26
     */
27
    protected $schema;
28
29
    protected $scalars = [];
30
31
    protected function __construct()
32
    {
33
        // empty
34
    }
35
36
    public static function extendDatatypes(array $typeConfig, $typeDefinitionNode): array
0 ignored issues
show
Unused Code introduced by
The parameter $typeDefinitionNode is not used and could be removed. ( Ignorable by Annotation )

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

36
    public static function extendDatatypes(array $typeConfig, /** @scrutinizer ignore-unused */ $typeDefinitionNode): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
37
    {
38
        /* TODO: extended datatypes
39
        if ($typeConfig['name'] === 'Email') {
40 22
            $typeConfig = array_merge($typeConfig, [
41
                'serialize' => function ($value) {
42
                    // ...
43 23
                },
44
                'parseValue' => function ($value) {
45
                    // ...
46 23
                },
47
                'parseLiteral' => function ($ast) {
48
                    // ...
49
                }
50
            ]);
51
        } */
52
        return $typeConfig;
53
    }
54
55
    /**
56
     *
57
     * @param array $files
58
     * @return self
59
     * @throws \Safe\Exceptions\FilesystemException
60
     */
61
    public static function fromFiles(array $files): self
62
    {
63
        $p = new self();
0 ignored issues
show
Unused Code introduced by
The assignment to $p is dead and can be removed.
Loading history...
64
        $sources = array_map('\Safe\file_get_contents', $files);
65
        return static::fromStrings($sources);
66
    }
67
68
69 17
    /**
70
     * Returns a Parser from a file path
71 17
     *
72 17
     * @param string $path The file path
73
     * @return Parser
74
     * @throws \Safe\Exceptions\FilesystemException If file is not found or parsing fails.
75
     */
76
    public static function fromFile(string $path): self
77
    {
78
        $data = \Safe\file_get_contents($path);
79
        return self::fromString($data);
80
    }
81
82 22
    /**
83
     * Returns a Parser from a string
84 22
     *
85 22
     * @param string $data the string
86 22
     * @return Parser
87 22
     */
88 22
    public static function fromString(string $data): self
89
    {
90 22
        $p = new self();
91 22
        $p->ast = \GraphQL\Language\Parser::parse($data);
92
        $p->processAst();
93
        $schemaBuilder = new \GraphQL\Utils\BuildSchema(
94
            $p->ast,
95
            [__CLASS__, 'extendDatatypes']
96
        );
97
        
98
        $p->schema = $schemaBuilder->buildSchema();
99 1
        $p->processSchema();
100
        return $p;
101 1
    }
102 1
103 1
    protected function processSchema(): void
104 1
    {
105
        $originalTypeLoader = $this->schema->getConfig()->typeLoader;
106
107 1
        $this->schema->getConfig()->typeLoader = function ($typeName) use ($originalTypeLoader) {
108 1
            $type = $originalTypeLoader($typeName);
109 1
            if ($type instanceof \GraphQL\Type\Definition\CustomScalarType) {
110
                $scalarName = $type->name;
111 1
                $className = $this->scalars[$scalarName];
112 1
                return new $className($type->config);
113
            }
114
            return $type;
115 1
        };
116 1
    }
117 1
118
    protected function processAst(): void
119
    {
120
        $this->ast = Visitor::visit($this->ast, [
121
            // load the scalar type classes
122
            NodeKind::SCALAR_TYPE_DEFINITION => function ($node) {
123
                $scalarName = $node->name->value;
124
125 1
                // load classes
126
                /**
127
                 * @var $astNode ScalarTypeDefinitionNode
128
                 */
129 21
130
                $className = null;
131 21
                foreach ($node->directives as $directive) {
132
                    switch ($directive->name->value) {
133
                    case 'scalar':
134 4
                        foreach ($directive->arguments as $arg) {
135
                            /**
136 4
                             * @var \GraphQL\Language\AST\ArgumentNode $arg
137
                             */
138
        
139
                            $value = $arg->value->value;
0 ignored issues
show
Bug introduced by
Accessing value on the interface GraphQL\Language\AST\ValueNode suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
140
        
141
                            switch ($arg->name->value) {
142
                            case 'class':
143
                                $className = $value;
144
                            break;
145
                            }
146
                        }
147
                    break;
148
                    }
149
                }
150
151
                // Require special handler class for custom scalars:
152
                if (!class_exists($className, true)) {
153
                    throw new \Modelarium\Exception\Exception(
154
                        "Custom scalar must have corresponding handler class $className"
155
                    );
156
                }
157
158
                $this->scalars[$scalarName] = $className;
159
160
                // return
161
                //   null: no action
162
                //   Visitor::skipNode(): skip visiting this node
163
                //   Visitor::stop(): stop visiting altogether
164
                //   Visitor::removeNode(): delete this node
165
                //   any value: replace this node with the returned value
166
                return null;
167
            },
168
            NodeKind::NAMED_TYPE  => function ($node) {
0 ignored issues
show
Unused Code introduced by
The parameter $node is not used and could be removed. ( Ignorable by Annotation )

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

168
            NodeKind::NAMED_TYPE  => function (/** @scrutinizer ignore-unused */ $node) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
169
                return null;
170
            },
171
            NodeKind::OBJECT_TYPE_DEFINITION  => function ($node) {
0 ignored issues
show
Unused Code introduced by
The parameter $node is not used and could be removed. ( Ignorable by Annotation )

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

171
            NodeKind::OBJECT_TYPE_DEFINITION  => function (/** @scrutinizer ignore-unused */ $node) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
172
                return null;
173
            },
174
            'enter' => function ($node) {
0 ignored issues
show
Unused Code introduced by
The parameter $node is not used and could be removed. ( Ignorable by Annotation )

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

174
            'enter' => function (/** @scrutinizer ignore-unused */ $node) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
175
                return null;
176
            }
177
        ]);
178
    }
179
180
    /**
181
     *
182
     * @param string[] $sources
183
     * @return self
184
     */
185
    public static function fromStrings(array $sources): self
186
    {
187
        $p = new self();
188
        $schema = new Schema([
189
            'query' => new ObjectType(['name' => 'Query']),
190
            'mutation' => new ObjectType(['name' => 'Mutation']),
191
        ]);
192
193
        foreach ($sources as &$s) {
194
            $s = \Safe\preg_replace('/^type Mutation/m', 'extend type Mutation', $s);
195
            $s = \Safe\preg_replace('/^type Query/m', 'extend type Query', $s);
196
        }
197
        $extensionSource = implode("\n", $sources);
198
        $p->ast = \GraphQL\Language\Parser::parse($extensionSource);
199
200
        // TODO: extendDatatypes
201
        $p->schema = SchemaExtender::extend(
202
            $schema,
203
            $p->ast
204
        );
205
        // $schemaBuilder = new \GraphQL\Utils\BuildSchema(
206
        //     $p->ast,
207
        //     [__CLASS__, 'extendDatatypes']
208
        // );
209
210
        // $p->schema = $schemaBuilder->buildSchema();
211
        $p->processAst();
212
        return $p;
213
    }
214
215
216
    public function getSchema(): Schema
217
    {
218
        return $this->schema;
219
    }
220
221
    public function getType(string $name) : ?Type
222
    {
223
        return $this->schema->getType($name);
224
    }
225
226
    /**
227
     * Factory.
228
     *
229
     * @param string $datatype
230
     * @return ScalarType
231
     */
232
    public function getScalarType(string $datatype): ?ScalarType
233
    {
234
        $className = $this->scalars[$datatype] ?? null;
235
        if (!$className) {
236
            return null;
237
        }
238
        if (!class_exists($className)) {
239
            throw new ScalarNotFoundException("Class not found for $datatype");
240
        }
241
        return new $className();
242
    }
243
}
244