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