Passed
Pull Request — master (#25)
by Quang
02:00
created

ExecutionStrategy::executeFields()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 22
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 9
nc 2
nop 4
dl 0
loc 22
rs 9.2
c 0
b 0
f 0
1
<?php
2
3
namespace Digia\GraphQL\Execution;
4
5
use Digia\GraphQL\Error\GraphQLError;
6
use Digia\GraphQL\Language\AST\Node\ArgumentNode;
7
use Digia\GraphQL\Language\AST\Node\FieldNode;
8
use Digia\GraphQL\Language\AST\Node\FragmentDefinitionNode;
9
use Digia\GraphQL\Language\AST\Node\InputValueDefinitionNode;
10
use Digia\GraphQL\Language\AST\Node\OperationDefinitionNode;
11
use Digia\GraphQL\Language\AST\Node\SelectionSetNode;
12
use Digia\GraphQL\Language\AST\NodeKindEnum;
13
use Digia\GraphQL\Type\Definition\ObjectType;
14
15
/**
16
 * Class AbstractStrategy
17
 * @package Digia\GraphQL\Execution\Strategies
18
 */
19
abstract class ExecutionStrategy
20
{
21
    /**
22
     * @var ExecutionContext
23
     */
24
    protected $context;
25
26
    /**
27
     * @var OperationDefinitionNode
28
     */
29
    protected $operation;
30
31
    /**
32
     * @var mixed
33
     */
34
    protected $rootValue;
35
36
37
    /**
38
     * @var array
39
     */
40
    protected $finalResult;
41
42
    /**
43
     * AbstractStrategy constructor.
44
     * @param ExecutionContext $context
45
     *
46
     * @param OperationDefinitionNode $operation
47
     */
48
    public function __construct(
49
        ExecutionContext $context,
50
        OperationDefinitionNode $operation,
51
        $rootValue)
52
    {
53
        $this->context   = $context;
54
        $this->operation = $operation;
55
        $this->rootValue = $rootValue;
56
    }
57
58
    /**
59
     * @return array|null
60
     */
61
    abstract function execute(): ?array;
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
62
63
    /**
64
     * @param ObjectType $runtimeType
65
     * @param SelectionSetNode $selectionSet
66
     * @param $fields
67
     * @param $visitedFragmentNames
68
     * @return \ArrayObject
69
     */
70
    protected function collectFields(
71
        ObjectType $runtimeType,
72
        SelectionSetNode $selectionSet,
73
        $fields,
74
        $visitedFragmentNames
75
    ) {
76
        foreach ($selectionSet->getSelections() as $selection) {
77
            /** @var FieldNode $selection */
78
            switch ($selection->getKind()) {
79
                case NodeKindEnum::FIELD:
80
                    $name = $this->getFieldNameKey($selection);
81
                    if(!isset($runtimeType->getFields()[$selection->getNameValue()])){
82
                        continue;
83
                    }
84
                    if (!isset($fields[$name])) {
85
                        $fields[$name] = new \ArrayObject();
86
                    }
87
                    $fields[$name][] = $selection;
88
                    break;
89
                case NodeKindEnum::INLINE_FRAGMENT:
90
                    //TODO check if should include this node
91
                    $this->collectFields(
92
                        $runtimeType,
93
                        $selection->getSelectionSet(),
94
                        $fields,
95
                        $visitedFragmentNames
96
                    );
97
                    break;
98
                case NodeKindEnum::FRAGMENT_SPREAD:
99
                    //TODO check if should include this node
100
                    if (!empty($visitedFragmentNames[$selection->getNameValue()])) {
101
                        continue;
102
                    }
103
                    $visitedFragmentNames[$selection->getNameValue()] = true;
104
                    /** @var FragmentDefinitionNode $fragment */
105
                    $fragment = $this->context->getFragments()[$selection->getNameValue()];
106
                    $this->collectFields(
107
                        $runtimeType,
108
                        $fragment->getSelectionSet(),
109
                        $fields,
110
                        $visitedFragmentNames
111
                    );
112
                    break;
113
            }
114
        }
115
116
        return $fields;
117
    }
118
119
    /**
120
     * @param FieldNode $node
121
     * @return string
122
     */
123
    private function getFieldNameKey(FieldNode $node)
124
    {
125
        return $node->getAlias()
126
            ? $node->getAlias()->getValue()
127
            : $node->getNameValue();
128
    }
129
130
    /**
131
     * Implements the "Evaluating selection sets" section of the spec
132
     * for "read" mode.
133
     * @param ObjectType $parentType
134
     * @param $source
135
     * @param $path
136
     * @param $fields
137
     *
138
     * @return array
139
     *
140
     * @throws GraphQLError|\Exception
141
     */
142
    protected function executeFields(
143
        ObjectType $parentType,
144
        $source,
0 ignored issues
show
Unused Code introduced by
The parameter $source 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

144
        /** @scrutinizer ignore-unused */ $source,

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...
145
        $path,
146
        $fields): array
147
    {
148
        $finalResults = [];
149
150
        foreach ($fields as $fieldName => $fieldNodes) {
151
            $fieldPath   = $path;
152
            $fieldPath[] = $fieldName;
153
154
            $result = $this->resolveField($parentType,
155
                [],
156
                $fieldNodes,
157
                $fieldPath
158
            );
159
160
            $finalResults[$fieldName] = $result;
161
        }
162
163
        return $finalResults;
164
    }
165
166
    /**
167
     * @param ObjectType $parentType
168
     * @param $source
169
     * @param $fieldNodes
170
     * @param $path
171
     *
172
     * @return mixed
173
     *
174
     * @throws GraphQLError|\Exception
175
     */
176
    protected function resolveField(
177
        ObjectType $parentType,
178
        $source,
179
        $fieldNodes,
180
        $path)
181
    {
182
        $result = [];
183
        /** @var FieldNode $fieldNode */
184
        foreach ($fieldNodes as $fieldNode) {
185
            $field       = $parentType->getFields()[$fieldNode->getNameValue()];
186
            $inputValues = $fieldNode->getArguments() ?? [];
187
            $args        = [];
188
189
            foreach ($inputValues as $value) {
190
                if ($value instanceof ArgumentNode) {
191
                    $args[] = $value->getValue()->getValue();
0 ignored issues
show
Bug introduced by
The method getValue() does not exist on Digia\GraphQL\Language\AST\Node\ValueNodeInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Digia\GraphQL\Language\AST\Node\VariableNode or Digia\GraphQL\Language\AST\Node\ListValueNode or Digia\GraphQL\Language\AST\Node\ObjectValueNode or Digia\GraphQL\Language\AST\Node\NullValueNode. Are you sure you never get one of those? ( Ignorable by Annotation )

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

191
                    $args[] = $value->getValue()->/** @scrutinizer ignore-call */ getValue();
Loading history...
192
                } elseif ($value instanceof InputValueDefinitionNode) {
193
                    $args[] = $value->getDefaultValue()->getValue();
194
                }
195
            }
196
197
            $subResult = $field->resolve(...$args);
198
199
            if (!empty($fieldNode->getSelectionSet())) {
200
                $fields = $this->collectFields(
201
                    $parentType,
202
                    $fieldNode->getSelectionSet(),
203
                    new \ArrayObject(),
204
                    new \ArrayObject()
205
                );
206
207
                $data = $this->executeFields(
208
                    $parentType,
209
                    $source,
210
                    $path,
211
                    $fields
212
                );
213
214
                $subResult = array_merge_recursive(
215
                    $result,
216
                    array_merge_recursive($data, $subResult)
217
                );
218
            }
219
220
            $result = $subResult;
221
        }
222
223
        return $result;
224
    }
225
}
226