|
18 | 18 | use Composer\Repository\RepositorySet; |
19 | 19 | use Composer\Script\Event; |
20 | 20 | use Composer\Script\ScriptEvents; |
| 21 | +use Composer\Util\Filesystem; |
21 | 22 | use Http\Discovery\ClassDiscovery; |
22 | 23 |
|
23 | 24 | /** |
@@ -98,9 +99,30 @@ class Plugin implements PluginInterface, EventSubscriberInterface |
98 | 99 | 'http-interop/http-factory-slim' => 'slim/slim:^3', |
99 | 100 | ]; |
100 | 101 |
|
| 102 | + private const INTERFACE_MAP = [ |
| 103 | + 'php-http/async-client-implementation' => [ |
| 104 | + 'Http\Client\HttpAsyncClient', |
| 105 | + ], |
| 106 | + 'php-http/client-implementation' => [ |
| 107 | + 'Http\Client\HttpClient', |
| 108 | + ], |
| 109 | + 'psr/http-client-implementation' => [ |
| 110 | + 'Psr\Http\Client\ClientInterface', |
| 111 | + ], |
| 112 | + 'psr/http-factory-implementation' => [ |
| 113 | + 'Psr\Http\Message\RequestFactoryInterface', |
| 114 | + 'Psr\Http\Message\ResponseFactoryInterface', |
| 115 | + 'Psr\Http\Message\ServerRequestFactoryInterface', |
| 116 | + 'Psr\Http\Message\StreamFactoryInterface', |
| 117 | + 'Psr\Http\Message\UploadedFileFactoryInterface', |
| 118 | + 'Psr\Http\Message\UriFactoryInterface', |
| 119 | + ], |
| 120 | + ]; |
| 121 | + |
101 | 122 | public static function getSubscribedEvents(): array |
102 | 123 | { |
103 | 124 | return [ |
| 125 | + ScriptEvents::PRE_AUTOLOAD_DUMP => 'preAutoloadDump', |
104 | 126 | ScriptEvents::POST_UPDATE_CMD => 'postUpdate', |
105 | 127 | ]; |
106 | 128 | } |
@@ -334,6 +356,70 @@ public function getMissingRequires(InstalledRepositoryInterface $repo, array $re |
334 | 356 | return $missingRequires; |
335 | 357 | } |
336 | 358 |
|
| 359 | + public function preAutoloadDump(Event $event) |
| 360 | + { |
| 361 | + $filesystem = new Filesystem(); |
| 362 | + // Double realpath() on purpose, see https://bugs.php.net/72738 |
| 363 | + $vendorDir = $filesystem->normalizePath(realpath(realpath($event->getComposer()->getConfig()->get('vendor-dir')))); |
| 364 | + $filesystem->ensureDirectoryExists($vendorDir.'/composer'); |
| 365 | + $pinned = $event->getComposer()->getPackage()->getExtra()['discovery'] ?? []; |
| 366 | + $candidates = []; |
| 367 | + |
| 368 | + $allInterfaces = array_merge(...array_values(self::INTERFACE_MAP)); |
| 369 | + foreach ($pinned as $abstraction => $class) { |
| 370 | + if (isset(self::INTERFACE_MAP[$abstraction])) { |
| 371 | + $interfaces = self::INTERFACE_MAP[$abstraction]; |
| 372 | + } elseif (false !== $k = array_search($abstraction, $allInterfaces, true)) { |
| 373 | + $interfaces = [$allInterfaces[$k]]; |
| 374 | + } else { |
| 375 | + throw new \UnexpectedValueException(sprintf('Invalid "extra.discovery" pinned in composer.json: "%s" is not one of ["%s"].', $abstraction, implode('", "', array_keys(self::INTERFACE_MAP)))); |
| 376 | + } |
| 377 | + |
| 378 | + foreach ($interfaces as $interface) { |
| 379 | + $candidates[] = sprintf("case %s: return [['class' => %s]];\n", var_export($interface, true), var_export($class, true)); |
| 380 | + } |
| 381 | + } |
| 382 | + |
| 383 | + $file = $vendorDir.'/composer/GeneratedDiscoveryStrategy.php'; |
| 384 | + |
| 385 | + if (!$candidates) { |
| 386 | + if (file_exists($file)) { |
| 387 | + unlink($file); |
| 388 | + } |
| 389 | + |
| 390 | + return; |
| 391 | + } |
| 392 | + |
| 393 | + $candidates = implode(' ', $candidates); |
| 394 | + $code = <<<EOPHP |
| 395 | +<?php |
| 396 | +
|
| 397 | +namespace Http\Discovery\Strategy; |
| 398 | +
|
| 399 | +class GeneratedDiscoveryStrategy implements DiscoveryStrategy |
| 400 | +{ |
| 401 | + public static function getCandidates(\$type) |
| 402 | + { |
| 403 | + switch (\$type) { |
| 404 | + $candidates |
| 405 | + default: return []; |
| 406 | + } |
| 407 | + } |
| 408 | +} |
| 409 | +
|
| 410 | +EOPHP |
| 411 | + ; |
| 412 | + |
| 413 | + if (!file_exists($file) || $code !== file_get_contents($file)) { |
| 414 | + file_put_contents($file, $code); |
| 415 | + } |
| 416 | + |
| 417 | + $rootPackage = $event->getComposer()->getPackage(); |
| 418 | + $autoload = $rootPackage->getAutoload(); |
| 419 | + $autoload['classmap'][] = $vendorDir.'/composer/GeneratedDiscoveryStrategy.php'; |
| 420 | + $rootPackage->setAutoload($autoload); |
| 421 | + } |
| 422 | + |
337 | 423 | private function updateComposerJson(array $missingRequires, bool $sortPackages) |
338 | 424 | { |
339 | 425 | $file = Factory::getComposerFile(); |
|
0 commit comments