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

src/PuliPluginImpl.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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);
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
$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)
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)
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, '<')) {
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, '>')) {
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