Completed
Push — master ( 580007...3ce141 )
by
unknown
9s
created

src/UpdateChecker.php (2 issues)

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
namespace BringYourOwnIdeas\UpdateChecker;
4
5
use BringYourOwnIdeas\Maintenance\Util\ComposerLoader;
6
use Composer\Composer;
7
use Composer\DependencyResolver\Pool;
8
use Composer\Package\BasePackage;
9
use Composer\Package\PackageInterface;
10
use Composer\Package\Version\VersionSelector;
11
use Composer\Repository\CompositeRepository;
12
use Package;
13
use DataObject;
14
use Injector;
15
16
/**
17
 * The update checker class is provided a {@link Link} object representing a package and uses the Composer API to
18
 * determine the next available updates for the package.
19
 */
20
class UpdateChecker
21
{
22
    /**
23
     * @var VersionSelector
24
     */
25
    protected $versionSelector;
26
27
    /**
28
     * Update types (see {@link ComposerPackageVersion}
29
     *
30
     * @var string
31
     */
32
    const TYPE_AVAILABLE = 'Available';
33
    const TYPE_LATEST = 'Latest';
34
35
    /**
36
     * Checks the given package for available and latest updates, and writes them to data models if found
37
     *
38
     * @param PackageInterface $package
39
     * @param string $constraint
40
     */
41
    public function checkForUpdates(PackageInterface $package, $constraint)
42
    {
43
        $installedVersion = $package->getPrettyVersion();
44
45
        /** @var Composer $composer */
46
        $composer = Injector::inst()->create(ComposerLoader::class)->getComposer();
47
48
        $updateInformation = [
49
            'Version' => $installedVersion,
50
            'VersionHash' => $package->getSourceReference(),
51
            'VersionConstraint' => $constraint,
52
        ];
53
54 View Code Duplication
        if ($available = $this->findLatestPackage($package, $constraint, $installedVersion, $composer, true)) {
0 ignored issues
show
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...
55
            $updateInformation[self::TYPE_AVAILABLE . 'Version'] = $available->getPrettyVersion();
56
            $updateInformation[self::TYPE_AVAILABLE . 'Hash'] = $available->getSourceReference();
57
        }
58
59 View Code Duplication
        if ($latest = $this->findLatestPackage($package, $constraint, $installedVersion, $composer, false)) {
0 ignored issues
show
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...
60
            $updateInformation[self::TYPE_LATEST . 'Version'] = $latest->getPrettyVersion();
61
            $updateInformation[self::TYPE_LATEST . 'Hash'] = $latest->getSourceReference();
62
        }
63
64
        $this->recordUpdate($package->getName(), $updateInformation);
65
    }
66
67
    /**
68
     * @param Composer $composer
69
     * @return VersionSelector
70
     */
71
    protected function getVersionSelector(Composer $composer)
72
    {
73
        if (!$this->versionSelector) {
74
            // Instantiate a new repository pool, providing the stability flags from the project
75
            $pool = new Pool(
76
                $composer->getPackage()->getMinimumStability(),
77
                $composer->getPackage()->getStabilityFlags()
78
            );
79
            $pool->addRepository(new CompositeRepository($composer->getRepositoryManager()->getRepositories()));
80
81
            $this->versionSelector = new VersionSelector($pool);
82
        }
83
84
        return $this->versionSelector;
85
    }
86
87
    /**
88
     * Given a package, this finds the latest package matching it
89
     *
90
     * Based on Composer's ShowCommand::findLatestPackage
91
     *
92
     * @param PackageInterface $package
93
     * @param string $constraint
94
     * @param string $installedVersion
95
     * @param Composer $composer
96
     * @param bool $minorOnly
97
     * @return bool|PackageInterface
98
     */
99
    protected function findLatestPackage(
100
        PackageInterface $package,
101
        $constraint,
102
        $installedVersion,
103
        Composer $composer,
104
        $minorOnly = false
105
    ) {
106
        // find the latest version allowed in this pool
107
        $name = $package->getName();
108
        $versionSelector = $this->getVersionSelector($composer);
109
        $stability = $composer->getPackage()->getMinimumStability();
110
        $flags = $composer->getPackage()->getStabilityFlags();
111
        if (isset($flags[$name])) {
112
            $stability = array_search($flags[$name], BasePackage::$stabilities, true);
113
        }
114
115
        $bestStability = $stability;
116
        if ($composer->getPackage()->getPreferStable()) {
117
            $bestStability = $composer->getPackage()->getStability();
118
        }
119
120
        $targetVersion = null;
121
        if (0 === strpos($installedVersion, 'dev-')) {
122
            $targetVersion = $installedVersion;
123
        }
124
125
        if ($targetVersion === null && $minorOnly) {
126
            // Use the semver constraint to determine the next available version
127
            $targetVersion = $constraint;
128
        }
129
130
        return $versionSelector->findBestCandidate($name, $targetVersion, null, $bestStability);
131
    }
132
133
    /**
134
     * Record package details in the database
135
     *
136
     * @param string $package Name of the Composer Package
137
     * @param array $updateInformation Data to write to the model
138
     */
139
    protected function recordUpdate($package, array $updateInformation)
140
    {
141
        // Is there a record already for the package? If so find it.
142
        $packages = Package::get()->filter(['Name' => $package]);
143
144
        // if there is already one use it otherwise create a new data object
145
        if ($packages->count() > 0) {
146
            $update = $packages->first();
147
        } else {
148
            $update = Package::create();
149
            $update->Name = $package;
150
        }
151
152
        /** @var DataObject $update */
153
        $update->update($updateInformation)->write();
154
    }
155
}
156