6
6
use DrupalFinder \DrupalFinder ;
7
7
use Nette \Utils \Finder ;
8
8
use PHPStan \DependencyInjection \Container ;
9
+ use Symfony \Component \Yaml \Yaml ;
9
10
10
11
class DrupalAutoloader
11
12
{
12
- /**
13
- * @var array
14
- */
15
- protected $ defaultConfig = [
16
- 'modules ' => [],
17
- 'themes ' => [],
18
- ];
19
13
20
14
/**
21
15
* @var \Composer\Autoload\ClassLoader
@@ -42,14 +36,19 @@ class DrupalAutoloader
42
36
protected $ themeData = [];
43
37
44
38
/**
45
- * @var array
39
+ * @var array<array<string, string>>
46
40
*/
47
- private $ modules = [];
41
+ private $ serviceMap = [];
48
42
49
43
/**
50
- * @var array
44
+ * @var array<string, string>
51
45
*/
52
- private $ themes = [];
46
+ private $ serviceYamls = [];
47
+
48
+ /**
49
+ * @var array<string, string>
50
+ */
51
+ private $ serviceClassProviders = [];
53
52
54
53
/**
55
54
* @var ?\PHPStan\Drupal\ExtensionDiscovery
@@ -88,6 +87,10 @@ public function register(Container $container): void
88
87
89
88
$ this ->autoloader = include $ drupalVendorRoot . '/autoload.php ' ;
90
89
90
+ $ this ->serviceYamls ['core ' ] = $ drupalRoot . '/core/core.services.yml ' ;
91
+ $ this ->serviceClassProviders ['core ' ] = '\Drupal\Core\CoreServiceProvider ' ;
92
+ $ this ->serviceMap ['service_provider.core.service_provider ' ] = ['class ' => $ this ->serviceClassProviders ['core ' ]];
93
+
91
94
$ this ->extensionDiscovery = new ExtensionDiscovery ($ this ->drupalRoot );
92
95
$ this ->extensionDiscovery ->setProfileDirectories ([]);
93
96
$ profiles = $ this ->extensionDiscovery ->scan ('profile ' );
@@ -156,6 +159,45 @@ public function register(Container $container): void
156
159
}
157
160
}
158
161
}
162
+
163
+ foreach ($ this ->serviceYamls as $ extension => $ serviceYaml ) {
164
+ $ yaml = Yaml::parseFile ($ serviceYaml );
165
+ // Weed out service files which only provide parameters.
166
+ if (!isset ($ yaml ['services ' ]) || !is_array ($ yaml ['services ' ])) {
167
+ continue ;
168
+ }
169
+ foreach ($ yaml ['services ' ] as $ serviceId => $ serviceDefinition ) {
170
+ // Prevent \Nette\DI\ContainerBuilder::completeStatement from array_walk_recursive into the arguments
171
+ // and thinking these are real services for PHPStan's container.
172
+ if (isset ($ serviceDefinition ['arguments ' ]) && is_array ($ serviceDefinition ['arguments ' ])) {
173
+ array_walk ($ serviceDefinition ['arguments ' ], function (&$ argument ) : void {
174
+ if (is_array ($ argument )) {
175
+ // @todo fix for @http_kernel.controller.argument_metadata_factory
176
+ $ argument = '' ;
177
+ } else {
178
+ $ argument = str_replace ('@ ' , '' , $ argument );
179
+ }
180
+ });
181
+ }
182
+ // @todo sanitize "calls" and "configurator" and "factory"
183
+ /**
184
+ jsonapi.params.enhancer:
185
+ class: Drupal\jsonapi\Routing\JsonApiParamEnhancer
186
+ calls:
187
+ - [setContainer, ['@service_container']]
188
+ tags:
189
+ - { name: route_enhancer }
190
+ */
191
+ unset($ serviceDefinition ['tags ' ], $ serviceDefinition ['calls ' ], $ serviceDefinition ['configurator ' ], $ serviceDefinition ['factory ' ]);
192
+ $ this ->serviceMap [$ serviceId ] = $ serviceDefinition ;
193
+ }
194
+ }
195
+
196
+ $ service_map = $ container ->getByType (ServiceMap::class);
197
+ assert ($ service_map instanceof ServiceMap);
198
+ // @todo this is not updating the reference in the container.
199
+ $ service_map ->setDrupalServices ($ this ->serviceMap );
200
+
159
201
}
160
202
161
203
protected function loadLegacyIncludes (): void
@@ -216,6 +258,18 @@ protected function addModuleNamespaces(): void
216
258
}
217
259
}
218
260
}
261
+
262
+ $ servicesFileName = $ module_dir . '/ ' . $ module_name . '.services.yml ' ;
263
+ if (file_exists ($ servicesFileName )) {
264
+ $ this ->serviceYamls [$ module_name ] = $ servicesFileName ;
265
+ }
266
+ $ camelized = $ this ->camelize ($ module_name );
267
+ $ name = "{$ camelized }ServiceProvider " ;
268
+ $ class = "Drupal \\{$ module_name }\\{$ name }" ;
269
+
270
+ $ this ->serviceClassProviders [$ module_name ] = $ class ;
271
+ $ serviceId = "service_provider. $ module_name.service_provider " ;
272
+ $ this ->serviceMap [$ serviceId ] = ['class ' => $ class ];
219
273
}
220
274
}
221
275
protected function addThemeNamespaces (): void
@@ -261,4 +315,9 @@ protected function loadAndCatchErrors(string $path): void
261
315
@trigger_error ("$ path failed loading due to {$ e ->getMessage ()}" , E_USER_WARNING );
262
316
}
263
317
}
318
+
319
+ protected function camelize (string $ id ): string
320
+ {
321
+ return strtr (ucwords (strtr ($ id , ['_ ' => ' ' , '. ' => '_ ' , '\\' => '_ ' ])), [' ' => '' ]);
322
+ }
264
323
}
0 commit comments