UpdateChecker::getVersionSelector()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 2
nop 1
dl 0
loc 13
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace BringYourOwnIdeas\UpdateChecker;
4
5
use BringYourOwnIdeas\Maintenance\Util\ComposerLoader;
6
use Composer\Composer;
7
use Composer\Package\BasePackage;
8
use Composer\Package\PackageInterface;
9
use Composer\Package\Version\VersionSelector;
10
use Composer\Repository\CompositeRepository;
11
use Composer\Repository\RepositorySet;
12
use SilverStripe\Core\Injector\Injector;
13
14
/**
15
 * The update checker class is provided a {@link Link} object representing a package and uses the Composer API to
16
 * determine the next available updates for the package.
17
 */
18
class UpdateChecker
19
{
20
    /**
21
     * @var VersionSelector
22
     */
23
    protected $versionSelector;
24
25
    /**
26
     * Update types (see {@link ComposerPackageVersion}
27
     *
28
     * @var string
29
     */
30
    const TYPE_AVAILABLE = 'Available';
31
    const TYPE_LATEST = 'Latest';
32
33
    /**
34
     * Checks the given package for available and latest updates, and writes them to data models if found
35
     *
36
     * @param PackageInterface $package
37
     * @param string $constraint
38
     * @return string[]
39
     */
40
    public function checkForUpdates(PackageInterface $package, $constraint)
41
    {
42
        $installedVersion = $package->getPrettyVersion();
43
44
        /** @var Composer $composer */
45
        $composer = Injector::inst()->get(ComposerLoader::class)->getComposer();
46
47
        $updateInformation = [
48
            'Version' => $installedVersion,
49
            'VersionHash' => $package->getSourceReference(),
50
            'VersionConstraint' => $constraint,
51
        ];
52
53
        if ($available = $this->findLatestPackage($package, $constraint, $installedVersion, $composer, true)) {
54
            $updateInformation[self::TYPE_AVAILABLE . 'Version'] = $available->getPrettyVersion();
55
            $updateInformation[self::TYPE_AVAILABLE . 'Hash'] = $available->getSourceReference();
56
        }
57
58
        if ($latest = $this->findLatestPackage($package, $constraint, $installedVersion, $composer, false)) {
59
            $updateInformation[self::TYPE_LATEST . 'Version'] = $latest->getPrettyVersion();
60
            $updateInformation[self::TYPE_LATEST . 'Hash'] = $latest->getSourceReference();
61
        }
62
63
        return $updateInformation;
64
    }
65
66
    /**
67
     * @param Composer $composer
68
     * @return VersionSelector
69
     */
70
    protected function getVersionSelector(Composer $composer)
71
    {
72
        if (!$this->versionSelector) {
73
            // Instantiate a new repository pool, providing the stability flags from the project
74
            $respositorySet = new RepositorySet(
75
                $composer->getPackage()->getMinimumStability(),
76
                $composer->getPackage()->getStabilityFlags()
77
            );
78
            $repo = new CompositeRepository($composer->getRepositoryManager()->getRepositories());
79
            $respositorySet->addRepository($repo);
80
            $this->versionSelector = new VersionSelector($respositorySet);
81
        }
82
        return $this->versionSelector;
83
    }
84
85
    /**
86
     * Given a package, this finds the latest package matching it
87
     *
88
     * Based on Composer's ShowCommand::findLatestPackage
89
     *
90
     * @param PackageInterface $package
91
     * @param string $constraint
92
     * @param string $installedVersion
93
     * @param Composer $composer
94
     * @param bool $minorOnly
95
     * @return bool|PackageInterface
96
     */
97
    protected function findLatestPackage(
98
        PackageInterface $package,
99
        $constraint,
100
        $installedVersion,
101
        Composer $composer,
102
        $minorOnly = false
103
    ) {
104
        // find the latest version allowed in this pool
105
        $name = $package->getName();
106
        $versionSelector = $this->getVersionSelector($composer);
107
        $stability = $composer->getPackage()->getMinimumStability();
108
        $flags = $composer->getPackage()->getStabilityFlags();
109
        if (isset($flags[$name])) {
110
            $stability = array_search($flags[$name], BasePackage::$stabilities, true);
111
        }
112
113
        $bestStability = $stability;
114
        if ($composer->getPackage()->getPreferStable()) {
115
            $bestStability = $composer->getPackage()->getStability();
116
        }
117
118
        $targetVersion = null;
119
        if (0 === strpos($installedVersion ?? '', 'dev-')) {
120
            $targetVersion = $installedVersion;
121
        }
122
123
        if ($targetVersion === null && $minorOnly) {
124
            // Use the semver constraint to determine the next available version
125
            $targetVersion = $constraint;
126
        }
127
128
        return $versionSelector->findBestCandidate($name, $targetVersion, $bestStability);
129
    }
130
}
131