annotate core/lib/Drupal/Core/Composer/Composer.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 1fec387a4317
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Core\Composer;
Chris@0 4
Chris@0 5 use Drupal\Component\PhpStorage\FileStorage;
Chris@0 6 use Composer\Script\Event;
Chris@0 7 use Composer\Installer\PackageEvent;
Chris@0 8 use Composer\Semver\Constraint\Constraint;
Chris@0 9
Chris@0 10 /**
Chris@0 11 * Provides static functions for composer script events.
Chris@0 12 *
Chris@0 13 * @see https://getcomposer.org/doc/articles/scripts.md
Chris@0 14 */
Chris@0 15 class Composer {
Chris@0 16
Chris@0 17 protected static $packageToCleanup = [
Chris@0 18 'behat/mink' => ['tests', 'driver-testsuite'],
Chris@0 19 'behat/mink-browserkit-driver' => ['tests'],
Chris@0 20 'behat/mink-goutte-driver' => ['tests'],
Chris@0 21 'drupal/coder' => ['coder_sniffer/Drupal/Test', 'coder_sniffer/DrupalPractice/Test'],
Chris@0 22 'doctrine/cache' => ['tests'],
Chris@0 23 'doctrine/collections' => ['tests'],
Chris@0 24 'doctrine/common' => ['tests'],
Chris@0 25 'doctrine/inflector' => ['tests'],
Chris@0 26 'doctrine/instantiator' => ['tests'],
Chris@0 27 'egulias/email-validator' => ['documentation', 'tests'],
Chris@0 28 'fabpot/goutte' => ['Goutte/Tests'],
Chris@0 29 'guzzlehttp/promises' => ['tests'],
Chris@0 30 'guzzlehttp/psr7' => ['tests'],
Chris@0 31 'jcalderonzumba/gastonjs' => ['docs', 'examples', 'tests'],
Chris@0 32 'jcalderonzumba/mink-phantomjs-driver' => ['tests'],
Chris@0 33 'masterminds/html5' => ['test'],
Chris@0 34 'mikey179/vfsStream' => ['src/test'],
Chris@0 35 'paragonie/random_compat' => ['tests'],
Chris@0 36 'phpdocumentor/reflection-docblock' => ['tests'],
Chris@0 37 'phpunit/php-code-coverage' => ['tests'],
Chris@0 38 'phpunit/php-timer' => ['tests'],
Chris@0 39 'phpunit/php-token-stream' => ['tests'],
Chris@0 40 'phpunit/phpunit' => ['tests'],
Chris@0 41 'phpunit/php-mock-objects' => ['tests'],
Chris@0 42 'sebastian/comparator' => ['tests'],
Chris@0 43 'sebastian/diff' => ['tests'],
Chris@0 44 'sebastian/environment' => ['tests'],
Chris@0 45 'sebastian/exporter' => ['tests'],
Chris@0 46 'sebastian/global-state' => ['tests'],
Chris@0 47 'sebastian/recursion-context' => ['tests'],
Chris@0 48 'stack/builder' => ['tests'],
Chris@0 49 'symfony/browser-kit' => ['Tests'],
Chris@0 50 'symfony/class-loader' => ['Tests'],
Chris@0 51 'symfony/console' => ['Tests'],
Chris@0 52 'symfony/css-selector' => ['Tests'],
Chris@0 53 'symfony/debug' => ['Tests'],
Chris@0 54 'symfony/dependency-injection' => ['Tests'],
Chris@0 55 'symfony/dom-crawler' => ['Tests'],
Chris@0 56 // @see \Drupal\Tests\Component\EventDispatcher\ContainerAwareEventDispatcherTest
Chris@0 57 // 'symfony/event-dispatcher' => ['Tests'],
Chris@0 58 'symfony/http-foundation' => ['Tests'],
Chris@0 59 'symfony/http-kernel' => ['Tests'],
Chris@0 60 'symfony/process' => ['Tests'],
Chris@0 61 'symfony/psr-http-message-bridge' => ['Tests'],
Chris@0 62 'symfony/routing' => ['Tests'],
Chris@0 63 'symfony/serializer' => ['Tests'],
Chris@0 64 'symfony/translation' => ['Tests'],
Chris@0 65 'symfony/validator' => ['Tests', 'Resources'],
Chris@0 66 'symfony/yaml' => ['Tests'],
Chris@0 67 'symfony-cmf/routing' => ['Test', 'Tests'],
Chris@0 68 'twig/twig' => ['doc', 'ext', 'test'],
Chris@0 69 ];
Chris@0 70
Chris@0 71 /**
Chris@0 72 * Add vendor classes to Composer's static classmap.
Chris@0 73 */
Chris@0 74 public static function preAutoloadDump(Event $event) {
Chris@0 75 // Get the configured vendor directory.
Chris@0 76 $vendor_dir = $event->getComposer()->getConfig()->get('vendor-dir');
Chris@0 77
Chris@0 78 // We need the root package so we can add our classmaps to its loader.
Chris@0 79 $package = $event->getComposer()->getPackage();
Chris@0 80 // We need the local repository so that we can query and see if it's likely
Chris@0 81 // that our files are present there.
Chris@0 82 $repository = $event->getComposer()->getRepositoryManager()->getLocalRepository();
Chris@0 83 // This is, essentially, a null constraint. We only care whether the package
Chris@0 84 // is present in the vendor directory yet, but findPackage() requires it.
Chris@0 85 $constraint = new Constraint('>', '');
Chris@0 86 // It's possible that there is no classmap specified in a custom project
Chris@0 87 // composer.json file. We need one so we can optimize lookup for some of our
Chris@0 88 // dependencies.
Chris@0 89 $autoload = $package->getAutoload();
Chris@0 90 if (!isset($autoload['classmap'])) {
Chris@0 91 $autoload['classmap'] = [];
Chris@0 92 }
Chris@0 93 // Check for our packages, and then optimize them if they're present.
Chris@0 94 if ($repository->findPackage('symfony/http-foundation', $constraint)) {
Chris@0 95 $autoload['classmap'] = array_merge($autoload['classmap'], [
Chris@0 96 $vendor_dir . '/symfony/http-foundation/Request.php',
Chris@0 97 $vendor_dir . '/symfony/http-foundation/ParameterBag.php',
Chris@0 98 $vendor_dir . '/symfony/http-foundation/FileBag.php',
Chris@0 99 $vendor_dir . '/symfony/http-foundation/ServerBag.php',
Chris@0 100 $vendor_dir . '/symfony/http-foundation/HeaderBag.php',
Chris@0 101 ]);
Chris@0 102 }
Chris@0 103 if ($repository->findPackage('symfony/http-kernel', $constraint)) {
Chris@0 104 $autoload['classmap'] = array_merge($autoload['classmap'], [
Chris@0 105 $vendor_dir . '/symfony/http-kernel/HttpKernel.php',
Chris@0 106 $vendor_dir . '/symfony/http-kernel/HttpKernelInterface.php',
Chris@0 107 $vendor_dir . '/symfony/http-kernel/TerminableInterface.php',
Chris@0 108 ]);
Chris@0 109 }
Chris@0 110 $package->setAutoload($autoload);
Chris@0 111 }
Chris@0 112
Chris@0 113 /**
Chris@0 114 * Ensures that .htaccess and web.config files are present in Composer root.
Chris@0 115 *
Chris@0 116 * @param \Composer\Script\Event $event
Chris@0 117 */
Chris@0 118 public static function ensureHtaccess(Event $event) {
Chris@0 119
Chris@0 120 // The current working directory for composer scripts is where you run
Chris@0 121 // composer from.
Chris@0 122 $vendor_dir = $event->getComposer()->getConfig()->get('vendor-dir');
Chris@0 123
Chris@0 124 // Prevent access to vendor directory on Apache servers.
Chris@0 125 $htaccess_file = $vendor_dir . '/.htaccess';
Chris@0 126 if (!file_exists($htaccess_file)) {
Chris@0 127 file_put_contents($htaccess_file, FileStorage::htaccessLines(TRUE) . "\n");
Chris@0 128 }
Chris@0 129
Chris@0 130 // Prevent access to vendor directory on IIS servers.
Chris@0 131 $webconfig_file = $vendor_dir . '/web.config';
Chris@0 132 if (!file_exists($webconfig_file)) {
Chris@0 133 $lines = <<<EOT
Chris@0 134 <configuration>
Chris@0 135 <system.webServer>
Chris@0 136 <authorization>
Chris@0 137 <deny users="*">
Chris@0 138 </authorization>
Chris@0 139 </system.webServer>
Chris@0 140 </configuration>
Chris@0 141 EOT;
Chris@0 142 file_put_contents($webconfig_file, $lines . "\n");
Chris@0 143 }
Chris@0 144 }
Chris@0 145
Chris@0 146 /**
Chris@0 147 * Remove possibly problematic test files from vendored projects.
Chris@0 148 *
Chris@0 149 * @param \Composer\Installer\PackageEvent $event
Chris@0 150 * A PackageEvent object to get the configured composer vendor directories
Chris@0 151 * from.
Chris@0 152 */
Chris@0 153 public static function vendorTestCodeCleanup(PackageEvent $event) {
Chris@0 154 $vendor_dir = $event->getComposer()->getConfig()->get('vendor-dir');
Chris@0 155 $io = $event->getIO();
Chris@0 156 $op = $event->getOperation();
Chris@0 157 if ($op->getJobType() == 'update') {
Chris@0 158 $package = $op->getTargetPackage();
Chris@0 159 }
Chris@0 160 else {
Chris@0 161 $package = $op->getPackage();
Chris@0 162 }
Chris@0 163 $package_key = static::findPackageKey($package->getName());
Chris@0 164 $message = sprintf(" Processing <comment>%s</comment>", $package->getPrettyName());
Chris@0 165 if ($io->isVeryVerbose()) {
Chris@0 166 $io->write($message);
Chris@0 167 }
Chris@0 168 if ($package_key) {
Chris@0 169 foreach (static::$packageToCleanup[$package_key] as $path) {
Chris@0 170 $dir_to_remove = $vendor_dir . '/' . $package_key . '/' . $path;
Chris@0 171 $print_message = $io->isVeryVerbose();
Chris@0 172 if (is_dir($dir_to_remove)) {
Chris@0 173 if (static::deleteRecursive($dir_to_remove)) {
Chris@0 174 $message = sprintf(" <info>Removing directory '%s'</info>", $path);
Chris@0 175 }
Chris@0 176 else {
Chris@0 177 // Always display a message if this fails as it means something has
Chris@0 178 // gone wrong. Therefore the message has to include the package name
Chris@0 179 // as the first informational message might not exist.
Chris@0 180 $print_message = TRUE;
Chris@0 181 $message = sprintf(" <error>Failure removing directory '%s'</error> in package <comment>%s</comment>.", $path, $package->getPrettyName());
Chris@0 182 }
Chris@0 183 }
Chris@0 184 else {
Chris@0 185 // If the package has changed or the --prefer-dist version does not
Chris@0 186 // include the directory this is not an error.
Chris@0 187 $message = sprintf(" Directory '%s' does not exist", $path);
Chris@0 188 }
Chris@0 189 if ($print_message) {
Chris@0 190 $io->write($message);
Chris@0 191 }
Chris@0 192 }
Chris@0 193
Chris@0 194 if ($io->isVeryVerbose()) {
Chris@0 195 // Add a new line to separate this output from the next package.
Chris@0 196 $io->write("");
Chris@0 197 }
Chris@0 198 }
Chris@0 199 }
Chris@0 200
Chris@0 201 /**
Chris@0 202 * Find the array key for a given package name with a case-insensitive search.
Chris@0 203 *
Chris@0 204 * @param string $package_name
Chris@0 205 * The package name from composer. This is always already lower case.
Chris@0 206 *
Chris@0 207 * @return string|null
Chris@0 208 * The string key, or NULL if none was found.
Chris@0 209 */
Chris@0 210 protected static function findPackageKey($package_name) {
Chris@0 211 $package_key = NULL;
Chris@0 212 // In most cases the package name is already used as the array key.
Chris@0 213 if (isset(static::$packageToCleanup[$package_name])) {
Chris@0 214 $package_key = $package_name;
Chris@0 215 }
Chris@0 216 else {
Chris@0 217 // Handle any mismatch in case between the package name and array key.
Chris@0 218 // For example, the array key 'mikey179/vfsStream' needs to be found
Chris@0 219 // when composer returns a package name of 'mikey179/vfsstream'.
Chris@0 220 foreach (static::$packageToCleanup as $key => $dirs) {
Chris@0 221 if (strtolower($key) === $package_name) {
Chris@0 222 $package_key = $key;
Chris@0 223 break;
Chris@0 224 }
Chris@0 225 }
Chris@0 226 }
Chris@0 227 return $package_key;
Chris@0 228 }
Chris@0 229
Chris@0 230 /**
Chris@0 231 * Helper method to remove directories and the files they contain.
Chris@0 232 *
Chris@0 233 * @param string $path
Chris@0 234 * The directory or file to remove. It must exist.
Chris@0 235 *
Chris@0 236 * @return bool
Chris@0 237 * TRUE on success or FALSE on failure.
Chris@0 238 */
Chris@0 239 protected static function deleteRecursive($path) {
Chris@0 240 if (is_file($path) || is_link($path)) {
Chris@0 241 return unlink($path);
Chris@0 242 }
Chris@0 243 $success = TRUE;
Chris@0 244 $dir = dir($path);
Chris@0 245 while (($entry = $dir->read()) !== FALSE) {
Chris@0 246 if ($entry == '.' || $entry == '..') {
Chris@0 247 continue;
Chris@0 248 }
Chris@0 249 $entry_path = $path . '/' . $entry;
Chris@0 250 $success = static::deleteRecursive($entry_path) && $success;
Chris@0 251 }
Chris@0 252 $dir->close();
Chris@0 253
Chris@0 254 return rmdir($path) && $success;
Chris@0 255 }
Chris@0 256
Chris@0 257 }