1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the puli/composer-plugin package. |
5
|
|
|
* |
6
|
|
|
* (c) Bernhard Schussek <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Puli\ComposerPlugin; |
13
|
|
|
|
14
|
|
|
use Composer\Composer; |
15
|
|
|
use Composer\EventDispatcher\EventSubscriberInterface; |
16
|
|
|
use Composer\IO\IOInterface; |
17
|
|
|
use Composer\Package\AliasPackage; |
18
|
|
|
use Composer\Package\PackageInterface; |
19
|
|
|
use Composer\Plugin\PluginInterface; |
20
|
|
|
use Composer\Script\CommandEvent; |
21
|
|
|
use Composer\Script\Event; |
22
|
|
|
use Composer\Script\ScriptEvents; |
23
|
|
|
use RuntimeException; |
24
|
|
|
use Webmozart\PathUtil\Path; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* A Puli plugin for Composer. |
28
|
|
|
* |
29
|
|
|
* The plugin updates the Puli package repository based on the Composer |
30
|
|
|
* packages whenever `composer install` or `composer update` is executed. |
31
|
|
|
* |
32
|
|
|
* @since 1.0 |
33
|
|
|
* |
34
|
|
|
* @author Bernhard Schussek <[email protected]> |
35
|
|
|
*/ |
36
|
|
|
class PuliPlugin implements PluginInterface, EventSubscriberInterface |
37
|
|
|
{ |
38
|
|
|
/** |
39
|
|
|
* The minimum version of the Puli CLI. |
40
|
|
|
*/ |
41
|
|
|
const MIN_CLI_VERSION = '1.0.0-beta9'; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* The maximum version of the Puli CLI. |
45
|
|
|
*/ |
46
|
|
|
const MAX_CLI_VERSION = '1.999.99999'; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* The name of the installer. |
50
|
|
|
*/ |
51
|
|
|
const INSTALLER_NAME = 'composer'; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* @var bool |
55
|
|
|
*/ |
56
|
|
|
private $initialized = false; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @var PuliRunner |
60
|
|
|
*/ |
61
|
|
|
private $puliRunner; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @var string |
65
|
|
|
*/ |
66
|
|
|
private $rootDir; |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @var bool |
70
|
|
|
*/ |
71
|
|
|
private $runPostInstall = true; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* @var bool |
75
|
|
|
*/ |
76
|
|
|
private $runPostAutoloadDump = true; |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* {@inheritdoc} |
80
|
|
|
*/ |
81
|
|
|
public static function getSubscribedEvents() |
82
|
|
|
{ |
83
|
|
|
return array( |
84
|
|
|
ScriptEvents::POST_INSTALL_CMD => 'postInstall', |
85
|
|
|
ScriptEvents::POST_UPDATE_CMD => 'postInstall', |
86
|
|
|
ScriptEvents::POST_AUTOLOAD_DUMP => 'postAutoloadDump', |
87
|
|
|
); |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
public function __construct(PuliRunner $puliRunner = null) |
91
|
|
|
{ |
92
|
|
|
$this->puliRunner = $puliRunner; |
93
|
|
|
$this->rootDir = Path::normalize(getcwd()); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* {@inheritdoc} |
98
|
|
|
*/ |
99
|
|
|
public function activate(Composer $composer, IOInterface $io) |
100
|
|
|
{ |
101
|
|
|
// Verify if Puli has the right version |
102
|
|
|
try { |
103
|
|
|
$versionString = $this->puliRunner->run('-V'); |
104
|
|
|
} catch (PuliRunnerException $e) { |
105
|
|
|
$this->printWarning($io, 'Could not determine Puli version', $e); |
106
|
|
|
|
107
|
|
|
return; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
if (!preg_match('~\d+\.\d+\.\d+(-\w+)?~', $versionString, $matches)) { |
111
|
|
|
$this->printWarning($io, sprintf( |
112
|
|
|
'Could not determine Puli version. "puli -V" returned: %s', |
113
|
|
|
$versionString |
114
|
|
|
)); |
115
|
|
|
|
116
|
|
|
return; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
View Code Duplication |
if (version_compare($matches[0], self::MIN_CLI_VERSION, '<')) { |
|
|
|
|
120
|
|
|
$this->printWarning($io, sprintf( |
121
|
|
|
'Found an unsupported version of the Puli CLI: %s. Please '. |
122
|
|
|
'upgrade to version %s or higher. You can also install the '. |
123
|
|
|
'puli/cli dependency at version %s in your project.', |
124
|
|
|
$matches[0], |
125
|
|
|
self::MIN_CLI_VERSION, |
126
|
|
|
self::MIN_CLI_VERSION |
127
|
|
|
)); |
128
|
|
|
|
129
|
|
|
return; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
View Code Duplication |
if (version_compare($matches[0], self::MAX_CLI_VERSION, '>')) { |
|
|
|
|
133
|
|
|
$this->printWarning($io, sprintf( |
134
|
|
|
'Found an unsupported version of the Puli CLI: %s. Please '. |
135
|
|
|
'downgrade to a lower version. You can also install the '. |
136
|
|
|
'puli/cli dependency at a lower version than %s in your project.', |
137
|
|
|
$matches[0], |
138
|
|
|
self::MAX_CLI_VERSION |
139
|
|
|
)); |
140
|
|
|
|
141
|
|
|
return; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
$composer->getEventDispatcher()->addSubscriber($this); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
public function postAutoloadDump(Event $event) |
148
|
|
|
{ |
149
|
|
|
// Plugin has been uninstalled |
150
|
|
|
if (!file_exists(__FILE__)) { |
151
|
|
|
return; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
if (!$this->initialized) { |
155
|
|
|
$this->initialize($event->getComposer(), $event->getIO()); |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
// This method is called twice. Run it only once. |
159
|
|
|
if (!$this->runPostAutoloadDump) { |
160
|
|
|
return; |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
$this->runPostAutoloadDump = false; |
164
|
|
|
|
165
|
|
|
$io = $event->getIO(); |
166
|
|
|
|
167
|
|
|
$compConfig = $event->getComposer()->getConfig(); |
168
|
|
|
$vendorDir = $compConfig->get('vendor-dir'); |
169
|
|
|
|
170
|
|
|
// On TravisCI, $vendorDir is a relative path. Probably an old Composer |
171
|
|
|
// build or something. Usually, $vendorDir should be absolute already. |
172
|
|
|
$vendorDir = Path::makeAbsolute($vendorDir, $this->rootDir); |
173
|
|
|
|
174
|
|
|
$autoloadFile = $vendorDir.'/autoload.php'; |
175
|
|
|
$classMapFile = $vendorDir.'/composer/autoload_classmap.php'; |
176
|
|
|
|
177
|
|
|
try { |
178
|
|
|
$factoryClass = $this->getConfigKey('factory.in.class'); |
179
|
|
|
$factoryFile = $this->getConfigKey('factory.in.file'); |
180
|
|
|
} catch (PuliRunnerException $e) { |
181
|
|
|
$this->printWarning($io, 'Could not load Puli configuration', $e); |
182
|
|
|
|
183
|
|
|
return; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
$factoryFile = Path::makeAbsolute($factoryFile, $this->rootDir); |
|
|
|
|
187
|
|
|
|
188
|
|
|
$this->insertFactoryClassConstant($io, $autoloadFile, $factoryClass); |
189
|
|
|
$this->insertFactoryClassMap($io, $classMapFile, $vendorDir, $factoryClass, $factoryFile); |
190
|
|
|
$this->setBootstrapFile($io, $autoloadFile); |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* Updates the Puli repository after Composer installations/updates. |
195
|
|
|
* |
196
|
|
|
* @param CommandEvent $event The Composer event. |
197
|
|
|
*/ |
198
|
|
|
public function postInstall(CommandEvent $event) |
199
|
|
|
{ |
200
|
|
|
// Plugin has been uninstalled |
201
|
|
|
if (!file_exists(__FILE__)) { |
202
|
|
|
return; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
if (!$this->initialized) { |
206
|
|
|
$this->initialize($event->getComposer(), $event->getIO()); |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
// This method is called twice. Run it only once. |
210
|
|
|
if (!$this->runPostInstall) { |
211
|
|
|
return; |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
$this->runPostInstall = false; |
215
|
|
|
|
216
|
|
|
$io = $event->getIO(); |
217
|
|
|
|
218
|
|
|
$io->write('<info>Looking for updated Puli packages</info>'); |
219
|
|
|
|
220
|
|
|
$rootPackage = $event->getComposer()->getPackage(); |
221
|
|
|
$composerPackages = $this->loadComposerPackages($event->getComposer()); |
222
|
|
|
$prodPackageNames = $this->filterProdPackageNames($composerPackages, $rootPackage); |
223
|
|
|
$env = $event->isDevMode() ? PuliPackage::ENV_DEV : PuliPackage::ENV_PROD; |
224
|
|
|
|
225
|
|
|
try { |
226
|
|
|
$puliPackages = $this->loadPuliPackages(); |
227
|
|
|
} catch (PuliRunnerException $e) { |
228
|
|
|
$this->printWarning($io, 'Could not load Puli packages', $e); |
229
|
|
|
|
230
|
|
|
return; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
// Don't remove non-existing packages in production environment |
234
|
|
|
// Removed packages could be dev dependencies (i.e. "require-dev" |
235
|
|
|
// of the root package or "require" of another dev dependency), and |
236
|
|
|
// we can't find out whether they are since Composer doesn't load them |
237
|
|
|
if (PuliPackage::ENV_PROD !== $env) { |
238
|
|
|
$this->removeRemovedPackages($composerPackages, $puliPackages, $io); |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
$this->installNewPackages($composerPackages, $prodPackageNames, $puliPackages, $io, $event->getComposer()); |
242
|
|
|
|
243
|
|
|
// Don't print warnings for non-existing packages in production |
244
|
|
|
if (PuliPackage::ENV_PROD !== $env) { |
245
|
|
|
$this->checkForNotFoundErrors($puliPackages, $io); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
$this->checkForNotLoadableErrors($puliPackages, $io); |
249
|
|
|
$this->adoptComposerName($puliPackages, $io, $event->getComposer()); |
250
|
|
|
$this->buildPuli($io); |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* @param Composer $composer |
255
|
|
|
* @param IOInterface $io |
256
|
|
|
*/ |
257
|
|
|
private function initialize(Composer $composer, IOInterface $io) |
258
|
|
|
{ |
259
|
|
|
// This method must be run after all packages are installed, otherwise |
260
|
|
|
// it could be that the puli/cli is not yet installed and hence the |
261
|
|
|
// CLI is not available |
262
|
|
|
|
263
|
|
|
// Previously, this was called in activate(), which is called |
264
|
|
|
// immediately after installing the plugin, but potentially before |
265
|
|
|
// installing the CLI |
266
|
|
|
|
267
|
|
|
$this->initialized = true; |
268
|
|
|
|
269
|
|
|
// Keep the manually set runner |
270
|
|
|
if ($this->puliRunner) { |
271
|
|
|
return; |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
try { |
275
|
|
|
// Add Composer's bin directory in case the "puli" executable is |
276
|
|
|
// installed with Composer |
277
|
|
|
$this->puliRunner = new PuliRunner($composer->getConfig()->get('bin-dir')); |
278
|
|
|
} catch (RuntimeException $e) { |
279
|
|
|
$io->writeError('<warning>'.$e->getMessage().'</warning>'); |
280
|
|
|
$this->runPostAutoloadDump = false; |
281
|
|
|
$this->runPostInstall = false; |
282
|
|
|
} |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
/** |
286
|
|
|
* @param PackageInterface[] $composerPackages |
287
|
|
|
* @param bool[] $prodPackageNames |
288
|
|
|
* @param PuliPackage[] $puliPackages |
289
|
|
|
* @param IOInterface $io |
290
|
|
|
* @param Composer $composer |
291
|
|
|
*/ |
292
|
|
|
private function installNewPackages(array $composerPackages, array $prodPackageNames, array &$puliPackages, IOInterface $io, Composer $composer) |
293
|
|
|
{ |
294
|
|
|
$installationManager = $composer->getInstallationManager(); |
295
|
|
|
|
296
|
|
|
foreach ($composerPackages as $packageName => $package) { |
297
|
|
|
if ($package instanceof AliasPackage) { |
298
|
|
|
$package = $package->getAliasOf(); |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
// We need to normalize the system-dependent paths returned by Composer |
302
|
|
|
$installPath = Path::normalize($installationManager->getInstallPath($package)); |
303
|
|
|
$env = isset($prodPackageNames[$packageName]) ? PuliPackage::ENV_PROD : PuliPackage::ENV_DEV; |
304
|
|
|
|
305
|
|
|
// Skip meta packages |
306
|
|
|
if ('' === $installPath) { |
307
|
|
|
continue; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
if (isset($puliPackages[$packageName])) { |
311
|
|
|
$puliPackage = $puliPackages[$packageName]; |
312
|
|
|
|
313
|
|
|
// Only proceed if the install path or environment has changed |
314
|
|
|
if ($installPath === $puliPackage->getInstallPath() && $env === $puliPackage->getEnvironment()) { |
315
|
|
|
continue; |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
// Only remove packages installed by Composer |
319
|
|
|
if (self::INSTALLER_NAME === $puliPackage->getInstallerName()) { |
320
|
|
|
$io->write(sprintf( |
321
|
|
|
'Reinstalling <info>%s</info> (<comment>%s</comment>) in <comment>%s</comment>', |
322
|
|
|
$packageName, |
323
|
|
|
Path::makeRelative($installPath, $this->rootDir), |
324
|
|
|
$env |
325
|
|
|
)); |
326
|
|
|
|
327
|
|
|
try { |
328
|
|
|
$this->removePackage($packageName); |
329
|
|
|
} catch (PuliRunnerException $e) { |
330
|
|
|
$this->printPackageWarning($io, 'Could not remove package "%s" (at ./%s)', $packageName, $installPath, $e); |
331
|
|
|
|
332
|
|
|
continue; |
333
|
|
|
} |
334
|
|
|
} |
335
|
|
|
} else { |
336
|
|
|
$io->write(sprintf( |
337
|
|
|
'Installing <info>%s</info> (<comment>%s</comment>) in <comment>%s</comment>', |
338
|
|
|
$packageName, |
339
|
|
|
Path::makeRelative($installPath, $this->rootDir), |
340
|
|
|
$env |
341
|
|
|
)); |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
try { |
345
|
|
|
$this->installPackage($installPath, $packageName, $env); |
346
|
|
|
} catch (PuliRunnerException $e) { |
347
|
|
|
$this->printPackageWarning($io, 'Could not install package "%s" (at ./%s)', $packageName, $installPath, $e); |
348
|
|
|
|
349
|
|
|
continue; |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
$puliPackages[$packageName] = new PuliPackage( |
353
|
|
|
$packageName, |
354
|
|
|
self::INSTALLER_NAME, |
355
|
|
|
$installPath, |
356
|
|
|
PuliPackage::STATE_ENABLED, |
357
|
|
|
$env |
358
|
|
|
); |
359
|
|
|
} |
360
|
|
|
} |
361
|
|
|
|
362
|
|
|
/** |
363
|
|
|
* @param PackageInterface[] $composerPackages |
364
|
|
|
* @param PuliPackage[] $puliPackages |
365
|
|
|
* @param IOInterface $io |
366
|
|
|
*/ |
367
|
|
|
private function removeRemovedPackages(array $composerPackages, array &$puliPackages, IOInterface $io) |
368
|
|
|
{ |
369
|
|
|
/** @var PuliPackage[] $notFoundPackages */ |
370
|
|
|
$notFoundPackages = array_filter($puliPackages, function (PuliPackage $package) { |
371
|
|
|
return PuliPackage::STATE_NOT_FOUND === $package->getState() |
372
|
|
|
&& PuliPlugin::INSTALLER_NAME === $package->getInstallerName(); |
373
|
|
|
}); |
374
|
|
|
|
375
|
|
|
foreach ($notFoundPackages as $packageName => $package) { |
376
|
|
|
// Check whether package was only moved |
377
|
|
|
if (isset($composerPackages[$packageName])) { |
378
|
|
|
continue; |
379
|
|
|
} |
380
|
|
|
|
381
|
|
|
$io->write(sprintf( |
382
|
|
|
'Removing <info>%s</info> (<comment>%s</comment>)', |
383
|
|
|
$packageName, |
384
|
|
|
Path::makeRelative($package->getInstallPath(), $this->rootDir) |
385
|
|
|
)); |
386
|
|
|
|
387
|
|
|
try { |
388
|
|
|
$this->removePackage($packageName); |
389
|
|
|
} catch (PuliRunnerException $e) { |
390
|
|
|
$this->printPackageWarning($io, 'Could not remove package "%s" (at ./%s)', $packageName, $package->getInstallPath(), $e); |
391
|
|
|
|
392
|
|
|
continue; |
393
|
|
|
} |
394
|
|
|
|
395
|
|
|
unset($puliPackages[$packageName]); |
396
|
|
|
} |
397
|
|
|
} |
398
|
|
|
|
399
|
|
View Code Duplication |
private function checkForNotFoundErrors(array $puliPackages, IOInterface $io) |
|
|
|
|
400
|
|
|
{ |
401
|
|
|
/** @var PuliPackage[] $notFoundPackages */ |
402
|
|
|
$notFoundPackages = array_filter($puliPackages, |
403
|
|
|
function (PuliPackage $package) { |
404
|
|
|
return PuliPackage::STATE_NOT_FOUND === $package->getState() |
405
|
|
|
&& PuliPlugin::INSTALLER_NAME === $package->getInstallerName(); |
406
|
|
|
}); |
407
|
|
|
|
408
|
|
|
foreach ($notFoundPackages as $package) { |
409
|
|
|
$this->printPackageWarning( |
410
|
|
|
$io, |
411
|
|
|
'The package "%s" (at ./%s) could not be found', |
412
|
|
|
$package->getName(), |
413
|
|
|
$package->getInstallPath() |
414
|
|
|
); |
415
|
|
|
} |
416
|
|
|
} |
417
|
|
|
|
418
|
|
View Code Duplication |
private function checkForNotLoadableErrors(array $puliPackages, IOInterface $io) |
|
|
|
|
419
|
|
|
{ |
420
|
|
|
/** @var PuliPackage[] $notLoadablePackages */ |
421
|
|
|
$notLoadablePackages = array_filter($puliPackages, function (PuliPackage $package) { |
422
|
|
|
return PuliPackage::STATE_NOT_LOADABLE === $package->getState() |
423
|
|
|
&& PuliPlugin::INSTALLER_NAME === $package->getInstallerName(); |
424
|
|
|
}); |
425
|
|
|
|
426
|
|
|
foreach ($notLoadablePackages as $package) { |
427
|
|
|
$this->printPackageWarning( |
428
|
|
|
$io, |
429
|
|
|
'The package "%s" (at ./%s) could not be loaded', |
430
|
|
|
$package->getName(), |
431
|
|
|
$package->getInstallPath() |
432
|
|
|
); |
433
|
|
|
} |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
private function adoptComposerName(array $puliPackages, IOInterface $io, Composer $composer) |
437
|
|
|
{ |
438
|
|
|
$rootDir = $this->rootDir; |
439
|
|
|
|
440
|
|
|
/** @var PuliPackage[] $rootPackages */ |
441
|
|
|
$rootPackages = array_filter($puliPackages, function (PuliPackage $package) use ($rootDir) { |
442
|
|
|
return !$package->getInstallerName() && $rootDir === $package->getInstallPath(); |
443
|
|
|
}); |
444
|
|
|
|
445
|
|
|
if (0 === count($rootPackages)) { |
446
|
|
|
// This should never happen |
447
|
|
|
$this->printWarning($io, 'No root package could be found'); |
448
|
|
|
|
449
|
|
|
return; |
450
|
|
|
} |
451
|
|
|
|
452
|
|
|
if (count($rootPackages) > 1) { |
453
|
|
|
// This should never happen |
454
|
|
|
$this->printWarning($io, 'More than one root package was found'); |
455
|
|
|
|
456
|
|
|
return; |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
/** @var PuliPackage $rootPackage */ |
460
|
|
|
$rootPackage = reset($rootPackages); |
461
|
|
|
$name = $rootPackage->getName(); |
462
|
|
|
$newName = $composer->getPackage()->getName(); |
463
|
|
|
|
464
|
|
|
// Rename the root package after changing the name in composer.json |
465
|
|
|
if ($name !== $newName) { |
466
|
|
|
try { |
467
|
|
|
$this->renamePackage($name, $newName); |
468
|
|
|
} catch (PuliRunnerException $e) { |
469
|
|
|
$this->printWarning($io, sprintf( |
470
|
|
|
'Could not rename root package to "%s"', |
471
|
|
|
$newName |
472
|
|
|
), $e); |
473
|
|
|
} |
474
|
|
|
} |
475
|
|
|
} |
476
|
|
|
|
477
|
|
|
private function insertFactoryClassConstant(IOInterface $io, $autoloadFile, $factoryClass) |
478
|
|
|
{ |
479
|
|
|
if (!file_exists($autoloadFile)) { |
480
|
|
|
throw new PuliPluginException(sprintf( |
481
|
|
|
'Could not adjust autoloader: The file %s was not found.', |
482
|
|
|
$autoloadFile |
483
|
|
|
)); |
484
|
|
|
} |
485
|
|
|
|
486
|
|
|
$io->write('<info>Generating PULI_FACTORY_CLASS constant</info>'); |
487
|
|
|
|
488
|
|
|
$contents = file_get_contents($autoloadFile); |
489
|
|
|
$escFactoryClass = var_export($factoryClass, true); |
490
|
|
|
$constant = "if (!defined('PULI_FACTORY_CLASS')) {\n"; |
491
|
|
|
$constant .= sprintf(" define('PULI_FACTORY_CLASS', %s);\n", $escFactoryClass); |
492
|
|
|
$constant .= "}\n\n"; |
493
|
|
|
|
494
|
|
|
// Regex modifiers: |
495
|
|
|
// "m": \s matches newlines |
496
|
|
|
// "D": $ matches at EOF only |
497
|
|
|
// Translation: insert before the last "return" in the file |
498
|
|
|
$contents = preg_replace('/\n(?=return [^;]+;\s*$)/mD', "\n".$constant, |
499
|
|
|
$contents); |
500
|
|
|
|
501
|
|
|
file_put_contents($autoloadFile, $contents); |
502
|
|
|
} |
503
|
|
|
|
504
|
|
|
private function insertFactoryClassMap(IOInterface $io, $classMapFile, $vendorDir, $factoryClass, $factoryFile) |
505
|
|
|
{ |
506
|
|
|
if (!file_exists($classMapFile)) { |
507
|
|
|
throw new PuliPluginException(sprintf( |
508
|
|
|
'Could not adjust autoloader: The file %s was not found.', |
509
|
|
|
$classMapFile |
510
|
|
|
)); |
511
|
|
|
} |
512
|
|
|
|
513
|
|
|
$io->write(sprintf('<info>Registering %s with the class-map autoloader</info>', $factoryClass)); |
514
|
|
|
|
515
|
|
|
$relFactoryFile = Path::makeRelative($factoryFile, $vendorDir); |
516
|
|
|
$escFactoryClass = var_export($factoryClass, true); |
517
|
|
|
$escFactoryFile = var_export('/'.$relFactoryFile, true); |
518
|
|
|
$classMap = sprintf("\n %s => \$vendorDir . %s,", $escFactoryClass, $escFactoryFile); |
519
|
|
|
|
520
|
|
|
$contents = file_get_contents($classMapFile); |
521
|
|
|
|
522
|
|
|
// Regex modifiers: |
523
|
|
|
// "m": \s matches newlines |
524
|
|
|
// "D": $ matches at EOF only |
525
|
|
|
// Translation: insert before the last ");" in the file |
526
|
|
|
$contents = preg_replace('/\n(?=\);\s*$)/mD', "\n".$classMap, $contents); |
527
|
|
|
|
528
|
|
|
file_put_contents($classMapFile, $contents); |
529
|
|
|
} |
530
|
|
|
|
531
|
|
|
private function setBootstrapFile(IOInterface $io, $autoloadFile) |
532
|
|
|
{ |
533
|
|
|
$bootstrapFile = $this->getConfigKey('bootstrap-file'); |
534
|
|
|
|
535
|
|
|
// Don't change user-defined bootstrap files |
536
|
|
|
if (!empty($bootstrapFile)) { |
537
|
|
|
return; |
538
|
|
|
} |
539
|
|
|
|
540
|
|
|
$relAutoloadFile = Path::makeRelative($autoloadFile, $this->rootDir); |
541
|
|
|
|
542
|
|
|
$io->write(sprintf('<info>Setting "bootstrap-file" to "%s"</info>', $relAutoloadFile)); |
543
|
|
|
|
544
|
|
|
$this->setConfigKey('bootstrap-file', $relAutoloadFile); |
545
|
|
|
} |
546
|
|
|
|
547
|
|
|
/** |
548
|
|
|
* Loads Composer's currently installed packages. |
549
|
|
|
* |
550
|
|
|
* @param Composer $composer The Composer instance. |
551
|
|
|
* |
552
|
|
|
* @return PackageInterface[] The installed packages indexed by their names. |
553
|
|
|
*/ |
554
|
|
|
private function loadComposerPackages(Composer $composer) |
555
|
|
|
{ |
556
|
|
|
$repository = $composer->getRepositoryManager()->getLocalRepository(); |
557
|
|
|
$packages = array(); |
558
|
|
|
|
559
|
|
|
foreach ($repository->getPackages() as $package) { |
560
|
|
|
/* @var PackageInterface $package */ |
561
|
|
|
$packages[$package->getName()] = $package; |
562
|
|
|
} |
563
|
|
|
|
564
|
|
|
return $packages; |
565
|
|
|
} |
566
|
|
|
|
567
|
|
|
private function getConfigKey($key) |
568
|
|
|
{ |
569
|
|
|
$value = trim($this->puliRunner->run('config %key% --parsed', array( |
570
|
|
|
'key' => $key, |
571
|
|
|
))); |
572
|
|
|
|
573
|
|
|
switch ($value) { |
574
|
|
|
case 'null': |
575
|
|
|
return null; |
576
|
|
|
case 'true': |
577
|
|
|
return true; |
578
|
|
|
case 'false': |
579
|
|
|
return false; |
580
|
|
|
default: |
581
|
|
|
return $value; |
582
|
|
|
} |
583
|
|
|
} |
584
|
|
|
|
585
|
|
|
private function setConfigKey($key, $value) |
586
|
|
|
{ |
587
|
|
|
$this->puliRunner->run('config %key% %value%', array( |
588
|
|
|
'key' => $key, |
589
|
|
|
'value' => $value, |
590
|
|
|
)); |
591
|
|
|
} |
592
|
|
|
|
593
|
|
|
/** |
594
|
|
|
* @return PuliPackage[] |
595
|
|
|
*/ |
596
|
|
|
private function loadPuliPackages() |
597
|
|
|
{ |
598
|
|
|
$packages = array(); |
599
|
|
|
|
600
|
|
|
$output = $this->puliRunner->run('package --list --format %format%', array( |
601
|
|
|
'format' => '%name%;%installer%;%install_path%;%state%;%env%', |
602
|
|
|
)); |
603
|
|
|
|
604
|
|
|
// PuliRunner replaces \r\n by \n for those Windows boxes |
605
|
|
|
foreach (explode("\n", $output) as $packageLine) { |
606
|
|
|
if (!$packageLine) { |
607
|
|
|
continue; |
608
|
|
|
} |
609
|
|
|
|
610
|
|
|
$packageParts = explode(';', $packageLine); |
611
|
|
|
|
612
|
|
|
$packages[$packageParts[0]] = new PuliPackage( |
613
|
|
|
$packageParts[0], |
614
|
|
|
$packageParts[1], |
615
|
|
|
$packageParts[2], |
616
|
|
|
$packageParts[3], |
617
|
|
|
$packageParts[4] |
618
|
|
|
); |
619
|
|
|
} |
620
|
|
|
|
621
|
|
|
return $packages; |
622
|
|
|
} |
623
|
|
|
|
624
|
|
|
private function installPackage($installPath, $packageName, $env) |
625
|
|
|
{ |
626
|
|
|
$env = PuliPackage::ENV_DEV === $env ? ' --dev' : ''; |
627
|
|
|
|
628
|
|
|
$this->puliRunner->run('package --install %path% %package_name% --installer %installer%'.$env, array( |
629
|
|
|
'path' => $installPath, |
630
|
|
|
'package_name' => $packageName, |
631
|
|
|
'installer' => self::INSTALLER_NAME, |
632
|
|
|
)); |
633
|
|
|
} |
634
|
|
|
|
635
|
|
|
private function removePackage($packageName) |
636
|
|
|
{ |
637
|
|
|
$this->puliRunner->run('package --delete %package_name%', array( |
638
|
|
|
'package_name' => $packageName, |
639
|
|
|
)); |
640
|
|
|
} |
641
|
|
|
|
642
|
|
|
private function buildPuli(IOInterface $io) |
643
|
|
|
{ |
644
|
|
|
$io->write('<info>Running "puli build"</info>'); |
645
|
|
|
|
646
|
|
|
$this->puliRunner->run('build'); |
647
|
|
|
} |
648
|
|
|
|
649
|
|
|
private function renamePackage($name, $newName) |
650
|
|
|
{ |
651
|
|
|
$this->puliRunner->run('package --rename %old_name% %new_name%', array( |
652
|
|
|
'old_name' => $name, |
653
|
|
|
'new_name' => $newName, |
654
|
|
|
)); |
655
|
|
|
} |
656
|
|
|
|
657
|
|
|
private function printWarning(IOInterface $io, $message, PuliRunnerException $exception = null) |
658
|
|
|
{ |
659
|
|
|
if (!$exception) { |
660
|
|
|
$reasonPhrase = ''; |
661
|
|
|
} elseif ($io->isVerbose()) { |
662
|
|
|
$reasonPhrase = $exception->getFullError(); |
663
|
|
|
} else { |
664
|
|
|
$reasonPhrase = $exception->getShortError(); |
665
|
|
|
} |
666
|
|
|
|
667
|
|
|
$io->writeError(sprintf( |
668
|
|
|
'<warning>Warning: %s%s</warning>', |
669
|
|
|
$message, |
670
|
|
|
$reasonPhrase ? ': '.$reasonPhrase : '.' |
671
|
|
|
)); |
672
|
|
|
} |
673
|
|
|
|
674
|
|
|
private function printPackageWarning(IOInterface $io, $message, $packageName, $installPath, PuliRunnerException $exception = null) |
675
|
|
|
{ |
676
|
|
|
$this->printWarning($io, sprintf( |
677
|
|
|
$message, |
678
|
|
|
$packageName, |
679
|
|
|
Path::makeRelative($installPath, $this->rootDir) |
680
|
|
|
), $exception); |
681
|
|
|
} |
682
|
|
|
|
683
|
|
|
private function filterProdPackageNames(array $composerPackages, PackageInterface $package, array &$result = array()) |
684
|
|
|
{ |
685
|
|
|
// Resolve aliases |
686
|
|
|
if ($package instanceof AliasPackage) { |
687
|
|
|
$package = $package->getAliasOf(); |
688
|
|
|
} |
689
|
|
|
|
690
|
|
|
// Package was processed already |
691
|
|
|
if (isset($result[$package->getName()])) { |
692
|
|
|
return $result; |
693
|
|
|
} |
694
|
|
|
|
695
|
|
|
$result[$package->getName()] = true; |
696
|
|
|
|
697
|
|
|
// Recursively filter package names |
698
|
|
|
foreach ($package->getRequires() as $packageName => $link) { |
699
|
|
|
if (isset($composerPackages[$packageName])) { |
700
|
|
|
$this->filterProdPackageNames($composerPackages, $composerPackages[$packageName], $result); |
701
|
|
|
} |
702
|
|
|
} |
703
|
|
|
|
704
|
|
|
return $result; |
705
|
|
|
} |
706
|
|
|
} |
707
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.