Projeto

Geral

Perfil

Estatísticas
| Branch: | Revisão:

expressolivretools / vendor / composer / InstalledVersions.php @ 68c328a2

Histórico | Ver | Anotar | Baixar (13,6 KB)

1
<?php
2

    
3
/*
4
 * This file is part of Composer.
5
 *
6
 * (c) Nils Adermann <naderman@naderman.de>
7
 *     Jordi Boggiano <j.boggiano@seld.be>
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12

    
13
namespace Composer;
14

    
15
use Composer\Autoload\ClassLoader;
16
use Composer\Semver\VersionParser;
17

    
18
/**
19
 * This class is copied in every Composer installed project and available to all
20
 *
21
 * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
22
 *
23
 * To require it's presence, you can require `composer-runtime-api ^2.0`
24
 */
25
class InstalledVersions
26
{
27
    private static $installed;
28
    private static $canGetVendors;
29
    private static $installedByVendor = array();
30

    
31
    /**
32
     * Returns a list of all package names which are present, either by being installed, replaced or provided
33
     *
34
     * @return string[]
35
     * @psalm-return list<string>
36
     */
37
    public static function getInstalledPackages()
38
    {
39
        $packages = array();
40
        foreach (self::getInstalled() as $installed) {
41
            $packages[] = array_keys($installed['versions']);
42
        }
43

    
44
        if (1 === \count($packages)) {
45
            return $packages[0];
46
        }
47

    
48
        return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
49
    }
50

    
51
    /**
52
     * Returns a list of all package names with a specific type e.g. 'library'
53
     *
54
     * @param  string   $type
55
     * @return string[]
56
     * @psalm-return list<string>
57
     */
58
    public static function getInstalledPackagesByType($type)
59
    {
60
        $packagesByType = array();
61

    
62
        foreach (self::getInstalled() as $installed) {
63
            foreach ($installed['versions'] as $name => $package) {
64
                if (isset($package['type']) && $package['type'] === $type) {
65
                    $packagesByType[] = $name;
66
                }
67
            }
68
        }
69

    
70
        return $packagesByType;
71
    }
72

    
73
    /**
74
     * Checks whether the given package is installed
75
     *
76
     * This also returns true if the package name is provided or replaced by another package
77
     *
78
     * @param  string $packageName
79
     * @param  bool   $includeDevRequirements
80
     * @return bool
81
     */
82
    public static function isInstalled($packageName, $includeDevRequirements = true)
83
    {
84
        foreach (self::getInstalled() as $installed) {
85
            if (isset($installed['versions'][$packageName])) {
86
                return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
87
            }
88
        }
89

    
90
        return false;
91
    }
92

    
93
    /**
94
     * Checks whether the given package satisfies a version constraint
95
     *
96
     * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
97
     *
98
     *   Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
99
     *
100
     * @param  VersionParser $parser      Install composer/semver to have access to this class and functionality
101
     * @param  string        $packageName
102
     * @param  string|null   $constraint  A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
103
     * @return bool
104
     */
105
    public static function satisfies(VersionParser $parser, $packageName, $constraint)
106
    {
107
        $constraint = $parser->parseConstraints($constraint);
108
        $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
109

    
110
        return $provided->matches($constraint);
111
    }
112

    
113
    /**
114
     * Returns a version constraint representing all the range(s) which are installed for a given package
115
     *
116
     * It is easier to use this via isInstalled() with the $constraint argument if you need to check
117
     * whether a given version of a package is installed, and not just whether it exists
118
     *
119
     * @param  string $packageName
120
     * @return string Version constraint usable with composer/semver
121
     */
122
    public static function getVersionRanges($packageName)
123
    {
124
        foreach (self::getInstalled() as $installed) {
125
            if (!isset($installed['versions'][$packageName])) {
126
                continue;
127
            }
128

    
129
            $ranges = array();
130
            if (isset($installed['versions'][$packageName]['pretty_version'])) {
131
                $ranges[] = $installed['versions'][$packageName]['pretty_version'];
132
            }
133
            if (array_key_exists('aliases', $installed['versions'][$packageName])) {
134
                $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
135
            }
136
            if (array_key_exists('replaced', $installed['versions'][$packageName])) {
137
                $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
138
            }
139
            if (array_key_exists('provided', $installed['versions'][$packageName])) {
140
                $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
141
            }
142

    
143
            return implode(' || ', $ranges);
144
        }
145

    
146
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
147
    }
148

    
149
    /**
150
     * @param  string      $packageName
151
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
152
     */
153
    public static function getVersion($packageName)
154
    {
155
        foreach (self::getInstalled() as $installed) {
156
            if (!isset($installed['versions'][$packageName])) {
157
                continue;
158
            }
159

    
160
            if (!isset($installed['versions'][$packageName]['version'])) {
161
                return null;
162
            }
163

    
164
            return $installed['versions'][$packageName]['version'];
165
        }
166

    
167
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
168
    }
169

    
170
    /**
171
     * @param  string      $packageName
172
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
173
     */
174
    public static function getPrettyVersion($packageName)
175
    {
176
        foreach (self::getInstalled() as $installed) {
177
            if (!isset($installed['versions'][$packageName])) {
178
                continue;
179
            }
180

    
181
            if (!isset($installed['versions'][$packageName]['pretty_version'])) {
182
                return null;
183
            }
184

    
185
            return $installed['versions'][$packageName]['pretty_version'];
186
        }
187

    
188
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
189
    }
190

    
191
    /**
192
     * @param  string      $packageName
193
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
194
     */
195
    public static function getReference($packageName)
196
    {
197
        foreach (self::getInstalled() as $installed) {
198
            if (!isset($installed['versions'][$packageName])) {
199
                continue;
200
            }
201

    
202
            if (!isset($installed['versions'][$packageName]['reference'])) {
203
                return null;
204
            }
205

    
206
            return $installed['versions'][$packageName]['reference'];
207
        }
208

    
209
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
210
    }
211

    
212
    /**
213
     * @param  string      $packageName
214
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
215
     */
216
    public static function getInstallPath($packageName)
217
    {
218
        foreach (self::getInstalled() as $installed) {
219
            if (!isset($installed['versions'][$packageName])) {
220
                continue;
221
            }
222

    
223
            return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
224
        }
225

    
226
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
227
    }
228

    
229
    /**
230
     * @return array
231
     * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}
232
     */
233
    public static function getRootPackage()
234
    {
235
        $installed = self::getInstalled();
236

    
237
        return $installed[0]['root'];
238
    }
239

    
240
    /**
241
     * Returns the raw installed.php data for custom implementations
242
     *
243
     * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
244
     * @return array[]
245
     * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}
246
     */
247
    public static function getRawData()
248
    {
249
        @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
250

    
251
        if (null === self::$installed) {
252
            // only require the installed.php file if this file is loaded from its dumped location,
253
            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
254
            if (substr(__DIR__, -8, 1) !== 'C') {
255
                self::$installed = include __DIR__ . '/installed.php';
256
            } else {
257
                self::$installed = array();
258
            }
259
        }
260

    
261
        return self::$installed;
262
    }
263

    
264
    /**
265
     * Returns the raw data of all installed.php which are currently loaded for custom implementations
266
     *
267
     * @return array[]
268
     * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}>
269
     */
270
    public static function getAllRawData()
271
    {
272
        return self::getInstalled();
273
    }
274

    
275
    /**
276
     * Lets you reload the static array from another file
277
     *
278
     * This is only useful for complex integrations in which a project needs to use
279
     * this class but then also needs to execute another project's autoloader in process,
280
     * and wants to ensure both projects have access to their version of installed.php.
281
     *
282
     * A typical case would be PHPUnit, where it would need to make sure it reads all
283
     * the data it needs from this class, then call reload() with
284
     * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
285
     * the project in which it runs can then also use this class safely, without
286
     * interference between PHPUnit's dependencies and the project's dependencies.
287
     *
288
     * @param  array[] $data A vendor/composer/installed.php data set
289
     * @return void
290
     *
291
     * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>} $data
292
     */
293
    public static function reload($data)
294
    {
295
        self::$installed = $data;
296
        self::$installedByVendor = array();
297
    }
298

    
299
    /**
300
     * @return array[]
301
     * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}>
302
     */
303
    private static function getInstalled()
304
    {
305
        if (null === self::$canGetVendors) {
306
            self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
307
        }
308

    
309
        $installed = array();
310

    
311
        if (self::$canGetVendors) {
312
            foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
313
                if (isset(self::$installedByVendor[$vendorDir])) {
314
                    $installed[] = self::$installedByVendor[$vendorDir];
315
                } elseif (is_file($vendorDir.'/composer/installed.php')) {
316
                    $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
317
                    if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
318
                        self::$installed = $installed[count($installed) - 1];
319
                    }
320
                }
321
            }
322
        }
323

    
324
        if (null === self::$installed) {
325
            // only require the installed.php file if this file is loaded from its dumped location,
326
            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
327
            if (substr(__DIR__, -8, 1) !== 'C') {
328
                self::$installed = require __DIR__ . '/installed.php';
329
            } else {
330
                self::$installed = array();
331
            }
332
        }
333
        $installed[] = self::$installed;
334

    
335
        return $installed;
336
    }
337
}