[ Index ]

PHP Cross Reference of YOURLS

title

Body

[close]

/includes/vendor/composer/ -> InstalledVersions.php (source)

   1  <?php
   2  
   3  /*
   4   * This file is part of Composer.
   5   *
   6   * (c) Nils Adermann <[email protected]>
   7   *     Jordi Boggiano <[email protected]>
   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 its presence, you can require `composer-runtime-api ^2.0`
  24   *
  25   * @final
  26   */
  27  class InstalledVersions
  28  {
  29      /**
  30       * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
  31       * @internal
  32       */
  33      private static $selfDir = null;
  34  
  35      /**
  36       * @var mixed[]|null
  37       * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
  38       */
  39      private static $installed;
  40  
  41      /**
  42       * @var bool
  43       */
  44      private static $installedIsLocalDir;
  45  
  46      /**
  47       * @var bool|null
  48       */
  49      private static $canGetVendors;
  50  
  51      /**
  52       * @var array[]
  53       * @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
  54       */
  55      private static $installedByVendor = array();
  56  
  57      /**
  58       * Returns a list of all package names which are present, either by being installed, replaced or provided
  59       *
  60       * @return string[]
  61       * @psalm-return list<string>
  62       */
  63      public static function getInstalledPackages()
  64      {
  65          $packages = array();
  66          foreach (self::getInstalled() as $installed) {
  67              $packages[] = array_keys($installed['versions']);
  68          }
  69  
  70          if (1 === \count($packages)) {
  71              return $packages[0];
  72          }
  73  
  74          return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
  75      }
  76  
  77      /**
  78       * Returns a list of all package names with a specific type e.g. 'library'
  79       *
  80       * @param  string   $type
  81       * @return string[]
  82       * @psalm-return list<string>
  83       */
  84      public static function getInstalledPackagesByType($type)
  85      {
  86          $packagesByType = array();
  87  
  88          foreach (self::getInstalled() as $installed) {
  89              foreach ($installed['versions'] as $name => $package) {
  90                  if (isset($package['type']) && $package['type'] === $type) {
  91                      $packagesByType[] = $name;
  92                  }
  93              }
  94          }
  95  
  96          return $packagesByType;
  97      }
  98  
  99      /**
 100       * Checks whether the given package is installed
 101       *
 102       * This also returns true if the package name is provided or replaced by another package
 103       *
 104       * @param  string $packageName
 105       * @param  bool   $includeDevRequirements
 106       * @return bool
 107       */
 108      public static function isInstalled($packageName, $includeDevRequirements = true)
 109      {
 110          foreach (self::getInstalled() as $installed) {
 111              if (isset($installed['versions'][$packageName])) {
 112                  return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
 113              }
 114          }
 115  
 116          return false;
 117      }
 118  
 119      /**
 120       * Checks whether the given package satisfies a version constraint
 121       *
 122       * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
 123       *
 124       *   Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
 125       *
 126       * @param  VersionParser $parser      Install composer/semver to have access to this class and functionality
 127       * @param  string        $packageName
 128       * @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
 129       * @return bool
 130       */
 131      public static function satisfies(VersionParser $parser, $packageName, $constraint)
 132      {
 133          $constraint = $parser->parseConstraints((string) $constraint);
 134          $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
 135  
 136          return $provided->matches($constraint);
 137      }
 138  
 139      /**
 140       * Returns a version constraint representing all the range(s) which are installed for a given package
 141       *
 142       * It is easier to use this via isInstalled() with the $constraint argument if you need to check
 143       * whether a given version of a package is installed, and not just whether it exists
 144       *
 145       * @param  string $packageName
 146       * @return string Version constraint usable with composer/semver
 147       */
 148      public static function getVersionRanges($packageName)
 149      {
 150          foreach (self::getInstalled() as $installed) {
 151              if (!isset($installed['versions'][$packageName])) {
 152                  continue;
 153              }
 154  
 155              $ranges = array();
 156              if (isset($installed['versions'][$packageName]['pretty_version'])) {
 157                  $ranges[] = $installed['versions'][$packageName]['pretty_version'];
 158              }
 159              if (array_key_exists('aliases', $installed['versions'][$packageName])) {
 160                  $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
 161              }
 162              if (array_key_exists('replaced', $installed['versions'][$packageName])) {
 163                  $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
 164              }
 165              if (array_key_exists('provided', $installed['versions'][$packageName])) {
 166                  $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
 167              }
 168  
 169              return implode(' || ', $ranges);
 170          }
 171  
 172          throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
 173      }
 174  
 175      /**
 176       * @param  string      $packageName
 177       * @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
 178       */
 179      public static function getVersion($packageName)
 180      {
 181          foreach (self::getInstalled() as $installed) {
 182              if (!isset($installed['versions'][$packageName])) {
 183                  continue;
 184              }
 185  
 186              if (!isset($installed['versions'][$packageName]['version'])) {
 187                  return null;
 188              }
 189  
 190              return $installed['versions'][$packageName]['version'];
 191          }
 192  
 193          throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
 194      }
 195  
 196      /**
 197       * @param  string      $packageName
 198       * @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
 199       */
 200      public static function getPrettyVersion($packageName)
 201      {
 202          foreach (self::getInstalled() as $installed) {
 203              if (!isset($installed['versions'][$packageName])) {
 204                  continue;
 205              }
 206  
 207              if (!isset($installed['versions'][$packageName]['pretty_version'])) {
 208                  return null;
 209              }
 210  
 211              return $installed['versions'][$packageName]['pretty_version'];
 212          }
 213  
 214          throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
 215      }
 216  
 217      /**
 218       * @param  string      $packageName
 219       * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
 220       */
 221      public static function getReference($packageName)
 222      {
 223          foreach (self::getInstalled() as $installed) {
 224              if (!isset($installed['versions'][$packageName])) {
 225                  continue;
 226              }
 227  
 228              if (!isset($installed['versions'][$packageName]['reference'])) {
 229                  return null;
 230              }
 231  
 232              return $installed['versions'][$packageName]['reference'];
 233          }
 234  
 235          throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
 236      }
 237  
 238      /**
 239       * @param  string      $packageName
 240       * @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.
 241       */
 242      public static function getInstallPath($packageName)
 243      {
 244          foreach (self::getInstalled() as $installed) {
 245              if (!isset($installed['versions'][$packageName])) {
 246                  continue;
 247              }
 248  
 249              return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
 250          }
 251  
 252          throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
 253      }
 254  
 255      /**
 256       * @return array
 257       * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
 258       */
 259      public static function getRootPackage()
 260      {
 261          $installed = self::getInstalled();
 262  
 263          return $installed[0]['root'];
 264      }
 265  
 266      /**
 267       * Returns the raw installed.php data for custom implementations
 268       *
 269       * @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.
 270       * @return array[]
 271       * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
 272       */
 273      public static function getRawData()
 274      {
 275          @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);
 276  
 277          if (null === self::$installed) {
 278              // only require the installed.php file if this file is loaded from its dumped location,
 279              // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
 280              if (substr(__DIR__, -8, 1) !== 'C') {
 281                  self::$installed = include  __DIR__ . '/installed.php';
 282              } else {
 283                  self::$installed = array();
 284              }
 285          }
 286  
 287          return self::$installed;
 288      }
 289  
 290      /**
 291       * Returns the raw data of all installed.php which are currently loaded for custom implementations
 292       *
 293       * @return array[]
 294       * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
 295       */
 296      public static function getAllRawData()
 297      {
 298          return self::getInstalled();
 299      }
 300  
 301      /**
 302       * Lets you reload the static array from another file
 303       *
 304       * This is only useful for complex integrations in which a project needs to use
 305       * this class but then also needs to execute another project's autoloader in process,
 306       * and wants to ensure both projects have access to their version of installed.php.
 307       *
 308       * A typical case would be PHPUnit, where it would need to make sure it reads all
 309       * the data it needs from this class, then call reload() with
 310       * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
 311       * the project in which it runs can then also use this class safely, without
 312       * interference between PHPUnit's dependencies and the project's dependencies.
 313       *
 314       * @param  array[] $data A vendor/composer/installed.php data set
 315       * @return void
 316       *
 317       * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
 318       */
 319      public static function reload($data)
 320      {
 321          self::$installed = $data;
 322          self::$installedByVendor = array();
 323  
 324          // when using reload, we disable the duplicate protection to ensure that self::$installed data is
 325          // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
 326          // so we have to assume it does not, and that may result in duplicate data being returned when listing
 327          // all installed packages for example
 328          self::$installedIsLocalDir = false;
 329      }
 330  
 331      /**
 332       * @return string
 333       */
 334      private static function getSelfDir()
 335      {
 336          if (self::$selfDir === null) {
 337              self::$selfDir = strtr(__DIR__, '\\', '/');
 338          }
 339  
 340          return self::$selfDir;
 341      }
 342  
 343      /**
 344       * @return array[]
 345       * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
 346       */
 347      private static function getInstalled()
 348      {
 349          if (null === self::$canGetVendors) {
 350              self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
 351          }
 352  
 353          $installed = array();
 354          $copiedLocalDir = false;
 355  
 356          if (self::$canGetVendors) {
 357              $selfDir = self::getSelfDir();
 358              foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
 359                  $vendorDir = strtr($vendorDir, '\\', '/');
 360                  if (isset(self::$installedByVendor[$vendorDir])) {
 361                      $installed[] = self::$installedByVendor[$vendorDir];
 362                  } elseif (is_file($vendorDir.'/composer/installed.php')) {
 363                      /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
 364                      $required = require $vendorDir.'/composer/installed.php';
 365                      self::$installedByVendor[$vendorDir] = $required;
 366                      $installed[] = $required;
 367                      if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
 368                          self::$installed = $required;
 369                          self::$installedIsLocalDir = true;
 370                      }
 371                  }
 372                  if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
 373                      $copiedLocalDir = true;
 374                  }
 375              }
 376          }
 377  
 378          if (null === self::$installed) {
 379              // only require the installed.php file if this file is loaded from its dumped location,
 380              // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
 381              if (substr(__DIR__, -8, 1) !== 'C') {
 382                  /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
 383                  $required = require __DIR__ . '/installed.php';
 384                  self::$installed = $required;
 385              } else {
 386                  self::$installed = array();
 387              }
 388          }
 389  
 390          if (self::$installed !== array() && !$copiedLocalDir) {
 391              $installed[] = self::$installed;
 392          }
 393  
 394          return $installed;
 395      }
 396  }


Generated: Tue Jan 6 05:10:29 2026 Cross-referenced by PHPXref 0.7.1