From 7917a7e7579c9c96916398da7d9736c65420fb64 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 13 Oct 2020 10:28:36 +0200 Subject: [PATCH 1/4] Allow fetching the http downloader and process executor from the loop --- src/Composer/Util/Loop.php | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/Composer/Util/Loop.php b/src/Composer/Util/Loop.php index 00159d562..bed9151a9 100644 --- a/src/Composer/Util/Loop.php +++ b/src/Composer/Util/Loop.php @@ -21,22 +21,40 @@ use Symfony\Component\Console\Helper\ProgressBar; */ class Loop { + /** @var HttpDownloader */ private $httpDownloader; + /** @var ProcessExecutor|null */ private $processExecutor; + /** @var Promise[]|null */ private $currentPromises; - public function __construct(HttpDownloader $httpDownloader = null, ProcessExecutor $processExecutor = null) + public function __construct(HttpDownloader $httpDownloader, ProcessExecutor $processExecutor = null) { $this->httpDownloader = $httpDownloader; - if ($this->httpDownloader) { - $this->httpDownloader->enableAsync(); - } + $this->httpDownloader->enableAsync(); + $this->processExecutor = $processExecutor; if ($this->processExecutor) { $this->processExecutor->enableAsync(); } } + /** + * @return HttpDownloader + */ + public function getHttpDownloader() + { + return $this->httpDownloader; + } + + /** + * @return ProcessExecutor|null + */ + public function getProcessExecutor() + { + return $this->processExecutor; + } + public function wait(array $promises, ProgressBar $progress = null) { /** @var \Exception|null */ From 431dc0d52634a11af3df6c3b49ab84d42442ad48 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 13 Oct 2020 10:54:20 +0200 Subject: [PATCH 2/4] Add sync helper to give plugins utilities to work with async stuff more easily when one does not care about async --- src/Composer/Factory.php | 2 ++ src/Composer/Util/SyncHelper.php | 54 ++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 src/Composer/Util/SyncHelper.php diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 5d7790d2f..3862ee78d 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -591,6 +591,8 @@ class Factory } /** + * If you are calling this in a plugin, you probably should instead use $composer->getLoop()->getHttpDownloader() + * * @param IOInterface $io IO instance * @param Config $config Config instance * @param array $options Array of options passed directly to HttpDownloader constructor diff --git a/src/Composer/Util/SyncHelper.php b/src/Composer/Util/SyncHelper.php new file mode 100644 index 000000000..9b230781f --- /dev/null +++ b/src/Composer/Util/SyncHelper.php @@ -0,0 +1,54 @@ +getLoop() + * @param string $path the installation path + * @param PackageInterface|null $prevPackage the previous package if this is an update and not an initial installation + */ + public static function downloadAndInstallPackageSync(Loop $loop, DownloaderInterface $downloader, $path, PackageInterface $package, PackageInterface $prevPackage = null) + { + $type = $prevPackage ? 'update' : 'install'; + + try { + self::await($loop, $downloader->download($package, $path, $prevPackage)); + + self::await($loop, $downloader->prepare($type, $package, $path, $prevPackage)); + + if ($type === 'update') { + self::await($loop, $downloader->update($package, $path, $prevPackage)); + } else { + self::await($loop, $downloader->install($package, $path, $prevPackage)); + } + } catch (\Exception $e) { + self::await($loop, $downloader->cleanup($type, $package, $path, $prevPackage)); + throw $e; + } + + self::await($loop, $downloader->cleanup($type, $package, $path, $prevPackage)); + } + + /** + * Waits for a promise to resolve + * + * @param Loop $loop Loop instance which you can get from $composer->getLoop() + * @param PromiseInterface|null $promise + */ + public static function await(Loop $loop, PromiseInterface $promise = null) + { + if ($promise) { + $loop->wait(array($promise)); + } + } +} From 31cf6788e609c19c471b3c4f550a7302c187d6e6 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 13 Oct 2020 11:05:37 +0200 Subject: [PATCH 3/4] Improve plugin docs --- doc/articles/plugins.md | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/doc/articles/plugins.md b/doc/articles/plugins.md index 28b7c1475..97ebd67e1 100644 --- a/doc/articles/plugins.md +++ b/doc/articles/plugins.md @@ -161,6 +161,14 @@ class AwsPlugin implements PluginInterface, EventSubscriberInterface $this->io = $io; } + public function deactivate(Composer $composer, IOInterface $io) + { + } + + public function uninstall(Composer $composer, IOInterface $io) + { + } + public static function getSubscribedEvents() { return array( @@ -175,9 +183,7 @@ class AwsPlugin implements PluginInterface, EventSubscriberInterface $protocol = parse_url($event->getProcessedUrl(), PHP_URL_SCHEME); if ($protocol === 's3') { - $awsClient = new AwsClient($this->io, $this->composer->getConfig()); - $s3Downloader = new S3Downloader($this->io, $event->getHttpDownloader()->getOptions(), $awsClient); - $event->setHttpdownloader($s3Downloader); + // ... } } } @@ -263,7 +269,7 @@ Now the `custom-plugin-command` is available alongside Composer commands. ## Running plugins manually -Plugins for an event can be run manually by the `run-script` command. This works the same way as +Plugins for an event can be run manually by the `run-script` command. This works the same way as [running scripts manually](scripts.md#running-scripts-manually). ## Using Plugins @@ -278,6 +284,12 @@ local project plugins are loaded. > installed plugins. This may be particularly helpful if any of the plugins > causes errors and you wish to update or uninstall it. +## Plugin Helpers + +As of Composer 2, due to the fact that DownloaderInterface can sometimes return Promises +and have been split up in more steps than they used to, we provide a [SyncHelper][11] +to make downloading and installing packages easier. + [1]: ../04-schema.md#type [2]: ../04-schema.md#extra [3]: https://github.com/composer/composer/blob/master/src/Composer/Plugin/PluginInterface.php @@ -288,3 +300,4 @@ local project plugins are loaded. [8]: https://github.com/composer/composer/blob/master/src/Composer/Plugin/Capable.php [9]: https://github.com/composer/composer/blob/master/src/Composer/Plugin/Capability/CommandProvider.php [10]: https://symfony.com/doc/current/components/console.html +[11]: https://github.com/composer/composer/blob/master/src/Composer/Util/SyncHelper.php From f87b1642bc98cbfc04058ae297c979acec980df1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 13 Oct 2020 11:09:13 +0200 Subject: [PATCH 4/4] Expand docs --- src/Composer/Util/SyncHelper.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Composer/Util/SyncHelper.php b/src/Composer/Util/SyncHelper.php index 9b230781f..964a78cd1 100644 --- a/src/Composer/Util/SyncHelper.php +++ b/src/Composer/Util/SyncHelper.php @@ -14,7 +14,9 @@ class SyncHelper * This executes all the required steps and waits for promises to complete * * @param Loop $loop Loop instance which you can get from $composer->getLoop() - * @param string $path the installation path + * @param DownloaderInterface $downloader Downloader instance you can get from $composer->getDownloadManager()->getDownloader('zip') for example + * @param string $path the installation path for the package + * @param PackageInterface $package the package to install * @param PackageInterface|null $prevPackage the previous package if this is an update and not an initial installation */ public static function downloadAndInstallPackageSync(Loop $loop, DownloaderInterface $downloader, $path, PackageInterface $package, PackageInterface $prevPackage = null)