71307322be9be7724761670a6d12ddcb14fa2e12
[yaffs-website] / vendor / drupal-composer / drupal-scaffold / src / Handler.php
1 <?php
2
3 /**
4  * @file
5  * Contains \DrupalComposer\DrupalScaffold\Handler.
6  */
7
8 namespace DrupalComposer\DrupalScaffold;
9
10 use Composer\Composer;
11 use Composer\DependencyResolver\Operation\InstallOperation;
12 use Composer\DependencyResolver\Operation\UpdateOperation;
13 use Composer\EventDispatcher\EventDispatcher;
14 use Composer\IO\IOInterface;
15 use Composer\Package\PackageInterface;
16 use Composer\Semver\Semver;
17 use Composer\Util\Filesystem;
18 use Composer\Util\RemoteFilesystem;
19 use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem;
20
21 class Handler {
22
23   const PRE_DRUPAL_SCAFFOLD_CMD = 'pre-drupal-scaffold-cmd';
24   const POST_DRUPAL_SCAFFOLD_CMD = 'post-drupal-scaffold-cmd';
25
26   /**
27    * @var \Composer\Composer
28    */
29   protected $composer;
30
31   /**
32    * @var \Composer\IO\IOInterface
33    */
34   protected $io;
35
36   /**
37    * @var \Composer\Package\PackageInterface
38    */
39   protected $drupalCorePackage;
40
41   /**
42    * Handler constructor.
43    *
44    * @param Composer $composer
45    * @param IOInterface $io
46    */
47   public function __construct(Composer $composer, IOInterface $io) {
48     $this->composer = $composer;
49     $this->io = $io;
50   }
51
52   /**
53    * @param $operation
54    * @return mixed
55    */
56   protected function getCorePackage($operation) {
57     if ($operation instanceof InstallOperation) {
58       $package = $operation->getPackage();
59     }
60     elseif ($operation instanceof UpdateOperation) {
61       $package = $operation->getTargetPackage();
62     }
63     if (isset($package) && $package instanceof PackageInterface && $package->getName() == 'drupal/core') {
64       return $package;
65     }
66     return NULL;
67   }
68
69   /**
70    * Marks scaffolding to be processed after an install or update command.
71    *
72    * @param \Composer\Installer\PackageEvent $event
73    */
74   public function onPostPackageEvent(\Composer\Installer\PackageEvent $event){
75     $package = $this->getCorePackage($event->getOperation());
76     if ($package) {
77       // By explicitly setting the core package, the onPostCmdEvent() will
78       // process the scaffolding automatically.
79       $this->drupalCorePackage = $package;
80     }
81   }
82
83   /**
84    * Post install command event to execute the scaffolding.
85    *
86    * @param \Composer\Script\Event $event
87    */
88   public function onPostCmdEvent(\Composer\Script\Event $event) {
89     // Only install the scaffolding if drupal/core was installed,
90     // AND there are no scaffolding files present.
91     if (isset($this->drupalCorePackage)) {
92       $this->downloadScaffold();
93       // Generate the autoload.php file after generating the scaffold files.
94       $this->generateAutoload();
95     }
96   }
97
98   /**
99    * Downloads drupal scaffold files for the current process.
100    */
101   public function downloadScaffold() {
102     $drupalCorePackage = $this->getDrupalCorePackage();
103     $webroot = realpath($this->getWebRoot());
104
105     // Collect options, excludes and settings files.
106     $options = $this->getOptions();
107     $files = array_diff($this->getIncludes(), $this->getExcludes());
108
109     // Call any pre-scaffold scripts that may be defined.
110     $dispatcher = new EventDispatcher($this->composer, $this->io);
111     $dispatcher->dispatch(self::PRE_DRUPAL_SCAFFOLD_CMD);
112
113     $version = $this->getDrupalCoreVersion($drupalCorePackage);
114
115     $remoteFs = new RemoteFilesystem($this->io);
116
117     $fetcher = new PrestissimoFileFetcher($remoteFs, $options['source'], $files, $this->io, $this->composer->getConfig());
118     $fetcher->fetch($version, $webroot);
119
120     $initialFileFetcher = new InitialFileFetcher($remoteFs, $options['source'], $this->getInitial());
121     $initialFileFetcher->fetch($version, $webroot);
122
123     // Call post-scaffold scripts.
124     $dispatcher->dispatch(self::POST_DRUPAL_SCAFFOLD_CMD);
125   }
126
127   /**
128    * Generate the autoload file at the project root.  Include the
129    * autoload file that Composer generated.
130    */
131   public function generateAutoload() {
132     $vendorPath = $this->getVendorPath();
133     $webroot = $this->getWebRoot();
134
135     // Calculate the relative path from the webroot (location of the
136     // project autoload.php) to the vendor directory.
137     $fs = new SymfonyFilesystem();
138     $relativeVendorPath = $fs->makePathRelative($vendorPath, realpath($webroot));
139
140     $fs->dumpFile($webroot . "/autoload.php", $this->autoLoadContents($relativeVendorPath));
141   }
142
143   /**
144    * Build the contents of the autoload file.
145    *
146    * @return string
147    */
148   protected function autoLoadContents($relativeVendorPath) {
149     $relativeVendorPath = rtrim($relativeVendorPath, '/');
150
151     $autoloadContents = <<<EOF
152 <?php
153
154 /**
155  * @file
156  * Includes the autoloader created by Composer.
157  *
158  * This file was generated by drupal-composer/drupal-scaffold.
159  * https://github.com/drupal-composer/drupal-scaffold
160  *
161  * @see composer.json
162  * @see index.php
163  * @see core/install.php
164  * @see core/rebuild.php
165  * @see core/modules/statistics/statistics.php
166  */
167
168 return require __DIR__ . '/$relativeVendorPath/autoload.php';
169
170 EOF;
171     return $autoloadContents;
172   }
173
174   /**
175    * Get the path to the 'vendor' directory.
176    *
177    * @return string
178    */
179   public function getVendorPath() {
180     $config = $this->composer->getConfig();
181     $filesystem = new Filesystem();
182     $filesystem->ensureDirectoryExists($config->get('vendor-dir'));
183     $vendorPath = $filesystem->normalizePath(realpath($config->get('vendor-dir')));
184
185     return $vendorPath;
186   }
187
188   /**
189    * Look up the Drupal core package object, or return it from where we cached
190    * it in the $drupalCorePackage field.
191    *
192    * @return PackageInterface
193    */
194   public function getDrupalCorePackage() {
195     if (!isset($this->drupalCorePackage)) {
196       $this->drupalCorePackage = $this->getPackage('drupal/core');
197     }
198     return $this->drupalCorePackage;
199   }
200
201   /**
202    * Returns the Drupal core version for the given package.
203    *
204    * @param \Composer\Package\PackageInterface $drupalCorePackage
205    *
206    * @return string
207    */
208   protected function getDrupalCoreVersion(PackageInterface $drupalCorePackage) {
209     $version = $drupalCorePackage->getPrettyVersion();
210     if ($drupalCorePackage->getStability() == 'dev' && substr($version, -4) == '-dev') {
211       $version = substr($version, 0, -4);
212       return $version;
213     }
214     return $version;
215   }
216
217   /**
218    * Retrieve the path to the web root.
219    *
220    *  @return string
221    */
222   public function getWebRoot() {
223     $drupalCorePackage = $this->getDrupalCorePackage();
224     $installationManager = $this->composer->getInstallationManager();
225     $corePath = $installationManager->getInstallPath($drupalCorePackage);
226     // Webroot is the parent path of the drupal core installation path.
227     $webroot = dirname($corePath);
228
229     return $webroot;
230   }
231
232   /**
233    * Retrieve a package from the current composer process.
234    *
235    * @param string $name
236    *   Name of the package to get from the current composer installation.
237    *
238    * @return PackageInterface
239    */
240   protected function getPackage($name) {
241     return $this->composer->getRepositoryManager()->getLocalRepository()->findPackage($name, '*');
242   }
243
244   /**
245    * Retrieve excludes from optional "extra" configuration.
246    *
247    * @return array
248    */
249   protected function getExcludes() {
250     return $this->getNamedOptionList('excludes', 'getExcludesDefault');
251   }
252
253   /**
254    * Retrieve list of additional settings files from optional "extra" configuration.
255    *
256    * @return array
257    */
258   protected function getIncludes() {
259     return $this->getNamedOptionList('includes', 'getIncludesDefault');
260   }
261
262   /**
263    * Retrieve list of initial files from optional "extra" configuration.
264    *
265    * @return array
266    */
267   protected function getInitial() {
268     return $this->getNamedOptionList('initial', 'getInitialDefault');
269   }
270
271   /**
272    * Retrieve a named list of options from optional "extra" configuration.
273    * Respects 'omit-defaults', and either includes or does not include the
274    * default values, as requested.
275    *
276    * @return array
277    */
278   protected function getNamedOptionList($optionName, $defaultFn) {
279     $options = $this->getOptions($this->composer);
280     $result = array();
281     if (empty($options['omit-defaults'])) {
282       $result = $this->$defaultFn();
283     }
284     $result = array_merge($result, (array) $options[$optionName]);
285
286     return $result;
287   }
288
289   /**
290    * Retrieve excludes from optional "extra" configuration.
291    *
292    * @return array
293    */
294   protected function getOptions() {
295     $extra = $this->composer->getPackage()->getExtra() + ['drupal-scaffold' => []];
296     $options = $extra['drupal-scaffold'] + [
297       'omit-defaults' => FALSE,
298       'excludes' => [],
299       'includes' => [],
300       'initial' => [],
301       'source' => 'http://cgit.drupalcode.org/drupal/plain/{path}?h={version}',
302       // Github: https://raw.githubusercontent.com/drupal/drupal/{version}/{path}
303     ];
304     return $options;
305   }
306
307   /**
308    * Holds default excludes.
309    */
310   protected function getExcludesDefault() {
311     return [];
312   }
313
314   /**
315    * Holds default settings files list.
316    */
317   protected function getIncludesDefault() {
318     $version = $this->getDrupalCoreVersion($this->getDrupalCorePackage());
319     list($major, $minor) = explode('.', $version, 3);
320     $version = "$major.$minor";
321
322     /**
323      * Files from 8.3.x
324      *
325      * @see http://cgit.drupalcode.org/drupal/tree/?h=8.3.x
326      */
327     $common = [
328       '.csslintrc',
329       '.editorconfig',
330       '.eslintignore',
331       '.gitattributes',
332       '.htaccess',
333       'index.php',
334       'robots.txt',
335       'sites/default/default.settings.php',
336       'sites/default/default.services.yml',
337       'sites/development.services.yml',
338       'sites/example.settings.local.php',
339       'sites/example.sites.php',
340       'update.php',
341       'web.config'
342     ];
343
344     // Version specific variations.
345     if (Semver::satisfies($version, '<8.3')) {
346       $common[] = '.eslintrc';
347     }
348     if (Semver::satisfies($version, '>=8.3')) {
349       $common[] = '.eslintrc.json';
350     }
351     if (Semver::satisfies($version, '>=8.5')) {
352       $common[] = '.ht.router.php';
353     }
354
355     sort($common);
356     return $common;
357   }
358
359   /**
360    * Holds default initial files.
361    */
362   protected function getInitialDefault() {
363     return [];
364   }
365
366 }