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

PluggableSchemaBuilder::getInstance()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 14
nc 4
nop 3
dl 0
loc 25
rs 8.439
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\Core\DependencyInjection\DependencySerializationTrait;
7
8
class PluggableSchemaBuilder implements PluggableSchemaBuilderInterface {
9
  use DependencySerializationTrait {
10
    __sleep as sleepDependencies;
11
  }
12
13
  /**
14
   * The type system plugin manager aggregator service.
15
   *
16
   * @var \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginManagerAggregator
17
   */
18
  protected $pluginManagers;
19
20
  /**
21
   * Static cache of type system plugin instances.
22
   *
23
   * @var \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginInterface
24
   */
25
  protected $instances = [];
26
27
  /**
28
   * PluggableSchemaBuilder constructor.
29
   *
30
   * @param \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginManagerAggregator $pluginManagers
31
   *   Type system plugin manager aggregator service.
32
   */
33
  public function __construct(TypeSystemPluginManagerAggregator $pluginManagers) {
34
    $this->pluginManagers = $pluginManagers;
35
  }
36
37
  /**
38
   * {@inheritdoc}
39
   */
40
  public function getInstance($pluginType, $pluginId, array $pluginConfiguration = []) {
41
    $cid = $this->getCacheIdentifier($pluginType, $pluginId, $pluginConfiguration);
42
    if (!isset($this->instances[$cid])) {
43
      $managers = $this->pluginManagers->getPluginManagers($pluginType);
44
      if (empty($managers)) {
45
        throw new \LogicException(sprintf('Could not find %s plugin manager for plugin %s.', $pluginType, $pluginId));
46
      }
47
48
      // We do not allow plugin configuration for now.
49
      $instance = NULL;
50
      foreach ($managers as $manager) {
51
        if($manager->hasDefinition($pluginId)) {
52
          $instance = $manager->createInstance($pluginId, $pluginConfiguration);
53
        }
54
      }
55
56
      if (empty($instance)) {
57
        throw new \LogicException(sprintf('Failed to instantiate plugin %s of type %s.', $pluginId, $pluginType));
58
      }
59
60
      $this->instances[$cid] = $instance;
61
    }
62
63
    return $this->instances[$cid];
64
  }
65
66
  /**
67
   * {@inheritdoc}
68
   */
69
  public function find(callable $selector, array $types, $invert = FALSE) {
70
    $items = [];
71
72
    /** @var \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginManagerInterface $manager */
73
    foreach ($this->pluginManagers as $type => $managers) {
74
      if (!in_array($type, $types)) {
75
        continue;
76
      }
77
78
      foreach ($managers as $manager) {
79
        foreach ($manager->getDefinitions() as $id => $definition) {
80
          $name = $definition['name'];
81
          if (empty($name)) {
82
            throw new InvalidPluginDefinitionException($id, 'Invalid plugin definition. No name defined.');
83
          }
84
85
          if (!array_key_exists($name, $items) || $items[$name]['weight'] < $definition['weight']) {
86
            if ((($invert && !$selector($definition)) || $selector($definition))) {
87
              $items[$name] = [
88
                'weight' => $definition['weight'],
89
                'id' => $id,
90
                'type' => $type,
91
              ];
92
            }
93
          }
94
        }
95
      }
96
    }
97
98
    // Sort the plugins so that the ones with higher weight come first.
99 View Code Duplication
    usort($items, function (array $a, array $b) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
100
      if ($a['weight'] === $b['weight']) {
101
        return 0;
102
      }
103
104
      return ($a['weight'] < $b['weight']) ? 1 : -1;
105
    });
106
107
    // @TODO: Add support configurable plugins.
108
    return array_map(function (array $item) {
109
      return $this->getInstance($item['type'], $item['id']);
110
    }, $items);
111
  }
112
113
  /**
114
   * {@inheritdoc}
115
   */
116
  public function findByName($name, array $types) {
117
    $result = $this->find(function($definition) use ($name) {
118
      return $definition['name'] === $name;
119
    }, $types);
120
121
    if (empty($result)) {
122
      throw new InvalidPluginDefinitionException(null, sprintf('Plugin with name %s could not be found.', $name));
123
    }
124
125
    return array_pop($result);
126
  }
127
128
  /**
129
   * {@inheritdoc}
130
   */
131
  public function findByDataType($dataType, array $types = [
132
    GRAPHQL_UNION_TYPE_PLUGIN,
133
    GRAPHQL_TYPE_PLUGIN,
134
    GRAPHQL_INTERFACE_PLUGIN,
135
    GRAPHQL_SCALAR_PLUGIN,
136
  ]) {
137
    $chain = explode(':', $dataType);
138
139
    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...
140
      $dataType = implode(':', $chain);
141
142
      $types = $this->find(function($definition) use ($dataType) {
143
        return isset($definition['data_type']) && $definition['data_type'] == $dataType;
144
      }, $types);
145
146
      if (!empty($types)) {
147
        return array_pop($types);
148
      }
149
150
      array_pop($chain);
151
    }
152
153
    return NULL;
154
  }
155
156
  /**
157
   * Creates a plugin instance cache identifier.
158
   *
159
   * @param string $pluginType
160
   *   The plugin type.
161
   * @param string $pluginId
162
   *   The plugin id.
163
   * @param array $pluginConfiguration
164
   *   The plugin configuration.
165
   *
166
   * @return string
167
   */
168
  protected function getCacheIdentifier($pluginType, $pluginId, array $pluginConfiguration) {
169
    if (empty($pluginConfiguration)) {
170
      return "$pluginType:::$pluginId";
171
    }
172
173
    $configCid = md5(serialize($this->sortRecursive($pluginConfiguration)));
174
    return "$pluginType:::$pluginId:::$configCid";
175
  }
176
177
  /**
178
   * Recursively sorts an array.
179
   *
180
   * Useful for generating a cache identifiers.
181
   *
182
   * @param array $subject
183
   *   The array to sort.
184
   *
185
   * @return array
186
   *   The sorted array.
187
   */
188
  protected function sortRecursive(array $subject) {
189
    asort($subject);
190
    foreach ($subject as $key => $item) {
191
      if (is_array($item)) {
192
        $subject[$key] = $this->sortRecursive($item);
193
      }
194
    }
195
196
    return $subject;
197
  }
198
199
  /**
200
   * {@inheritdoc}
201
   */
202
  public function __sleep() {
203
    // Don't write the plugin instances into the cache.
204
    return array_diff($this->sleepDependencies(), ['instances']);
205
  }
206
207
}
208