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