Completed
Pull Request — 8.x-3.x (#401)
by Philipp
02:44
created

SchemaBuilder::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Drupal\graphql\Plugin\GraphQL;
4
5
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
6
use Drupal\Component\Plugin\PluginManagerInterface;
7
8
class SchemaBuilder {
9
10
  /**
11
   * Static cache of type system plugin instances.
12
   *
13
   * @var \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginInterface
14
   */
15
  protected $instances = [];
16
17
  /**
18
   * Static cache of plugin definitions.
19
   *
20
   * @var array
21
   */
22
  protected $definitions;
23
24
  /**
25
   * List of registered type system plugin managers.
26
   *
27
   * @var \Drupal\Component\Plugin\PluginManagerInterface[]
28
   */
29
  protected $pluginManagers;
30
31
  /**
32
   * SchemaBuilder constructor.
33
   *
34
   * @param \Drupal\Component\Plugin\PluginManagerInterface[] $pluginManagers
35
   *   List of type system plugin managers.
36
   */
37
  public function __construct(array $pluginManagers) {
38
    $this->pluginManagers = $pluginManagers;
39
  }
40
41
  /**
42
   * Returns the list of sorted plugin definitions.
43
   *
44
   * @return array
45
   *   The list of sorted plugin definitions.
46
   */
47
  protected function getDefinitions() {
48
    if ($this->definitions == NULL) {
49
      foreach ($this->pluginManagers as $manager) {
50
        foreach ($manager->getDefinitions() as $pluginId => $definition) {
51
          $this->definitions[] = [
52
            'id' => $pluginId,
53
            'type' => $definition['pluginType'],
54
            'weight' => $definition['weight'],
55
            'manager' => $manager,
56
            'definition' => $definition,
57
          ];
58
        }
59
      }
60
61
      uasort($this->definitions, '\Drupal\Component\Utility\SortArray::sortByWeightElement');
62
      $this->definitions = array_reverse($this->definitions);
63
    }
64
65
    return $this->definitions;
66
  }
67
68
  /**
69
   * Search for a specific plugin.
70
   *
71
   * @param callable $selector
72
   *   A selector callable that will be used to array_filter the list of
73
   *   plugin definitions.
74
   * @param integer[] $types
75
   *   A list of type constants.
76
   * @param bool $invert
77
   *   Invert the selector result.
78
   *
79
   * @return object[]
80
   *   The list of matching plugin instances, keyed by name.
81
   */
82
  public function find(callable $selector, array $types, $invert = FALSE) {
83
    $instances = [];
84
    foreach ($this->getDefinitions() as $index => $definition) {
85
      $name = $definition['definition']['name'];
86
      if (empty($name)) {
87
        throw new InvalidPluginDefinitionException('Invalid GraphQL plugin definition. No name defined.');
88
      }
89
90
      if (!array_key_exists($name, $instances) && in_array($definition['definition']['pluginType'], $types)) {
91
        if ((($invert && !$selector($definition['definition'])) || $selector($definition['definition']))) {
92
          $instances[$name] = $this->getInstance($definition['manager'], $definition['type'], $definition['id']);
93
        }
94
      }
95
    }
96
97
    return $instances;
98
  }
99
100
  /**
101
   * Search for a specific plugin.
102
   *
103
   * @param string $name
104
   *   The specific plugin name.
105
   * @param integer[] $types
106
   *   A list of type constants.
107
   *
108
   * @return object
109
   *   The highest weighted plugin with a specific name.
110
   */
111
  public function findByName($name, array $types) {
112
    $result = $this->find(function($definition) use ($name) {
113
      return $definition['name'] === $name;
114
    }, $types);
115
116
    if (empty($result)) {
117
      throw new InvalidPluginDefinitionException(sprintf('GraphQL plugin with name %s could not be found.', $name));
118
    }
119
120
    return array_pop($result);
121
  }
122
123
  /**
124
   * Find the matching GraphQL data type for a Drupal type data identifier.
125
   *
126
   * Respects type chains. `entity:node:article` should return the
127
   * `NodeArticle` type if it is exposed or fall back to either `Node` or even
128
   * `Entity` otherwise.
129
   *
130
   * @param string $dataType
131
   *   The typed data identifier. E.g. `string` or `entity:node:article`.
132
   * @param string[] $types
133
   *   A list of type constants.
134
   *
135
   * @return object
136
   *   The matching type with the highest weight.
137
   */
138
  public function findByDataType($dataType, array $types = [
139
    GRAPHQL_UNION_TYPE_PLUGIN,
140
    GRAPHQL_TYPE_PLUGIN,
141
    GRAPHQL_INTERFACE_PLUGIN,
142
    GRAPHQL_SCALAR_PLUGIN,
143
  ]) {
144
    $chain = explode(':', $dataType);
145
146
    while ($chain) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $chain of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
147
      $dataType = implode(':', $chain);
148
149
      $types = $this->find(function($definition) use ($dataType) {
150
        return isset($definition['data_type']) && $definition['data_type'] == $dataType;
151
      }, $types);
0 ignored issues
show
Documentation introduced by
$types is of type array<integer,object|string>, but the function expects a array<integer,integer>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
152
153
      if (!empty($types)) {
154
        return array_pop($types);
155
      }
156
157
      array_pop($chain);
158
    }
159
160
    return NULL;
161
  }
162
163
  /**
164
   * Retrieve all mutations.
165
   *
166
   * @return object[]
167
   *   The list of mutation plugins.
168
   */
169
  public function getMutations() {
170
    return $this->find(function() {
171
      return TRUE;
172
    }, [GRAPHQL_MUTATION_PLUGIN]);
173
  }
174
175
  /**
176
   * Retrieve all fields that are not associated with a specific type.
177
   *
178
   * @return object[]
179
   *   The list root field plugins.
180
   */
181
  public function getRootFields() {
182
    // Retrieve the list of fields that are explicitly attached to a type.
183
    $attachedFields = array_reduce(array_filter(array_map(function($definition) {
184
      return array_key_exists('fields', $definition['definition']) ? $definition['definition']['fields'] : NULL;
185
    }, $this->getDefinitions())), 'array_merge', []);
186
187
    // Retrieve the list of fields that are not attached in any way or
188
    // explicitly attached to the artificial "Root" type.
189
    return $this->find(function($definition) use ($attachedFields) {
190
      return (!in_array($definition['name'], $attachedFields) && empty($definition['parents'])) || in_array('Root', $definition['parents']);
191
    }, [GRAPHQL_FIELD_PLUGIN]);
192
  }
193
194
  /**
195
   * Creates a type system plugin instance for a given plugin manager.
196
   *
197
   * @param \Drupal\Component\Plugin\PluginManagerInterface $manager
198
   *   The plugin manager responsible for creation of the plugin instance.
199
   * @param $pluginType
200
   *   The plugin type.
201
   * @param $pluginId
202
   *   The plugin id.
203
   *
204
   * @return \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginInterface
205
   *   The created plugin instance.
206
   */
207
  protected function getInstance(PluginManagerInterface $manager, $pluginType, $pluginId) {
208
    if (!isset($this->instances[$pluginType][$pluginId])) {
209
      // Initialize the static cache array if necessary.
210
      $this->instances[$pluginType] = isset($this->instances[$pluginType]) ? $this->instances[$pluginType] : [];
211
212
      // We do not allow plugin configuration for now.
213
      $instance = $manager->createInstance($pluginId);
214
      if (empty($instance)) {
215
        throw new \LogicException(sprintf('Could not instantiate plugin %s of type %s.', $pluginId, $pluginType));
216
      }
217
218
      if (!$instance instanceof TypeSystemPluginInterface) {
219
        throw new \LogicException(sprintf('Plugin %s of type %s does not implement \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginInterface.', $pluginId, $pluginType));
220
      }
221
222
      // Prevent circular dependencies by building the type after constructing the plugin instance.
223
      $this->instances[$pluginType][$pluginId] = $instance;
224
      $this->instances[$pluginType][$pluginId]->buildConfig($this);
225
    }
226
227
    return $this->instances[$pluginType][$pluginId];
228
  }
229
}
230