• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

eliashaeussler / composer-update-check / 20379468257

19 Dec 2025 06:48PM UTC coverage: 20.675%. First build
20379468257

Pull #130

github

web-flow
[TASK] Update all dependencies (#208)

* [TASK] Update all dependencies

* [TASK] Resolve Composer 2.9 deprecations

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Elias Häußler <elias@haeussler.dev>
Pull Request #130: [!!!][FEATURE] Modernize plugin

360 of 1839 new or added lines in 57 files covered. (19.58%)

386 of 1867 relevant lines covered (20.67%)

1.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.38
/src/Composer/Installer.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the Composer package "eliashaeussler/composer-update-check".
7
 *
8
 * Copyright (C) 2020-2025 Elias Häußler <elias@haeussler.dev>
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, either version 3 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22
 */
23

24
namespace EliasHaeussler\ComposerUpdateCheck\Composer;
25

26
use Composer\Advisory\AuditConfig;
27
use Composer\Composer;
28
use Composer\DependencyResolver;
29
use Composer\Factory;
30
use Composer\Installer as ComposerInstaller;
31
use Composer\IO;
32
use Composer\Package;
33
use Composer\Repository;
34
use EliasHaeussler\ComposerUpdateCheck\Entity;
35

36
use function array_map;
37
use function array_values;
38
use function method_exists;
39

40
/**
41
 * Installer.
42
 *
43
 * @author Elias Häußler <elias@haeussler.dev>
44
 * @license GPL-3.0-or-later
45
 *
46
 * @internal
47
 */
48
final class Installer
49
{
50
    public function __construct(
4✔
51
        private readonly Composer $composer,
52
    ) {}
4✔
53

54
    public function runInstall(?IO\IOInterface $io = null): int
4✔
55
    {
56
        $io ??= new IO\NullIO();
4✔
57

58
        $composer = $this->buildComposer($io);
4✔
59
        $preferredInstall = $composer->getConfig()->get('preferred-install');
4✔
60
        $installer = ComposerInstaller::create($io, $composer)
4✔
61
            ->setPreferSource('source' === $preferredInstall)
4✔
62
            ->setPreferDist('dist' === $preferredInstall)
4✔
63
            ->setDevMode()
4✔
64
        ;
4✔
65

66
        if (method_exists($installer, 'setAuditConfig')) {
4✔
67
            // Composer >= 2.9
68
            $installer->setAuditConfig(
4✔
69
                AuditConfig::fromConfig($composer->getConfig(), false),
4✔
70
            );
4✔
NEW
71
        } elseif (method_exists($installer, 'setAudit')) {
×
72
            // Composer < 2.9
NEW
73
            $installer->setAudit(false);
×
74
        }
75

76
        return $installer->run();
4✔
77
    }
78

79
    /**
80
     * @param list<Entity\Package\Package> $packages
81
     */
82
    public function runUpdate(array $packages, ?IO\IOInterface $io = null): Entity\Result\ComposerUpdateResult
3✔
83
    {
84
        $io ??= new IO\NullIO();
3✔
85

86
        $outdatedPackages = [];
3✔
87
        $allowedPackageNames = array_map(
3✔
88
            static fn (Entity\Package\Package $package) => $package->getName(),
3✔
89
            $packages,
3✔
90
        );
3✔
91

92
        // Inject installer logger
93
        $composer = $this->buildComposer($io);
3✔
94
        $composer->getInstallationManager()->addInstaller(
3✔
95
            $this->mockInstaller($allowedPackageNames, $outdatedPackages),
3✔
96
        );
3✔
97

98
        // Disable writing of package updates in local repository
99
        $composer->getRepositoryManager()->setLocalRepository(
3✔
100
            $this->mockLocalRepository($composer->getRepositoryManager()->getLocalRepository()),
3✔
101
        );
3✔
102

103
        // Disable lock file management
104
        $composer->getConfig()->merge([
3✔
105
            'config' => [
3✔
106
                'lock' => false,
3✔
107
            ],
3✔
108
        ]);
3✔
109

110
        $preferredInstall = $composer->getConfig()->get('preferred-install');
3✔
111
        $installer = ComposerInstaller::create($io, $composer)
3✔
112
            ->setPreferSource('source' === $preferredInstall)
3✔
113
            ->setPreferDist('dist' === $preferredInstall)
3✔
114
            ->setDevMode()
3✔
115
            ->setUpdate(true)
3✔
116
            ->setDumpAutoloader(false)
3✔
117
            ->setUpdateAllowList($allowedPackageNames)
3✔
118
            ->setUpdateAllowTransitiveDependencies(DependencyResolver\Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS)
3✔
119
        ;
3✔
120

121
        if (method_exists($installer, 'setAuditConfig')) {
3✔
122
            // Composer >= 2.9
123
            $installer->setAuditConfig(
3✔
124
                AuditConfig::fromConfig($composer->getConfig(), false),
3✔
125
            );
3✔
NEW
126
        } elseif (method_exists($installer, 'setAudit')) {
×
127
            // Composer < 2.9
NEW
128
            $installer->setAudit(false);
×
129
        }
130

131
        return new Entity\Result\ComposerUpdateResult(
3✔
132
            $installer->run(),
3✔
133
            array_values($outdatedPackages),
3✔
134
        );
3✔
135
    }
136

137
    /**
138
     * @param list<non-empty-string>                                  $allowedPackageNames
139
     * @param array<non-empty-string, Entity\Package\OutdatedPackage> $outdatedPackages
140
     */
141
    private function mockInstaller(
3✔
142
        array $allowedPackageNames,
143
        array &$outdatedPackages,
144
    ): ComposerInstaller\InstallerInterface {
145
        return new class($allowedPackageNames, $outdatedPackages) extends ComposerInstaller\NoopInstaller {
3✔
146
            /**
147
             * @param list<non-empty-string>                                  $allowedPackageNames
148
             * @param array<non-empty-string, Entity\Package\OutdatedPackage> $outdatedPackages
149
             */
150
            public function __construct(
151
                private readonly array $allowedPackageNames,
152
                private array &$outdatedPackages,
153
            ) {}
3✔
154

155
            public function update(
156
                Repository\InstalledRepositoryInterface $repo,
157
                Package\PackageInterface $initial,
158
                Package\PackageInterface $target,
159
            ) {
160
                $promise = parent::update($repo, $initial, $target);
3✔
161
                /** @var non-empty-string $name */
162
                $name = $initial->getName();
3✔
163

164
                if ($this->isSuitableUpdate($initial, $target)) {
3✔
165
                    $this->outdatedPackages[$name] = new Entity\Package\OutdatedPackage(
2✔
166
                        $name,
2✔
167
                        new Entity\Version($initial->getPrettyVersion()),
2✔
168
                        new Entity\Version($target->getPrettyVersion()),
2✔
169
                    );
2✔
170
                }
171

172
                return $promise;
3✔
173
            }
174

175
            private function isSuitableUpdate(
176
                Package\PackageInterface $initial,
177
                Package\PackageInterface $target,
178
            ): bool {
179
                $name = $initial->getName();
3✔
180

181
                if (!in_array($name, $this->allowedPackageNames, true)) {
3✔
182
                    return false;
3✔
183
                }
184

185
                if (array_key_exists($name, $this->outdatedPackages)) {
2✔
NEW
186
                    return false;
×
187
                }
188

189
                return $initial->getPrettyVersion() !== $target->getPrettyVersion();
2✔
190
            }
191
        };
3✔
192
    }
193

194
    private function mockLocalRepository(
3✔
195
        Repository\InstalledRepositoryInterface $repository,
196
    ): Repository\InstalledRepositoryInterface {
197
        $newRepository = new Repository\InstalledArrayRepository();
3✔
198
        $newRepository->setDevPackageNames($repository->getDevPackageNames());
3✔
199

200
        foreach ($repository->getPackages() as $package) {
3✔
201
            $newPackage = clone $package;
3✔
202
            $newPackage->setId($package->getId());
3✔
203
            $newRepository->addPackage($newPackage);
3✔
204
        }
205

206
        return $newRepository;
3✔
207
    }
208

209
    private function buildComposer(IO\IOInterface $io): Composer
4✔
210
    {
211
        $composer = Factory::create($io, $this->composer->getConfig()->getConfigSource()->getName(), true);
4✔
212
        $composer->getEventDispatcher()->setRunScripts(false);
4✔
213
        $composer->getAutoloadGenerator()->setRunScripts(false);
4✔
214

215
        return $composer;
4✔
216
    }
217
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc