diff --git a/core/scripts/composer/scaffold/.gitignore b/core/scripts/composer/scaffold/.gitignore index de4a392c33..8b7ef35032 100644 --- a/core/scripts/composer/scaffold/.gitignore +++ b/core/scripts/composer/scaffold/.gitignore @@ -1,2 +1,2 @@ /vendor -/composer.lock +composer.lock diff --git a/core/scripts/composer/scaffold/README.md b/core/scripts/composer/scaffold/README.md index a8e0a76b23..3ead55067f 100644 --- a/core/scripts/composer/scaffold/README.md +++ b/core/scripts/composer/scaffold/README.md @@ -1,18 +1,18 @@ # drupal-scaffold Composer plugin for automatically downloading Drupal scaffold files (like -`index.php`, `update.php`, …) when using `drupal/core` via Composer. +`index.php`, `update.php`, etc) when requiring `drupal/core` via Composer. It is recommended that the vendor directory be placed in its standard location at the project root, outside of the Drupal root; however, the location of the -vendor directory and the name of the Drupal root may be placed in whatever +vendor directory and the Drupal root may be placed in whatever location suits the project. Drupal-scaffold will generate the autoload.php file at the Drupal root to require the Composer-generated autoload file in the vendor directory. ## Usage -Run `composer require drupal/drupal-scaffold` in your composer +Run `composer require drupal/drupal-scaffold` in your Composer project before installing or updating `drupal/core`. Once drupal-scaffold is required by your project, it will automatically update @@ -21,7 +21,7 @@ your scaffold files whenever `composer update` changes the version of ## Configuration -You can configure the plugin with providing some settings in the `extra` section +You can configure the plugin by providing some settings in the `extra` section of your root `composer.json`. ```json @@ -46,7 +46,7 @@ of your root `composer.json`. } ``` The `source` option may be used to specify the URL to download the -scaffold files from; the default source is drupal.org. The literal string +scaffold files from; the default source is cgit.drupalcode.org. The literal string `{version}` in the `source` option is replaced with the current version of Drupal core being updated prior to download. @@ -123,5 +123,4 @@ or "@composer drupal:scaffold" in Composer Scripts. It is assumed that the scaffold files will be committed to the repository, to ensure that the correct files are used on the CI server (see **Limitation**, -above). After running `composer install` for the first time commit the scaffold -files to your repository. +above). Commit the scaffold files to your repository after running `composer install` for the first time. diff --git a/core/scripts/composer/scaffold/composer.json b/core/scripts/composer/scaffold/composer.json index 907d736253..c758f5b016 100644 --- a/core/scripts/composer/scaffold/composer.json +++ b/core/scripts/composer/scaffold/composer.json @@ -1,10 +1,10 @@ { "name": "drupal/drupal-scaffold", - "description": "Composer Plugin for updating the Drupal scaffold files when using drupal/core", + "description": "New Composer plugin for updating the Drupal scaffold files when using drupal/core", "type": "composer-plugin", "license": "GPL-2.0-or-later", "require": { - "php": ">=5.4.5", + "php": ">=5.5.9", "composer-plugin-api": "^1.0.0" }, "autoload": { @@ -17,6 +17,7 @@ }, "require-dev": { "composer/composer": "^1.6.5", - "phpunit/phpunit": "^4.8.35 || ^6.5" + "phpunit/phpunit": "^4.8.35 || ^6.5", + "drupal/coder": "^8.2.12" } } diff --git a/core/scripts/composer/scaffold/src/CommandProvider.php b/core/scripts/composer/scaffold/src/CommandProvider.php index 327a7f229e..3e459cc84d 100644 --- a/core/scripts/composer/scaffold/src/CommandProvider.php +++ b/core/scripts/composer/scaffold/src/CommandProvider.php @@ -5,7 +5,7 @@ use Composer\Plugin\Capability\CommandProvider as CommandProviderCapability; /** - * List of all commands provided by this package. + * Lists composer commands provided by this package. */ class CommandProvider implements CommandProviderCapability { diff --git a/core/scripts/composer/scaffold/src/FileFetcher.php b/core/scripts/composer/scaffold/src/FileFetcher.php index 653e7f22f1..9eee39a422 100644 --- a/core/scripts/composer/scaffold/src/FileFetcher.php +++ b/core/scripts/composer/scaffold/src/FileFetcher.php @@ -7,44 +7,82 @@ use Composer\Util\RemoteFilesystem; /** - * Downloads all required files and writes it to the file system. + * Downloads all required files and writes them to the file system. */ class FileFetcher { /** + * Composer utility to retrieve remote files. + * * @var \Composer\Util\RemoteFilesystem */ protected $remoteFilesystem; /** + * The composer IO object for printing messages to the console. + * * @var \Composer\IO\IOInterface */ protected $io; /** - * @var bool - * * A boolean indicating if progress should be displayed. + * + * @var bool */ protected $progress; + /** + * The url pattern where scaffold files may be retrieved. + * + * @var string + */ protected $source; + + /** + * Array of filenames to fetch from the source repository. + * + * @var array + */ protected $filenames; + + /** + * Composer utility for working with the local filesystem. + * + * @var \Composer\Util\Filesystem + */ protected $fs; /** * Constructs this FileFetcher object. + * + * @param \Composer\Util\RemoteFilesystem $remoteFilesystem + * Composer utility to retrieve remote files. + * @param string $source + * The url pattern where scaffold files may be retrieved. + * @param \Composer\IO\IOInterface $io + * The composer IO object for printing messages to the console. + * @param bool $progress + * A boolean indicating if progress should be displayed. */ public function __construct(RemoteFilesystem $remoteFilesystem, $source, IOInterface $io, $progress = TRUE) { $this->remoteFilesystem = $remoteFilesystem; $this->io = $io; $this->source = $source; + // TODO: this should be injectable. $this->fs = new Filesystem(); $this->progress = $progress; } /** * Downloads all required files and writes it to the file system. + * + * @param string $version + * The version of the scaffold file to be retrieved. + * @param string $destination + * The location on the filesystem where we will place the file. + * @param bool $override + * Whether the file should be overridden or left in place. */ public function fetch($version, $destination, $override) { foreach ($this->filenames as $sourceFilename => $filename) { @@ -68,6 +106,9 @@ public function fetch($version, $destination, $override) { /** * Set filenames. + * + * @param array $filenames + * An array of filenames to retrieve from the drupal git repository. */ public function setFilenames(array $filenames) { $this->filenames = $filenames; @@ -75,6 +116,14 @@ public function setFilenames(array $filenames) { /** * Replace filename and version in the source pattern with their values. + * + * @param string $filename + * The filename to retrieve. + * @param string $version + * The version of the git drupal repository. + * + * @return string + * A uri of the filename/version combination. */ protected function getUri($filename, $version) { $map = [ diff --git a/core/scripts/composer/scaffold/src/Handler.php b/core/scripts/composer/scaffold/src/Handler.php index 2f076d3944..5f1fcb2bc1 100644 --- a/core/scripts/composer/scaffold/src/Handler.php +++ b/core/scripts/composer/scaffold/src/Handler.php @@ -2,6 +2,7 @@ namespace DrupalComposer\DrupalScaffold; +use Composer\DependencyResolver\Operation\OperationInterface; use Composer\Script\Event; use Composer\Installer\PackageEvent; use Composer\Plugin\CommandEvent; @@ -16,51 +17,132 @@ use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem; /** - * Core class of the plugin, contains all logic which files should be fetched. + * Primary class of the plugin that determines which files should be fetched. */ class Handler { + /** + * Defines a composer event that fires prior to building the scaffold. + */ const PRE_DRUPAL_SCAFFOLD_CMD = 'pre-drupal-scaffold-cmd'; + + /** + * Defines a composer event that fires after the scaffold is built. + */ const POST_DRUPAL_SCAFFOLD_CMD = 'post-drupal-scaffold-cmd'; /** + * The Composer application object. + * * @var \Composer\Composer */ protected $composer; /** + * The composer IO object for printing messages to the console. + * * @var \Composer\IO\IOInterface */ protected $io; /** - * @var bool - * * A boolean indicating if progress should be displayed. + * + * @var bool */ protected $progress; /** + * Composer package for drupal/core. + * * @var \Composer\Package\PackageInterface */ protected $drupalCorePackage; + /** + * Array of core's dependencies' unwanted folders. + * + * @var array + */ + protected static $packageToCleanup = [ + 'behat/mink' => ['tests', 'driver-testsuite'], + 'behat/mink-browserkit-driver' => ['tests'], + 'behat/mink-goutte-driver' => ['tests'], + 'drupal/coder' => ['coder_sniffer/Drupal/Test', 'coder_sniffer/DrupalPractice/Test'], + 'doctrine/cache' => ['tests'], + 'doctrine/collections' => ['tests'], + 'doctrine/common' => ['tests'], + 'doctrine/inflector' => ['tests'], + 'doctrine/instantiator' => ['tests'], + 'egulias/email-validator' => ['documentation', 'tests'], + 'fabpot/goutte' => ['Goutte/Tests'], + 'guzzlehttp/promises' => ['tests'], + 'guzzlehttp/psr7' => ['tests'], + 'jcalderonzumba/gastonjs' => ['docs', 'examples', 'tests'], + 'jcalderonzumba/mink-phantomjs-driver' => ['tests'], + 'masterminds/html5' => ['test'], + 'mikey179/vfsStream' => ['src/test'], + 'paragonie/random_compat' => ['tests'], + 'phpdocumentor/reflection-docblock' => ['tests'], + 'phpunit/php-code-coverage' => ['tests'], + 'phpunit/php-timer' => ['tests'], + 'phpunit/php-token-stream' => ['tests'], + 'phpunit/phpunit' => ['tests'], + 'phpunit/php-mock-objects' => ['tests'], + 'sebastian/comparator' => ['tests'], + 'sebastian/diff' => ['tests'], + 'sebastian/environment' => ['tests'], + 'sebastian/exporter' => ['tests'], + 'sebastian/global-state' => ['tests'], + 'sebastian/recursion-context' => ['tests'], + 'stack/builder' => ['tests'], + 'symfony/browser-kit' => ['Tests'], + 'symfony/class-loader' => ['Tests'], + 'symfony/console' => ['Tests'], + 'symfony/css-selector' => ['Tests'], + 'symfony/debug' => ['Tests'], + 'symfony/dependency-injection' => ['Tests'], + 'symfony/dom-crawler' => ['Tests'], + // @see \Drupal\Tests\Component\EventDispatcher\ContainerAwareEventDispatcherTest + // 'symfony/event-dispatcher' => ['Tests'], + 'symfony/http-foundation' => ['Tests'], + 'symfony/http-kernel' => ['Tests'], + 'symfony/process' => ['Tests'], + 'symfony/psr-http-message-bridge' => ['Tests'], + 'symfony/routing' => ['Tests'], + 'symfony/serializer' => ['Tests'], + 'symfony/translation' => ['Tests'], + 'symfony/validator' => ['Tests', 'Resources'], + 'symfony/yaml' => ['Tests'], + 'symfony-cmf/routing' => ['Test', 'Tests'], + 'twig/twig' => ['doc', 'ext', 'test'], + ]; + /** * Handler constructor. * * @param \Composer\Composer $composer + * The primary composer application object. * @param \Composer\IO\IOInterface $io + * The composer IO object for printing messages to the console. */ public function __construct(Composer $composer, IOInterface $io) { $this->composer = $composer; $this->io = $io; $this->progress = TRUE; - // Pre-load all of our sources so that we do not run up - // against problems in `composer update` operations. + // Pre-load all of the plugins classes so that when we update the + // drupal/drupal-scaffold plugin itself, we do not run into issues with + // api mismatches between versions. $this->manualLoad(); } + /** + * Pre-load all of our sources on initial load. + * + * This ensures that we will not get a more recent version of one of + * our classes e.g. after a 'composer update' operation. + */ protected function manualLoad() { $src_dir = __DIR__; @@ -79,10 +161,15 @@ protected function manualLoad() { } /** - * @param $operation + * Determines if drupal/core is being changed by the install or update. + * + * @param \Composer\DependencyResolver\Operation\OperationInterface $operation + * Determines what type of Composer package operation is occurring. + * * @return mixed + * Returns 'drupal/core' if core is being updated, otherwise returns null. */ - protected function getCorePackage($operation) { + protected function getCorePackage(OperationInterface $operation) { if ($operation instanceof InstallOperation) { $package = $operation->getPackage(); } @@ -99,6 +186,7 @@ protected function getCorePackage($operation) { * Get the command options. * * @param \Composer\Plugin\CommandEvent $event + * The Composer event that called this listener. */ public function onCmdBeginsEvent(CommandEvent $event) { if ($event->getInput()->hasOption('no-progress')) { @@ -110,9 +198,13 @@ public function onCmdBeginsEvent(CommandEvent $event) { } /** - * Marks scaffolding to be processed after an install or update command. + * Event handler that fires after an install or update command. + * + * Files to be processed are marked, and potentially problematic test files + * are removed from vendor packages. * * @param \Composer\Installer\PackageEvent $event + * The install or update event object from Composer. */ public function onPostPackageEvent(PackageEvent $event) { $package = $this->getCorePackage($event->getOperation()); @@ -121,12 +213,33 @@ public function onPostPackageEvent(PackageEvent $event) { // process the scaffolding automatically. $this->drupalCorePackage = $package; } + + $vendor_dir = $event->getComposer()->getConfig()->get('vendor-dir'); + $io = $event->getIO(); + + // Get target package if we're updating, package otherwise. + $operation = $event->getOperation(); + if ($operation->getJobType() == 'update') { + $package = $operation->getTargetPackage(); + } + else { + $package = $operation->getPackage(); + } + + // Get the case-adjusted package name, which is also the path to the package + // within the vendor directory. + $package_name = static::findPackageKey($package->getName()); + if ($package_name) { + $cleanup_paths = static::$packageToCleanup[$package_name]; + static::doTestCodeCleanup($vendor_dir, $package_name, $cleanup_paths, $io); + } } /** * Post install command event to execute the scaffolding. * * @param \Composer\Script\Event $event + * The Composer event. */ public function onPostCmdEvent(Event $event) { // Only install the scaffolding if drupal/core was installed, @@ -142,7 +255,7 @@ public function onPostCmdEvent(Event $event) { * Downloads drupal scaffold files for the current process. */ public function downloadScaffold() { - $drupalCorePackage = $this->getDrupalCorePackage(); + $drupal_core_package = $this->getDrupalCorePackage(); $webroot = realpath($this->getWebRoot()); // Collect options, excludes and settings files. @@ -153,7 +266,7 @@ public function downloadScaffold() { $dispatcher = new EventDispatcher($this->composer, $this->io); $dispatcher->dispatch(self::PRE_DRUPAL_SCAFFOLD_CMD); - $version = $this->getDrupalCoreVersion($drupalCorePackage); + $version = $this->getDrupalCoreVersion($drupal_core_package); $remoteFs = new RemoteFilesystem($this->io); @@ -169,30 +282,37 @@ public function downloadScaffold() { } /** - * Generate the autoload file at the project root. Include the - * autoload file that Composer generated. + * Generate the autoload file at the Drupal root. + * + * Include the autoload file that Composer generated. */ public function generateAutoload() { - $vendorPath = $this->getVendorPath(); + $vendor_path = $this->getVendorPath(); $webroot = $this->getWebRoot(); // Calculate the relative path from the webroot (location of the // project autoload.php) to the vendor directory. $fs = new SymfonyFilesystem(); - $relativeVendorPath = $fs->makePathRelative($vendorPath, realpath($webroot)); + $relative_vendor_path = $fs->makePathRelative($vendor_path, realpath($webroot)); - $fs->dumpFile($webroot . "/autoload.php", $this->autoLoadContents($relativeVendorPath)); + $fs->dumpFile($webroot . "/autoload.php", $this->autoLoadContents($relative_vendor_path)); } /** - * Build the contents of the autoload file. + * Build the contents of the docroot autoload file. + * + * This allows the vendor directory to live outside of the docroot. + * + * @param string $relativeVendorPath + * Path to the vendor directory, relative to the Drupal root. * * @return string + * The contents of the autoload.php for the docroot directory. */ protected function autoLoadContents($relativeVendorPath) { $relativeVendorPath = rtrim($relativeVendorPath, '/'); - $autoloadContents = <<composer->getConfig(); $filesystem = new Filesystem(); $filesystem->ensureDirectoryExists($config->get('vendor-dir')); - $vendorPath = $filesystem->normalizePath(realpath($config->get('vendor-dir'))); + $vendor_path = $filesystem->normalizePath(realpath($config->get('vendor-dir'))); - return $vendorPath; + return $vendor_path; } /** + * Returns the drupal core package object. + * * Look up the Drupal core package object, or return it from where we cached * it in the $drupalCorePackage field. * * @return \Composer\Package\PackageInterface + * Composer package for drupal/core. */ public function getDrupalCorePackage() { if (!isset($this->drupalCorePackage)) { @@ -243,8 +367,10 @@ public function getDrupalCorePackage() { * Returns the Drupal core version for the given package. * * @param \Composer\Package\PackageInterface $drupalCorePackage + * The Composer package for drupal/core. * * @return string + * The version number of drupal/core in this installation. */ protected function getDrupalCoreVersion(PackageInterface $drupalCorePackage) { $version = $drupalCorePackage->getPrettyVersion(); @@ -259,13 +385,14 @@ protected function getDrupalCoreVersion(PackageInterface $drupalCorePackage) { * Retrieve the path to the web root. * * @return string + * The webroot. Well, actually, the drupal root. */ public function getWebRoot() { - $drupalCorePackage = $this->getDrupalCorePackage(); - $installationManager = $this->composer->getInstallationManager(); - $corePath = $installationManager->getInstallPath($drupalCorePackage); + $drupal_core_package = $this->getDrupalCorePackage(); + $installation_manager = $this->composer->getInstallationManager(); + $core_path = $installation_manager->getInstallPath($drupal_core_package); // Webroot is the parent path of the drupal core installation path. - $webroot = dirname($corePath); + $webroot = dirname($core_path); return $webroot; } @@ -277,6 +404,7 @@ public function getWebRoot() { * Name of the package to get from the current composer installation. * * @return \Composer\Package\PackageInterface + * A Composer Package matching the given name. */ protected function getPackage($name) { return $this->composer->getRepositoryManager()->getLocalRepository()->findPackage($name, '*'); @@ -285,16 +413,18 @@ protected function getPackage($name) { /** * Retrieve excludes from optional "extra" configuration. * - * @return array + * @return string[] + * Any scaffold files that we would like to exclude from the default config. */ protected function getExcludes() { return $this->getNamedOptionList('excludes', 'getExcludesDefault'); } /** - * Retrieve list of additional settings files from optional "extra" configuration. + * Retrieve list of additional files from optional "extra" configuration. * - * @return array + * @return string[] + * Additional scaffold files we want to retrieve. */ protected function getIncludes() { return $this->getNamedOptionList('includes', 'getIncludesDefault'); @@ -303,21 +433,30 @@ protected function getIncludes() { /** * Retrieve list of initial files from optional "extra" configuration. * - * @return array + * @return string[] + * A list of files that should only be scaffolded on primary installation. */ protected function getInitial() { return $this->getNamedOptionList('initial', 'getInitialDefault'); } /** + * Gets a configuration value from extra config for drupal-scaffold. + * * Retrieve a named list of options from optional "extra" configuration. * Respects 'omit-defaults', and either includes or does not include the * default values, as requested. * - * @return array + * @param string $optionName + * The particular option value to retrieve. + * @param string $defaultFn + * Function to gather defaults from for this option. + * + * @return string[] + * Array of defined values in the extra config. */ protected function getNamedOptionList($optionName, $defaultFn) { - $options = $this->getOptions($this->composer); + $options = $this->getOptions(); $result = []; if (empty($options['omit-defaults'])) { $result = $this->$defaultFn(); @@ -330,7 +469,12 @@ protected function getNamedOptionList($optionName, $defaultFn) { /** * Retrieve excludes from optional "extra" configuration. * - * @return array + * The 'source' option is the url pattern where we locate the files, e.g. + * Github: https://raw.githubusercontent.com/drupal/drupal/{version}/{path} + * Drupal.org: https://cgit.drupalcode.org/drupal/plain/{path}?h={version} + * + * @return string[] + * An array of plugin configuration options. */ protected function getOptions() { $extra = $this->composer->getPackage()->getExtra() + ['drupal-scaffold' => []]; @@ -340,13 +484,15 @@ protected function getOptions() { 'includes' => [], 'initial' => [], 'source' => 'https://cgit.drupalcode.org/drupal/plain/{path}?h={version}', - // Github: https://raw.githubusercontent.com/drupal/drupal/{version}/{path} ]; return $options; } /** * Holds default excludes. + * + * @return string[] + * Nothing is excluded by default. */ protected function getExcludesDefault() { return []; @@ -354,6 +500,9 @@ protected function getExcludesDefault() { /** * Holds default settings files list. + * + * @return string[] + * The default scaffolding files that come with drupal. */ protected function getIncludesDefault() { @@ -390,9 +539,111 @@ protected function getIncludesDefault() { /** * Holds default initial files. + * + * @return string[] + * Empty array for no initial default. */ protected function getInitialDefault() { return []; } + /** + * Remove possibly problematic test files from a single vendor package. + * + * @param string $vendor_dir + * Full path to the vendor directory. + * @param string $package_path + * The package path within the vendor directory. Should also happen to be + * the package name. Example: psr/log. + * @param string[] $cleanup_paths + * An array of relative paths within the vendor path which should be + * removed. + * @param \Composer\IO\IOInterface $io + * IO object provided by Composer. + */ + protected static function doTestCodeCleanup($vendor_dir, $package_path, array $cleanup_paths, IOInterface $io) { + $package_dir = $vendor_dir . '/' . $package_path; + if (is_dir($package_dir)) { + $io->write(sprintf(" Test code cleanup for %s", $package_path), TRUE, $io::VERY_VERBOSE); + foreach ($cleanup_paths as $cleanup_path) { + $cleanup_dir = $package_dir . '/' . $cleanup_path; + if (is_dir($cleanup_dir)) { + // Try to clean up. + if (static::deleteRecursive($cleanup_dir)) { + $io->write(sprintf(" Removing directory '%s'", $cleanup_path), TRUE, $io::VERY_VERBOSE); + } + else { + // Always display a message if this fails as it means something + // has gone wrong. Therefore the message has to include the + // package name as the first informational message might not + // exist. + $io->write(sprintf(" Failure removing directory '%s' in package %s.", $cleanup_path, $package_path), TRUE, IOInterface::NORMAL); + } + } + else { + // If the package has changed or the --prefer-dist version does not + // include the directory this is not an error. + $io->write(sprintf(" Directory '%s' does not exist", $cleanup_dir), TRUE, $io::VERY_VERBOSE); + } + } + $io->write('', TRUE, $io::VERY_VERBOSE); + } + } + + /** + * Find the array key for a given package name with a case-insensitive search. + * + * @param string $package_name + * The package name from composer. This is always already lower case. + * + * @return string|null + * The string key, or NULL if none was found. + */ + protected static function findPackageKey($package_name) { + $package_key = NULL; + // In most cases the package name is already used as the array key. + if (isset(static::$packageToCleanup[$package_name])) { + $package_key = $package_name; + } + else { + // Handle any mismatch in case between the package name and array key. + // For example, the array key 'mikey179/vfsStream' needs to be found + // when composer returns a package name of 'mikey179/vfsstream'. + foreach (static::$packageToCleanup as $key => $dirs) { + if (strtolower($key) === $package_name) { + $package_key = $key; + break; + } + } + } + return $package_key; + } + + /** + * Helper method to remove directories and the files they contain. + * + * @param string $path + * The directory or file to remove. It must exist. + * + * @return bool + * TRUE on success or FALSE on failure. + */ + protected static function deleteRecursive($path) { + if (is_file($path) || is_link($path)) { + return unlink($path); + } + $success = TRUE; + $dir = dir($path); + while (($entry = $dir->read()) !== FALSE) { + if ($entry == '.' || $entry == '..') { + continue; + } + $entry_path = $path . '/' . $entry; + $success = static::deleteRecursive($entry_path) && $success; + } + $dir->close(); + + return rmdir($path) && $success; + } + } diff --git a/core/scripts/composer/scaffold/src/Plugin.php b/core/scripts/composer/scaffold/src/Plugin.php index 76677cb119..bc6d61fae7 100644 --- a/core/scripts/composer/scaffold/src/Plugin.php +++ b/core/scripts/composer/scaffold/src/Plugin.php @@ -20,6 +20,8 @@ class Plugin implements PluginInterface, EventSubscriberInterface, Capable { /** + * Handler class that does the actual processing of the scaffolding. + * * @var \DrupalComposer\DrupalScaffold\Handler */ protected $handler; @@ -52,6 +54,7 @@ public static function getSubscribedEvents() { PackageEvents::POST_PACKAGE_INSTALL => 'postPackage', PackageEvents::POST_PACKAGE_UPDATE => 'postPackage', ScriptEvents::POST_UPDATE_CMD => 'postCmd', + ScriptEvents::POST_CREATE_PROJECT_CMD => 'postCmd', PluginEvents::COMMAND => 'cmdBegins', ]; } @@ -60,6 +63,7 @@ public static function getSubscribedEvents() { * Command begins event callback. * * @param \Composer\Plugin\CommandEvent $event + * Composer event sent on command execution. */ public function cmdBegins(CommandEvent $event) { $this->handler->onCmdBeginsEvent($event); @@ -69,6 +73,7 @@ public function cmdBegins(CommandEvent $event) { * Post package event behaviour. * * @param \Composer\Installer\PackageEvent $event + * Composer package event sent on install/update/remove. */ public function postPackage(PackageEvent $event) { $this->handler->onPostPackageEvent($event); @@ -78,6 +83,7 @@ public function postPackage(PackageEvent $event) { * Post command event callback. * * @param \Composer\Script\Event $event + * Composer script event sent when commands are run. */ public function postCmd(Event $event) { $this->handler->onPostCmdEvent($event); diff --git a/core/scripts/composer/scaffold/src/PrestissimoFileFetcher.php b/core/scripts/composer/scaffold/src/PrestissimoFileFetcher.php index bd7ec1e63c..1d2cf6ec19 100644 --- a/core/scripts/composer/scaffold/src/PrestissimoFileFetcher.php +++ b/core/scripts/composer/scaffold/src/PrestissimoFileFetcher.php @@ -9,18 +9,33 @@ use Hirak\Prestissimo\CurlMulti; /** - * Extends the default FileFetcher and uses hirak/prestissimo for parallel - * downloads. + * Extends the default FileFetcher for parallel downloads. + * + * If hirak/prestissimo is available, it will use, otherwise it will default to + * FileFetcher default behavior. */ class PrestissimoFileFetcher extends FileFetcher { /** + * Composer configuration data. + * * @var \Composer\Config */ protected $config; /** * Constructs this PrestissimoFileFetcher object. + * + * @param \Composer\Util\RemoteFilesystem $remoteFilesystem + * Composer utility to retrieve remote files. + * @param string $source + * The url pattern where scaffold files may be retrieved. + * @param \Composer\IO\IOInterface $io + * The composer IO object for printing messages to the console. + * @param bool $progress + * A boolean indicating if progress should be displayed. + * @param \Composer\Config $config + * Composer config. */ public function __construct(RemoteFilesystem $remoteFilesystem, $source, IOInterface $io, $progress = TRUE, Config $config) { parent::__construct($remoteFilesystem, $source, $io, $progress); @@ -40,6 +55,13 @@ public function fetch($version, $destination, $override) { /** * Fetch files in parallel. + * + * @param string $version + * The version of the scaffold file to be retrieved. + * @param string $destination + * The location on the filesystem where we will place the file. + * @param bool $override + * Whether the file should be overridden or left in place. */ protected function fetchWithPrestissimo($version, $destination, $override) { $requests = [];