Completed
Pull Request — 8.x-3.x (#490)
by Sebastian
05:27
created

PluggableSchemaBuilder::sortRecursive()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 3
nop 1
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Drupal\graphql\Plugin\GraphQL;
4
5
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
6
7
class PluggableSchemaBuilder implements PluggableSchemaBuilderInterface {
8
  use DependencySerializationTrait {
9
    __sleep as sleepDependencies;
10
  }
11
12
  /**
13
   * The type system plugin manager aggregator service.
14
   *
15
   * @var \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginManagerAggregator
16
   */
17
  protected $pluginManagers;
18
19
  /**
20
   * Static cache of type system plugin instances.
21
   *
22
   * @var \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginInterface
23
   */
24
  protected $instances = [];
25
26
  /**
27
   * PluggableSchemaBuilder constructor.
28
   *
29
   * @param \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginManagerAggregator $pluginManagers
30
   *   Type system plugin manager aggregator service.
31
   */
32
  public function __construct(TypeSystemPluginManagerAggregator $pluginManagers) {
33
    $this->pluginManagers = $pluginManagers;
34
  }
35
36
  /**
37
   * {@inheritdoc}
38
   */
39
  public function getInstance($pluginType, $pluginId, array $pluginConfiguration = []) {
40
    $cid = $this->getCacheIdentifier($pluginType, $pluginId, $pluginConfiguration);
41
    if (!isset($this->instances[$cid])) {
42
      $manager = $this->pluginManagers->getPluginManager($pluginType);
43
      if (empty($manager)) {
44
        throw new \LogicException(sprintf('Could not find %s plugin manager for plugin %s.', $pluginType, $pluginId));
45
      }
46
47
      // We do not allow plugin configuration for now.
48
      $instance = $manager->createInstance($pluginId, $pluginConfiguration);
49
      if (empty($instance)) {
50
        throw new \LogicException(sprintf('Failed to instantiate plugin %s of type %s.', $pluginId, $pluginType));
51
      }
52
53
      $this->instances[$cid] = $instance;
54
    }
55
56
    return $this->instances[$cid];
57
  }
58
59
  /**
60
   * {@inheritdoc}
61
   */
62
  public function find(callable $selector, array $types) {
63
    $items = [];
64
65
    /** @var \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginManagerInterface $manager */
66
    foreach ($this->pluginManagers as $type => $manager) {
67
      if (!in_array($type, $types)) {
68
        continue;
69
      }
70
71
      foreach ($manager->getDefinitions() as $id => $definition) {
72
        $name = $definition['name'];
73
74
        if (!array_key_exists($name, $items) || $items[$name]['weight'] < $definition['weight']) {
75
          if ($selector($definition)) {
76
            $items[$name] = [
77
              'weight' => $definition['weight'],
78
              'id' => $id,
79
              'type' => $type,
80
            ];
81
          }
82
        }
83
      }
84
    }
85
86
    // Sort the plugins so that the ones with higher weight come first.
87 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...
88
      if ($a['weight'] === $b['weight']) {
89
        return 0;
90
      }
91
92
      return ($a['weight'] < $b['weight']) ? 1 : -1;
93
    });
94
95
    return array_map(function (array $item) {
96
      return $this->getInstance($item['type'], $item['id']);
97
    }, $items);
98
  }
99
100
  /**
101
   * {@inheritdoc}
102
   */
103
  public function findByDataType($type, array $types) {
104
    $parts = explode(':', $type);
105
    $chain = array_reverse(array_reduce($parts, function ($carry, $current) {
106
      return $carry + [implode(':', array_filter([end($carry), $current]))];
107
    }, []), TRUE);
108
109
    $result = $this->find(function($definition) use ($chain) {
110
      if (!empty($definition['type'])) {
111
        foreach ($chain as $priority => $part) {
112
          if ($definition['type'] === $part) {
113
            return TRUE;
114
          }
115
        }
116
      }
117
118
      return FALSE;
119
    }, $types);
120
121
    return array_pop($result);
122
  }
123
124
  public function findByName($name, array $types) {
125
    $result = $this->find(function($definition) use ($name) {
126
      return $definition['name'] === $name;
127
    }, $types);
128
129
    return array_pop($result);
130
  }
131
132
  /**
133
   * {@inheritdoc}
134
   */
135
  public function findByDataTypeOrName($input, array $types) {
136
    if ($type = $this->findByDataType($input, $types)) {
137
      return $type;
138
    }
139
140
    if ($type = $this->findByName($input, $types)) {
141
      return $type;
142
    }
143
144
    return $this->getInstance('scalar', 'undefined');
145
  }
146
147
  /**
148
   * Creates a plugin instance cache identifier.
149
   *
150
   * @param string $pluginType
151
   *   The plugin type.
152
   * @param string $pluginId
153
   *   The plugin id.
154
   * @param array $pluginConfiguration
155
   *   The plugin configuration.
156
   *
157
   * @return string
158
   */
159
  protected function getCacheIdentifier($pluginType, $pluginId, array $pluginConfiguration) {
160
    if (empty($pluginConfiguration)) {
161
      return "$pluginType:::$pluginId";
162
    }
163
164
    $configCid = md5(serialize($this->sortRecursive($pluginConfiguration)));
165
    return "$pluginType:::$pluginId:::$configCid";
166
  }
167
168
  /**
169
   * Recursively sorts an array.
170
   *
171
   * Useful for generating a cache identifiers.
172
   *
173
   * @param array $subject
174
   *   The array to sort.
175
   *
176
   * @return array
177
   *   The sorted array.
178
   */
179
  protected function sortRecursive(array $subject) {
180
    asort($subject);
181
    foreach ($subject as $key => $item) {
182
      if (is_array($item)) {
183
        $subject[$key] = $this->sortRecursive($item);
184
      }
185
    }
186
187
    return $subject;
188
  }
189
190
  /**
191
   * {@inheritdoc}
192
   */
193
  public function __sleep() {
194
    // Don't write the plugin instances into the cache.
195
    return array_diff($this->sleepDependencies(), ['instances']);
196
  }
197
198
}
199