Failed Conditions
Pull Request — master (#46)
by Chris
06:24
created

PuliPluginImpl::loadPuliPackages()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 27
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 27
ccs 15
cts 15
cp 1
rs 8.8571
cc 3
eloc 15
nc 3
nop 0
crap 3
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\Config;
16
use Composer\IO\IOInterface;
17
use Composer\Package\AliasPackage;
18
use Composer\Package\PackageInterface;
19
use Composer\Script\Event;
20
use Exception;
21
use RuntimeException;
22
use Symfony\Component\Filesystem\Filesystem;
23
use Webmozart\PathUtil\Path;
24
25
/**
26
 * Implementation of the Puli plugin.
27
 *
28
 * This class is separate from the main {@link PuliPlugin} class so that it can
29
 * be loaded lazily after updating the sources of this package in the project
30
 * that requires the package.
31
 *
32
 * @author Bernhard Schussek <[email protected]>
33
 */
34
class PuliPluginImpl
35
{
36
    /**
37
     * The version of the Puli plugin.
38
     */
39
    const VERSION = '@package_version@';
40
41
    /**
42
     * The minimum version of the Puli CLI.
43
     */
44
    const MIN_CLI_VERSION = '1.0.0-beta10';
45
46
    /**
47
     * The maximum version of the Puli CLI.
48
     */
49
    const MAX_CLI_VERSION = '1.999.99999';
50
51
    /**
52
     * The name of the installer.
53
     */
54
    const INSTALLER_NAME = 'composer';
55
56
    /**
57
     * @var Composer
58
     */
59
    private $composer;
60
61
    /**
62
     * @var IOInterface
63
     */
64
    private $io;
65
66
    /**
67
     * @var Config
68
     */
69
    private $config;
70
71
    /**
72
     * @var bool
73
     */
74
    private $isDev;
75
76
    /**
77
     * @var PuliRunner
78
     */
79
    private $puliRunner;
80
81
    /**
82
     * @var string
83
     */
84
    private $rootDir;
85
86
    /**
87
     * @var bool
88
     */
89
    private $runPreAutoloadDump = true;
90
91
    /**
92
     * @var bool
93
     */
94
    private $runPostAutoloadDump = true;
95
96
    /**
97
     * @var bool
98
     */
99
    private $runPostInstall = true;
100
101
    /**
102
     * @var bool
103
     */
104
    private $initialized = false;
105
106
    /**
107
     * @var string
108
     */
109
    private $autoloadFile;
110
111 41
    public function __construct(Event $event, PuliRunner $puliRunner = null)
112
    {
113 41
        $this->composer = $event->getComposer();
114 41
        $this->io = $event->getIO();
115 41
        $this->config = $this->composer->getConfig();
116 41
        $this->isDev = $event->isDevMode();
117 41
        $this->puliRunner = $puliRunner;
118 41
        $this->rootDir = Path::normalize(getcwd());
119
120 41
        $vendorDir = $this->config->get('vendor-dir');
121
122
        // On TravisCI, $vendorDir is a relative path. Probably an old Composer
123
        // build or something. Usually, $vendorDir should be absolute already.
124 41
        $vendorDir = Path::makeAbsolute($vendorDir, $this->rootDir);
125
126 41
        $this->autoloadFile = $vendorDir.'/autoload.php';
127 41
    }
128
129 2
    public function preAutoloadDump()
130
    {
131 2
        if (!$this->initialized) {
132 2
            $this->initialize();
133
        }
134
135
        // This method is called twice. Run it only once.
136 2
        if (!$this->runPreAutoloadDump) {
137
            return;
138
        }
139
140 2
        $this->runPreAutoloadDump = false;
141
142
        try {
143 2
            $factoryClass = $this->getConfigKey('factory.in.class');
144 2
            $factoryFile = $this->getConfigKey('factory.in.file');
145
        } catch (PuliRunnerException $e) {
146
            $this->printWarning('Could not load Puli configuration', $e);
147
148
            return;
149
        }
150
151 2
        $factoryFile = Path::makeAbsolute($factoryFile, $this->rootDir);
0 ignored issues
show
Bug introduced by
It seems like $factoryFile can also be of type boolean or null; however, Webmozart\PathUtil\Path::makeAbsolute() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
152
153 2
        $autoload = $this->composer->getPackage()->getAutoload();
154 2
        $autoload['classmap'][] = $factoryFile;
155
156 2
        $this->composer->getPackage()->setAutoload($autoload);
157
158 2
        if (!file_exists($factoryFile)) {
159 1
            $filesystem = new Filesystem();
160
            // Let Composer find the factory class with a temporary stub
161
162 1
            $normalizedFQCN = ltrim($factoryClass, '\\');
0 ignored issues
show
Unused Code introduced by
$normalizedFQCN is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
163 1
            $parts = explode('\\', $factoryClass);
164
165 1
            $shortName = array_pop($parts);
166
167 1
            if (count($parts) === 0) {
168
                // No namespace
169
                $filesystem->dumpFile($factoryFile, '<?php class '.$shortName.' {}');
170
            } else {
171
                // $factoryClass is a FQCN
172 1
                $ns = implode('\\', $parts);
173
174 1
                $filesystem->dumpFile($factoryFile, '<?php namespace '.$ns.'; class '.$shortName.' {}');
175
            }
176
        }
177 2
    }
178
179 5
    public function postAutoloadDump()
180
    {
181 5
        if (!$this->initialized) {
182 5
            $this->initialize();
183
        }
184
185
        // This method is called twice. Run it only once.
186 5
        if (!$this->runPostAutoloadDump) {
187 1
            return;
188
        }
189
190 5
        $this->runPostAutoloadDump = false;
191
192
        try {
193 5
            $factoryClass = $this->getConfigKey('factory.in.class');
194 1
        } catch (PuliRunnerException $e) {
195 1
            $this->printWarning('Could not load Puli configuration', $e);
196
197 1
            return;
198
        }
199
200 4
        $this->insertFactoryClassConstant($this->autoloadFile, $factoryClass);
201 4
        $this->setBootstrapFile($this->autoloadFile);
202 4
    }
203
204
    /**
205
     * Updates the Puli repository after Composer installations/updates.
206
     */
207 34
    public function postInstall()
208
    {
209 34
        if (!$this->initialized) {
210 34
            $this->initialize();
211
        }
212
213
        // This method is called twice. Run it only once.
214 34
        if (!$this->runPostInstall) {
215 3
            return;
216
        }
217
218 32
        $this->runPostInstall = false;
219
220 32
        $this->io->write('<info>Synchronizing Puli with Composer</info>');
221
222 32
        $rootPackage = $this->composer->getPackage();
223 32
        $composerPackages = $this->loadComposerPackages();
224 32
        $prodPackageNames = $this->filterProdPackageNames($composerPackages, $rootPackage);
225 32
        $env = $this->isDev ? PuliPackage::ENV_DEV : PuliPackage::ENV_PROD;
226
227
        try {
228 32
            $puliPackages = $this->loadPuliPackages();
229 1
        } catch (PuliRunnerException $e) {
230 1
            $this->printWarning('Could not load Puli packages', $e);
231
232 1
            return;
233
        }
234
235
        // Don't remove non-existing packages in production environment
236
        // Removed packages could be dev dependencies (i.e. "require-dev"
237
        // of the root package or "require" of another dev dependency), and
238
        // we can't find out whether they are since Composer doesn't load them
239 31
        if (PuliPackage::ENV_PROD !== $env) {
240 3
            $this->removeRemovedPackages($composerPackages, $puliPackages);
241
        }
242
243 31
        $this->installNewPackages($composerPackages, $prodPackageNames, $puliPackages);
244
245
        // Don't print warnings for non-existing packages in production
246 31
        if (PuliPackage::ENV_PROD !== $env) {
247 3
            $this->checkForNotFoundErrors($puliPackages);
248
        }
249
250 31
        $this->checkForNotLoadableErrors($puliPackages);
251 31
        $this->adoptComposerName($puliPackages);
252 31
        $this->removePuliDir();
253 31
        $this->buildPuli();
254 31
    }
255
256 41
    private function initialize()
257
    {
258 41
        if (!file_exists($this->autoloadFile)) {
259
            $filesystem = new Filesystem();
260
            // Avoid problems if using the runner before autoload.php has been
261
            // generated
262
            $filesystem->dumpFile($this->autoloadFile, '');
263
        }
264
265 41
        $this->initialized = true;
266
267
        // Keep the manually set runner
268 41
        if (null === $this->puliRunner) {
269
            try {
270
                // Add Composer's bin directory in case the "puli" executable is
271
                // installed with Composer
272
                $this->puliRunner = new PuliRunner($this->config->get('bin-dir'));
273
            } catch (RuntimeException $e) {
274
                $this->printWarning('Plugin initialization failed', $e);
275
                $this->runPreAutoloadDump = false;
276
                $this->runPostAutoloadDump = false;
277
                $this->runPostInstall = false;
278
            }
279
        }
280
281
        // Use the runner to verify if Puli has the right version
282
        try {
283 41
            $this->verifyPuliVersion();
284 2
        } catch (RuntimeException $e) {
285 2
            $this->printWarning('Version check failed', $e);
286 2
            $this->runPreAutoloadDump = false;
287 2
            $this->runPostAutoloadDump = false;
288 2
            $this->runPostInstall = false;
289
        }
290 41
    }
291
292
    /**
293
     * @param PackageInterface[] $composerPackages
294
     * @param bool[]             $prodPackageNames
295
     * @param PuliPackage[]      $puliPackages
296
     */
297 31
    private function installNewPackages(array $composerPackages, array $prodPackageNames, array &$puliPackages)
298
    {
299 31
        $installationManager = $this->composer->getInstallationManager();
300
301 31
        foreach ($composerPackages as $packageName => $package) {
302 21
            if ($package instanceof AliasPackage) {
303 2
                $package = $package->getAliasOf();
304
            }
305
306
            // We need to normalize the system-dependent paths returned by Composer
307 21
            $installPath = Path::normalize($installationManager->getInstallPath($package));
308 21
            $env = isset($prodPackageNames[$packageName]) ? PuliPackage::ENV_PROD : PuliPackage::ENV_DEV;
309
310
            // Skip meta packages
311 21
            if ('' === $installPath) {
312 1
                continue;
313
            }
314
315 20
            if (isset($puliPackages[$packageName])) {
316 15
                $puliPackage = $puliPackages[$packageName];
317
318
                // Only proceed if the install path or environment has changed
319 15
                if ($installPath === $puliPackage->getInstallPath() && $env === $puliPackage->getEnvironment()) {
320 14
                    continue;
321
                }
322
323
                // Only remove packages installed by Composer
324 6
                if (self::INSTALLER_NAME === $puliPackage->getInstallerName()) {
325 5
                    $this->io->write(sprintf(
326 5
                        'Reinstalling <info>%s</info> (<comment>%s</comment>) in <comment>%s</comment>',
327
                        $packageName,
328 5
                        Path::makeRelative($installPath, $this->rootDir),
329
                        $env
330
                    ));
331
332
                    try {
333 5
                        $this->removePackage($packageName);
334 1
                    } catch (PuliRunnerException $e) {
335 1
                        $this->printPackageWarning('Could not remove package "%s" (at "%s")', $packageName, $installPath, $e);
336
337 6
                        continue;
338
                    }
339
                }
340
            } else {
341 6
                $this->io->write(sprintf(
342 6
                    'Installing <info>%s</info> (<comment>%s</comment>) in <comment>%s</comment>',
343
                    $packageName,
344 6
                    Path::makeRelative($installPath, $this->rootDir),
345
                    $env
346
                ));
347
            }
348
349
            try {
350 11
                $this->installPackage($installPath, $packageName, $env);
351 3
            } catch (PuliRunnerException $e) {
352 3
                $this->printPackageWarning('Could not install package "%s" (at "%s")', $packageName, $installPath, $e);
353
354 3
                continue;
355
            }
356
357 8
            $puliPackages[$packageName] = new PuliPackage(
358
                $packageName,
359 8
                self::INSTALLER_NAME,
360
                $installPath,
361 8
                PuliPackage::STATE_ENABLED,
362
                $env
363
            );
364
        }
365 31
    }
366
367
    /**
368
     * @param PackageInterface[] $composerPackages
369
     * @param PuliPackage[]      $puliPackages
370
     */
371 3
    private function removeRemovedPackages(array $composerPackages, array &$puliPackages)
372
    {
373
        /** @var PuliPackage[] $notFoundPackages */
374
        $notFoundPackages = array_filter($puliPackages, function (PuliPackage $package) {
375 3
            return PuliPackage::STATE_NOT_FOUND === $package->getState()
376 3
                && PuliPluginImpl::INSTALLER_NAME === $package->getInstallerName();
377 3
        });
378
379 3
        foreach ($notFoundPackages as $packageName => $package) {
380
            // Check whether package was only moved
381 3
            if (isset($composerPackages[$packageName])) {
382 1
                continue;
383
            }
384
385 2
            $this->io->write(sprintf(
386 2
                'Removing <info>%s</info> (<comment>%s</comment>)',
387
                $packageName,
388 2
                Path::makeRelative($package->getInstallPath(), $this->rootDir)
389
            ));
390
391
            try {
392 2
                $this->removePackage($packageName);
393 1
            } catch (PuliRunnerException $e) {
394 1
                $this->printPackageWarning('Could not remove package "%s" (at "%s")', $packageName, $package->getInstallPath(), $e);
395
396 1
                continue;
397
            }
398
399 1
            unset($puliPackages[$packageName]);
400
        }
401 3
    }
402
403 3 View Code Duplication
    private function checkForNotFoundErrors(array $puliPackages)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
404
    {
405
        /** @var PuliPackage[] $notFoundPackages */
406 3
        $notFoundPackages = array_filter($puliPackages,
407
            function (PuliPackage $package) {
408 3
                return PuliPackage::STATE_NOT_FOUND === $package->getState()
409 3
                && PuliPluginImpl::INSTALLER_NAME === $package->getInstallerName();
410 3
            });
411
412 3
        foreach ($notFoundPackages as $package) {
413 2
            $this->printPackageWarning(
414 2
                'The package "%s" (at "%s") could not be found',
415 2
                $package->getName(),
416 2
                $package->getInstallPath()
417
            );
418
        }
419 3
    }
420
421 31 View Code Duplication
    private function checkForNotLoadableErrors(array $puliPackages)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
422
    {
423
        /** @var PuliPackage[] $notLoadablePackages */
424
        $notLoadablePackages = array_filter($puliPackages, function (PuliPackage $package) {
425 31
            return PuliPackage::STATE_NOT_LOADABLE === $package->getState()
426 31
                && PuliPluginImpl::INSTALLER_NAME === $package->getInstallerName();
427 31
        });
428
429 31
        foreach ($notLoadablePackages as $package) {
430 1
            $this->printPackageWarning(
431 1
                'The package "%s" (at "%s") could not be loaded',
432 1
                $package->getName(),
433 1
                $package->getInstallPath()
434
            );
435
        }
436 31
    }
437
438 31
    private function adoptComposerName(array $puliPackages)
439
    {
440 31
        $rootDir = $this->rootDir;
441
442
        /** @var PuliPackage[] $rootPackages */
443 31
        $rootPackages = array_filter($puliPackages, function (PuliPackage $package) use ($rootDir) {
444 31
            return !$package->getInstallerName() && $rootDir === $package->getInstallPath();
445 31
        });
446
447 31
        if (0 === count($rootPackages)) {
448
            // This should never happen
449
            $this->printWarning('No root package could be found');
450
451
            return;
452
        }
453
454 31
        if (count($rootPackages) > 1) {
455
            // This should never happen
456
            $this->printWarning('More than one root package was found');
457
458
            return;
459
        }
460
461
        /** @var PuliPackage $rootPackage */
462 31
        $rootPackage = reset($rootPackages);
463 31
        $name = $rootPackage->getName();
464 31
        $newName = $this->composer->getPackage()->getName();
465
466
        // Rename the root package after changing the name in composer.json
467 31
        if ($name !== $newName) {
468
            try {
469 2
                $this->renamePackage($name, $newName);
470 1
            } catch (PuliRunnerException $e) {
471 1
                $this->printWarning(sprintf(
472 1
                    'Could not rename root package to "%s"',
473
                    $newName
474
                ), $e);
475
            }
476
        }
477 31
    }
478
479 4
    private function insertFactoryClassConstant($autoloadFile, $factoryClass)
480
    {
481 4
        if (!file_exists($autoloadFile)) {
482
            throw new PuliPluginException(sprintf(
483
                'Could not adjust autoloader: The file %s was not found.',
484
                $autoloadFile
485
            ));
486
        }
487
488 4
        $this->io->write('<info>Generating the "PULI_FACTORY_CLASS" constant</info>');
489
490 4
        $contents = file_get_contents($autoloadFile);
491 4
        $escFactoryClass = var_export($factoryClass, true);
492 4
        $constant = "if (!defined('PULI_FACTORY_CLASS')) {\n";
493 4
        $constant .= sprintf("    define('PULI_FACTORY_CLASS', %s);\n", $escFactoryClass);
494 4
        $constant .= "}\n\n";
495
496
        // Regex modifiers:
497
        // "m": \s matches newlines
498
        // "D": $ matches at EOF only
499
        // Translation: insert before the last "return" in the file
500 4
        $contents = preg_replace('/\n(?=return [^;]+;\s*$)/mD', "\n".$constant,
501
            $contents);
502
503 4
        file_put_contents($autoloadFile, $contents);
504 4
    }
505
506 4
    private function setBootstrapFile($autoloadFile)
507
    {
508 4
        $bootstrapFile = $this->getConfigKey('bootstrap-file');
509
510
        // Don't change user-defined bootstrap files
511 4
        if (!empty($bootstrapFile)) {
512 1
            return;
513
        }
514
515 3
        $relAutoloadFile = Path::makeRelative($autoloadFile, $this->rootDir);
516
517 3
        $this->io->write(sprintf('<info>Setting "bootstrap-file" to "%s"</info>', $relAutoloadFile));
518
519 3
        $this->setConfigKey('bootstrap-file', $relAutoloadFile);
520 3
    }
521
522
    /**
523
     * Loads Composer's currently installed packages.
524
     *
525
     * @return PackageInterface[] The installed packages indexed by their names.
526
     */
527 32
    private function loadComposerPackages()
528
    {
529 32
        $repository = $this->composer->getRepositoryManager()->getLocalRepository();
530 32
        $packages = array();
531
532 32
        foreach ($repository->getPackages() as $package) {
533
            /* @var PackageInterface $package */
534 21
            $packages[$package->getName()] = $package;
535
        }
536
537 32
        return $packages;
538
    }
539
540 38
    private function getConfigKey($key)
541
    {
542 38
        $value = trim($this->puliRunner->run('config %key% --parsed', array(
543 38
            'key' => $key,
544
        )));
545
546
        switch ($value) {
547 37
            case 'null':
548 1
                return null;
549 37
            case 'true':
550
                return true;
551 37
            case 'false':
552
                return false;
553
            default:
554 37
                return $value;
555
        }
556
    }
557
558 3
    private function setConfigKey($key, $value)
559
    {
560 3
        $this->puliRunner->run('config %key% %value%', array(
561 3
            'key' => $key,
562 3
            'value' => $value,
563
        ));
564 3
    }
565
566
    /**
567
     * @return PuliPackage[]
568
     */
569 32
    private function loadPuliPackages()
570
    {
571 32
        $packages = array();
572
573 32
        $output = $this->puliRunner->run('package --list --format %format%', array(
574 32
            'format' => '%name%;%installer%;%install_path%;%state%;%env%',
575
        ));
576
577
        // PuliRunner replaces \r\n by \n for those Windows boxes
578 31
        foreach (explode("\n", $output) as $packageLine) {
579 31
            if (!$packageLine) {
580 31
                continue;
581
            }
582
583 31
            $packageParts = explode(';', $packageLine);
584
585 31
            $packages[$packageParts[0]] = new PuliPackage(
586 31
                $packageParts[0],
587 31
                $packageParts[1],
588 31
                $packageParts[2],
589 31
                $packageParts[3],
590 31
                $packageParts[4]
591
            );
592
        }
593
594 31
        return $packages;
595
    }
596
597 11
    private function installPackage($installPath, $packageName, $env)
598
    {
599 11
        $env = PuliPackage::ENV_DEV === $env ? ' --dev' : '';
600
601 11
        $this->puliRunner->run('package --install %path% %package_name% --installer %installer%'.$env, array(
602 11
            'path' => $installPath,
603 11
            'package_name' => $packageName,
604 11
            'installer' => self::INSTALLER_NAME,
605
        ));
606 8
    }
607
608 7
    private function removePackage($packageName)
609
    {
610 7
        $this->puliRunner->run('package --delete %package_name%', array(
611 7
            'package_name' => $packageName,
612
        ));
613 5
    }
614
615 31
    private function removePuliDir()
616
    {
617 31
        $relativePuliDir = rtrim($this->getConfigKey('puli-dir'), '/');
618
619 31
        $puliDir = Path::makeAbsolute($relativePuliDir, $this->rootDir);
620
621
        // Only remove existing sub-directories of the root directory
622 31
        if (!file_exists($puliDir) || 0 !== strpos($puliDir, $this->rootDir.'/')) {
623 30
            return;
624
        }
625
626 1
        $this->io->write(sprintf('<info>Deleting the "%s" directory</info>', $relativePuliDir));
627
628
        // Remove the .puli directory to prevent upgrade problems
629 1
        $filesystem = new Filesystem();
630 1
        $filesystem->remove($puliDir);
631 1
    }
632
633 31
    private function buildPuli()
634
    {
635 31
        $this->io->write('<info>Running "puli build"</info>');
636
637 31
        $this->puliRunner->run('build');
638 31
    }
639
640 2
    private function renamePackage($name, $newName)
641
    {
642 2
        $this->puliRunner->run('package --rename %old_name% %new_name%', array(
643 2
            'old_name' => $name,
644 2
            'new_name' => $newName,
645
        ));
646 1
    }
647
648
    /**
649
     * @param                $message
650
     * @param Exception|null $exception
651
     */
652 12
    private function printWarning($message, Exception $exception = null)
653
    {
654 12
        if (!$exception) {
655 3
            $reasonPhrase = '';
656 10
        } elseif ($this->io->isVerbose()) {
657
            $reasonPhrase = $exception instanceof PuliRunnerException
658
                ? $exception->getFullError()
659
                : $exception->getMessage()."\n\n".$exception->getTraceAsString();
660
        } else {
661 10
            $reasonPhrase = $exception instanceof PuliRunnerException
662 8
                ? $exception->getShortError()
663 10
                : $exception->getMessage();
664
        }
665
666 12
        $this->io->writeError(sprintf(
667 12
            '<warning>Warning: %s%s</warning>',
668
            $message,
669 12
            $reasonPhrase ? ': '.$reasonPhrase : '.'
670
        ));
671 12
    }
672
673 7
    private function printPackageWarning($message, $packageName, $installPath, PuliRunnerException $exception = null)
674
    {
675 7
        $this->printWarning(sprintf(
676
            $message,
677
            $packageName,
678 7
            Path::makeRelative($installPath, $this->rootDir)
679
        ), $exception);
680 7
    }
681
682 32
    private function filterProdPackageNames(array $composerPackages, PackageInterface $package, array &$result = array())
683
    {
684
        // Resolve aliases
685 32
        if ($package instanceof AliasPackage) {
686 2
            $package = $package->getAliasOf();
687
        }
688
689
        // Package was processed already
690 32
        if (isset($result[$package->getName()])) {
691
            return $result;
692
        }
693
694 32
        $result[$package->getName()] = true;
695
696
        // Recursively filter package names
697 32
        foreach ($package->getRequires() as $packageName => $link) {
698 21
            if (isset($composerPackages[$packageName])) {
699 21
                $this->filterProdPackageNames($composerPackages, $composerPackages[$packageName], $result);
700
            }
701
        }
702
703 32
        return $result;
704
    }
705
706 41
    private function verifyPuliVersion()
707
    {
708 41
        $versionString = $this->puliRunner->run('-V');
709
710 41
        if (!preg_match('~^Puli version (\S+)$~', $versionString, $matches)) {
711
            throw new RuntimeException(sprintf(
712
                'Could not determine Puli version. "puli -V" returned: %s',
713
                $versionString
714
            ));
715
        }
716
717
        // the development build of the plugin is always considered compatible
718
        // with the development build of the CLI
719
        // Split strings to prevent replacement during release
720 41
        if ('@package_'.'version@' === self::VERSION && '@package_'.'version@' === $matches[1]) {
721 1
            return;
722
        }
723
724 40 View Code Duplication
        if (version_compare($matches[1], self::MIN_CLI_VERSION, '<')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
725 1
            throw new RuntimeException(sprintf(
726
                'Found an unsupported version of the Puli CLI: %s. Please '.
727
                'upgrade to version %s or higher. You can also install the '.
728 1
                'puli/cli dependency at version %s in your project.',
729 1
                $matches[1],
730 1
                self::MIN_CLI_VERSION,
731 1
                self::MIN_CLI_VERSION
732
            ));
733
        }
734
735 39 View Code Duplication
        if (version_compare($matches[1], self::MAX_CLI_VERSION, '>')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
736 1
            throw new RuntimeException(sprintf(
737
                'Found an unsupported version of the Puli CLI: %s. Please '.
738
                'downgrade to a lower version than %s. You can also install '.
739 1
                'the puli/cli dependency in your project.',
740 1
                $matches[1],
741 1
                self::MAX_CLI_VERSION
742
            ));
743
        }
744 38
    }
745
}
746