Completed
Pull Request — 8.x-3.x (#475)
by Sebastian
04:19 queued 01:40
created

PluggableSchemaBuilder::__sleep()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
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\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
      $manager = $this->pluginManagers->getPluginManager($pluginType);
44
      if (empty($manager)) {
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 = $manager->createInstance($pluginId, $pluginConfiguration);
50
      if (empty($instance)) {
51
        throw new \LogicException(sprintf('Failed to instantiate plugin %s of type %s.', $pluginId, $pluginType));
52
      }
53
54
      $this->instances[$cid] = $instance;
55
    }
56
57
    return $this->instances[$cid];
58
  }
59
60
  /**
61
   * {@inheritdoc}
62
   */
63
  public function find(callable $selector, array $types, $invert = FALSE) {
64
    $items = [];
65
66
    /** @var \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginManagerInterface $manager */
67
    foreach ($this->pluginManagers as $type => $manager) {
68
      if (!in_array($type, $types)) {
69
        continue;
70
      }
71
72
      foreach ($manager->getDefinitions() as $id => $definition) {
73
        $name = $definition['name'];
74
        if (empty($name)) {
75
          throw new InvalidPluginDefinitionException($id, 'Invalid plugin definition. No name defined.');
76
        }
77
78
        if (!array_key_exists($name, $items) || $items[$name]['weight'] < $definition['weight']) {
79
          if ((($invert && !$selector($definition)) || $selector($definition))) {
80
            $items[$name] = [
81
              'weight' => $definition['weight'],
82
              'id' => $id,
83
              'type' => $type,
84
            ];
85
          }
86
        }
87
      }
88
    }
89
90
    // Sort the plugins so that the ones with higher weight come first.
91 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...
92
      if ($a['weight'] === $b['weight']) {
93
        return 0;
94
      }
95
96
      return ($a['weight'] < $b['weight']) ? 1 : -1;
97
    });
98
99
    // @TODO: Add support configurable plugins.
100
    return array_map(function (array $item) {
101
      return $this->getInstance($item['type'], $item['id']);
102
    }, $items);
103
  }
104
105
  /**
106
   * {@inheritdoc}
107
   */
108
  public function findByName($name, array $types) {
109
    $result = $this->find(function($definition) use ($name) {
110
      return $definition['name'] === $name;
111
    }, $types);
112
113
    if (empty($result)) {
114
      throw new InvalidPluginDefinitionException(null, sprintf('Plugin with name %s could not be found.', $name));
115
    }
116
117
    return array_pop($result);
118
  }
119
120
  /**
121
   * {@inheritdoc}
122
   */
123
  public function findByDataType($dataType, array $types = [
124
    GRAPHQL_UNION_TYPE_PLUGIN,
125
    GRAPHQL_TYPE_PLUGIN,
126
    GRAPHQL_INTERFACE_PLUGIN,
127
    GRAPHQL_SCALAR_PLUGIN,
128
  ]) {
129
    $chain = explode(':', $dataType);
130
131
    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...
132
      $dataType = implode(':', $chain);
133
134
      $types = $this->find(function($definition) use ($dataType) {
135
        return isset($definition['data_type']) && $definition['data_type'] == $dataType;
136
      }, $types);
137
138
      if (!empty($types)) {
139
        return array_pop($types);
140
      }
141
142
      array_pop($chain);
143
    }
144
145
    return NULL;
146
  }
147
148
  /**
149
   * Creates a plugin instance cache identifier.
150
   *
151
   * @param string $pluginType
152
   *   The plugin type.
153
   * @param string $pluginId
154
   *   The plugin id.
155
   * @param array $pluginConfiguration
156
   *   The plugin configuration.
157
   *
158
   * @return string
159
   */
160
  protected function getCacheIdentifier($pluginType, $pluginId, array $pluginConfiguration) {
161
    if (empty($pluginConfiguration)) {
162
      return "$pluginType:::$pluginId";
163
    }
164
165
    $configCid = md5(serialize($this->sortRecursive($pluginConfiguration)));
166
    return "$pluginType:::$pluginId:::$configCid";
167
  }
168
169
  /**
170
   * Recursively sorts an array.
171
   *
172
   * Useful for generating a cache identifiers.
173
   *
174
   * @param array $subject
175
   *   The array to sort.
176
   *
177
   * @return array
178
   *   The sorted array.
179
   */
180
  protected function sortRecursive(array $subject) {
181
    asort($subject);
182
    foreach ($subject as $key => $item) {
183
      if (is_array($item)) {
184
        $subject[$key] = $this->sortRecursive($item);
185
      }
186
    }
187
188
    return $subject;
189
  }
190
191
  /**
192
   * {@inheritdoc}
193
   */
194
  public function __sleep() {
195
    // Don't write the plugin instances into the cache.
196
    return array_diff($this->sleepDependencies(), ['instances']);
197
  }
198
199
}
200