|
4 | 4 |
|
5 | 5 | use Nette\DI\CompilerExtension;
|
6 | 6 | use Nette\DI\Config\Helpers;
|
| 7 | +use PHPStan\Drupal\ExtensionDiscovery; |
7 | 8 | use PHPStan\Rules\Classes\EnhancedRequireParentConstructCallRule;
|
8 | 9 | use PHPStan\Rules\Classes\RequireParentConstructCallRule;
|
| 10 | +use Symfony\Component\Yaml\Yaml; |
9 | 11 |
|
10 | 12 | class DrupalExtension extends CompilerExtension
|
11 | 13 | {
|
@@ -92,5 +94,75 @@ public function loadConfiguration(): void
|
92 | 94 | $definition->setFactory(EnhancedRequireParentConstructCallRule::class);
|
93 | 95 | }
|
94 | 96 | }
|
| 97 | + |
| 98 | + // Build the service definitions... |
| 99 | + $extensionDiscovery = new ExtensionDiscovery($this->drupalRoot); |
| 100 | + $extensionDiscovery->setProfileDirectories([]); |
| 101 | + $profiles = $extensionDiscovery->scan('profile'); |
| 102 | + $profile_directories = array_map(function ($profile) { |
| 103 | + return $profile->getPath(); |
| 104 | + }, $profiles); |
| 105 | + $extensionDiscovery->setProfileDirectories($profile_directories); |
| 106 | + |
| 107 | + |
| 108 | + $serviceYamls = [ |
| 109 | + 'core' => $this->drupalRoot . '/core/core.services.yml', |
| 110 | + ]; |
| 111 | + $serviceClassProviders = [ |
| 112 | + 'core' => 'Drupal\Core\CoreServiceProvider', |
| 113 | + ]; |
| 114 | + |
| 115 | + foreach ($extensionDiscovery->scan('module') as $extension) { |
| 116 | + $module_dir = $this->drupalRoot . '/' . $extension->getPath(); |
| 117 | + $moduleName = $extension->getName(); |
| 118 | + $servicesFileName = $module_dir . '/' . $moduleName . '.services.yml'; |
| 119 | + if (file_exists($servicesFileName)) { |
| 120 | + $serviceYamls[$moduleName] = $servicesFileName; |
| 121 | + } |
| 122 | + |
| 123 | + $camelized = $this->camelize($extension->getName()); |
| 124 | + $name = "{$camelized}ServiceProvider"; |
| 125 | + $class = "Drupal\\{$moduleName}\\{$name}"; |
| 126 | + |
| 127 | + if (class_exists($class)) { |
| 128 | + $serviceClassProviders[$moduleName] = $class; |
| 129 | + } |
| 130 | + } |
| 131 | + |
| 132 | + foreach ($serviceYamls as $extension => $serviceYaml) { |
| 133 | + $yaml = Yaml::parseFile($serviceYaml); |
| 134 | + // Weed out service files which only provide parameters. |
| 135 | + if (!isset($yaml['services']) || !is_array($yaml['services'])) { |
| 136 | + continue; |
| 137 | + } |
| 138 | + foreach ($yaml['services'] as $serviceId => $serviceDefinition) { |
| 139 | + // Prevent \Nette\DI\ContainerBuilder::completeStatement from array_walk_recursive into the arguments |
| 140 | + // and thinking these are real services for PHPStan's container. |
| 141 | + if (isset($serviceDefinition['arguments']) && is_array($serviceDefinition['arguments'])) { |
| 142 | + array_walk($serviceDefinition['arguments'], function (&$argument) { |
| 143 | + $argument = str_replace('@', '', $argument); |
| 144 | + }); |
| 145 | + } |
| 146 | + unset($serviceDefinition['tags']); |
| 147 | + // @todo sanitize "calls" and "configurator" and "factory" |
| 148 | + /** |
| 149 | + jsonapi.params.enhancer: |
| 150 | + class: Drupal\jsonapi\Routing\JsonApiParamEnhancer |
| 151 | + calls: |
| 152 | + - [setContainer, ['@service_container']] |
| 153 | + tags: |
| 154 | + - { name: route_enhancer } |
| 155 | + */ |
| 156 | + unset($serviceDefinition['calls']); |
| 157 | + unset($serviceDefinition['configurator']); |
| 158 | + unset($serviceDefinition['factory']); |
| 159 | + $builder->parameters['drupalServiceMap'][$serviceId] = $serviceDefinition; |
| 160 | + } |
| 161 | + } |
| 162 | + } |
| 163 | + |
| 164 | + protected function camelize(string $id): string |
| 165 | + { |
| 166 | + return strtr(ucwords(strtr($id, ['_' => ' ', '.' => '_ ', '\\' => '_ '])), [' ' => '']); |
95 | 167 | }
|
96 | 168 | }
|
0 commit comments