Completed
Pull Request — 8.x-3.x (#442)
by Sebastian
02:39
created

PluggableSchemaBuilder::createInstance()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 3
dl 0
loc 5
rs 9.4285
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 PluggableSchemaBuilder {
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
   * PluggableSchemaBuilder constructor.
26
   *
27
   * @param array $definitions
28
   *   List of plugin definitions.
29
   */
30
  public function __construct(array $definitions) {
31
    $this->definitions = $definitions;
32
  }
33
34
  /**
35
   * Search for a specific plugin.
36
   *
37
   * @param callable $selector
38
   *   A selector callable that will be used to array_filter the list of
39
   *   plugin definitions.
40
   * @param integer[] $types
41
   *   A list of type constants.
42
   * @param bool $invert
43
   *   Invert the selector result.
44
   *
45
   * @return \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginInterface[]
46
   *   The list of matching plugin instances, keyed by name.
47
   *
48
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
49
   */
50
  public function find(callable $selector, array $types, $invert = FALSE) {
51
    $instances = [];
52
    foreach ($this->definitions as $index => $definition) {
53
      $name = $definition['definition']['name'];
54
      if (empty($name)) {
55
        throw new InvalidPluginDefinitionException('Invalid GraphQL plugin definition. No name defined.');
56
      }
57
58
      if (!array_key_exists($name, $instances) && in_array($definition['definition']['pluginType'], $types)) {
59
        if ((($invert && !$selector($definition['definition'])) || $selector($definition['definition']))) {
60
          $instances[$name] = $this->getInstance($definition['manager'], $definition['type'], $definition['id']);
61
        }
62
      }
63
    }
64
65
    return $instances;
66
  }
67
68
  /**
69
   * Search for a specific plugin.
70
   *
71
   * @param string $name
72
   *   The specific plugin name.
73
   * @param integer[] $types
74
   *   A list of type constants.
75
   *
76
   * @return \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginInterface
77
   *   The highest weighted plugin with a specific name.
78
   *
79
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
80
   */
81
  public function findByName($name, array $types) {
82
    $result = $this->find(function($definition) use ($name) {
83
      return $definition['name'] === $name;
84
    }, $types);
85
86
    if (empty($result)) {
87
      throw new InvalidPluginDefinitionException(sprintf('GraphQL plugin with name %s could not be found.', $name));
88
    }
89
90
    return array_pop($result);
91
  }
92
93
  /**
94
   * Find the matching GraphQL data type for a Drupal type data identifier.
95
   *
96
   * Respects type chains. `entity:node:article` should return the
97
   * `NodeArticle` type if it is exposed or fall back to either `Node` or even
98
   * `Entity` otherwise.
99
   *
100
   * @param string $dataType
101
   *   The typed data identifier. E.g. `string` or `entity:node:article`.
102
   * @param string[] $types
103
   *   A list of type constants.
104
   *
105
   * @return \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginInterface
106
   *   The matching type with the highest weight.
107
   */
108
  public function findByDataType($dataType, array $types = [
109
    GRAPHQL_UNION_TYPE_PLUGIN,
110
    GRAPHQL_TYPE_PLUGIN,
111
    GRAPHQL_INTERFACE_PLUGIN,
112
    GRAPHQL_SCALAR_PLUGIN,
113
  ]) {
114
    $chain = explode(':', $dataType);
115
116
    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...
117
      $dataType = implode(':', $chain);
118
119
      $types = $this->find(function($definition) use ($dataType) {
120
        return isset($definition['data_type']) && $definition['data_type'] == $dataType;
121
      }, $types);
0 ignored issues
show
Documentation introduced by
$types is of type array<integer,object<Dru...luginInterface>|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...
122
123
      if (!empty($types)) {
124
        return array_pop($types);
125
      }
126
127
      array_pop($chain);
128
    }
129
130
    return NULL;
131
  }
132
133
  /**
134
   * Retrieve all mutations.
135
   *
136
   * @return \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginInterface[]
137
   *   The list of mutation plugins.
138
   */
139
  public function getMutations() {
140
    return $this->find(function() {
141
      return TRUE;
142
    }, [GRAPHQL_MUTATION_PLUGIN]);
143
  }
144
145
  /**
146
   * Retrieve all fields that are not associated with a specific type.
147
   *
148
   * @return \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginInterface[]
149
   *   The list root field plugins.
150
   */
151
  public function getRootFields() {
152
    // Retrieve the list of fields that are explicitly attached to a type.
153
    $attachedFields = array_reduce(array_filter(array_map(function($definition) {
154
      return array_key_exists('fields', $definition['definition']) ? $definition['definition']['fields'] : NULL;
155
    }, $this->definitions)), 'array_merge', []);
156
157
    // Retrieve the list of fields that are not attached in any way or
158
    // explicitly attached to the artificial "Root" type.
159
    return $this->find(function($definition) use ($attachedFields) {
160
      return (!in_array($definition['name'], $attachedFields) && empty($definition['parents'])) || in_array('Root', $definition['parents']);
161
    }, [GRAPHQL_FIELD_PLUGIN]);
162
  }
163
164
  /**
165
   * Creates a type system plugin instance for a given plugin manager.
166
   *
167
   * @param \Drupal\Component\Plugin\PluginManagerInterface $manager
168
   *   The plugin manager responsible for creation of the plugin instance.
169
   * @param $pluginType
170
   *   The plugin type.
171
   * @param $pluginId
172
   *   The plugin id.
173
   *
174
   * @return \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginInterface
175
   *   The created plugin instance.
176
   */
177
  protected function getInstance(PluginManagerInterface $manager, $pluginType, $pluginId) {
178
    if (!isset($this->instances[$pluginType][$pluginId])) {
179
      // Initialize the static cache array if necessary.
180
      $this->instances[$pluginType] = isset($this->instances[$pluginType]) ? $this->instances[$pluginType] : [];
181
182
      // We do not allow plugin configuration for now.
183
      $instance = $manager->createInstance($pluginId);
184
      if (empty($instance)) {
185
        throw new \LogicException(sprintf('Could not instantiate plugin %s of type %s.', $pluginId, $pluginType));
186
      }
187
188
      if (!$instance instanceof TypeSystemPluginInterface) {
189
        throw new \LogicException(sprintf('Plugin %s of type %s does not implement \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginInterface.', $pluginId, $pluginType));
190
      }
191
192
      // Prevent circular dependencies by building the type after constructing the plugin instance.
193
      $this->instances[$pluginType][$pluginId] = $instance;
194
    }
195
196
    return $this->instances[$pluginType][$pluginId];
197
  }
198
199
  /**
200
   * {@inheritdoc}
201
   */
202
  public function __sleep() {
203
    // Don't write the plugin instances into the cache.
204
    return array_keys(array_diff_key(get_object_vars($this), ['instances' => TRUE]));
205
  }
206
}
207