InstalledVersions.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. <?php
  2. /*
  3. * This file is part of Composer.
  4. *
  5. * (c) Nils Adermann <naderman@naderman.de>
  6. * Jordi Boggiano <j.boggiano@seld.be>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Composer;
  12. use Composer\Autoload\ClassLoader;
  13. use Composer\Semver\VersionParser;
  14. /**
  15. * This class is copied in every Composer installed project and available to all
  16. *
  17. * To require it's presence, you can require `composer-runtime-api ^2.0`
  18. */
  19. class InstalledVersions
  20. {
  21. private static $installed = array (
  22. 'root' =>
  23. array (
  24. 'pretty_version' => '1.0.0+no-version-set',
  25. 'version' => '1.0.0.0',
  26. 'aliases' =>
  27. array (
  28. ),
  29. 'reference' => NULL,
  30. 'name' => '__root__',
  31. ),
  32. 'versions' =>
  33. array (
  34. '__root__' =>
  35. array (
  36. 'pretty_version' => '1.0.0+no-version-set',
  37. 'version' => '1.0.0.0',
  38. 'aliases' =>
  39. array (
  40. ),
  41. 'reference' => NULL,
  42. ),
  43. 'discord-php/http' =>
  44. array (
  45. 'pretty_version' => 'v9.1.8',
  46. 'version' => '9.1.8.0',
  47. 'aliases' =>
  48. array (
  49. ),
  50. 'reference' => '8dc95a63836f7f0161fc5e52e1d9feeaa36b36ba',
  51. ),
  52. 'discord/interactions' =>
  53. array (
  54. 'pretty_version' => '2.2.0',
  55. 'version' => '2.2.0.0',
  56. 'aliases' =>
  57. array (
  58. ),
  59. 'reference' => 'a6fc0c877b75cf5ff5811f2ea69c5cc4ad6ac457',
  60. ),
  61. 'evenement/evenement' =>
  62. array (
  63. 'pretty_version' => 'v3.0.1',
  64. 'version' => '3.0.1.0',
  65. 'aliases' =>
  66. array (
  67. ),
  68. 'reference' => '531bfb9d15f8aa57454f5f0285b18bec903b8fb7',
  69. ),
  70. 'fig/http-message-util' =>
  71. array (
  72. 'pretty_version' => '1.1.5',
  73. 'version' => '1.1.5.0',
  74. 'aliases' =>
  75. array (
  76. ),
  77. 'reference' => '9d94dc0154230ac39e5bf89398b324a86f63f765',
  78. ),
  79. 'guzzlehttp/psr7' =>
  80. array (
  81. 'pretty_version' => '2.4.3',
  82. 'version' => '2.4.3.0',
  83. 'aliases' =>
  84. array (
  85. ),
  86. 'reference' => '67c26b443f348a51926030c83481b85718457d3d',
  87. ),
  88. 'mollie/polyfill-libsodium' =>
  89. array (
  90. 'pretty_version' => 'v1.1.1',
  91. 'version' => '1.1.1.0',
  92. 'aliases' =>
  93. array (
  94. ),
  95. 'reference' => '60351707e048080a2218a97296f88733f050f183',
  96. ),
  97. 'monolog/monolog' =>
  98. array (
  99. 'pretty_version' => '2.9.1',
  100. 'version' => '2.9.1.0',
  101. 'aliases' =>
  102. array (
  103. ),
  104. 'reference' => 'f259e2b15fb95494c83f52d3caad003bbf5ffaa1',
  105. ),
  106. 'nesbot/carbon' =>
  107. array (
  108. 'pretty_version' => '2.66.0',
  109. 'version' => '2.66.0.0',
  110. 'aliases' =>
  111. array (
  112. ),
  113. 'reference' => '496712849902241f04902033b0441b269effe001',
  114. ),
  115. 'psr/http-factory' =>
  116. array (
  117. 'pretty_version' => '1.0.1',
  118. 'version' => '1.0.1.0',
  119. 'aliases' =>
  120. array (
  121. ),
  122. 'reference' => '12ac7fcd07e5b077433f5f2bee95b3a771bf61be',
  123. ),
  124. 'psr/http-factory-implementation' =>
  125. array (
  126. 'provided' =>
  127. array (
  128. 0 => '1.0',
  129. ),
  130. ),
  131. 'psr/http-message' =>
  132. array (
  133. 'pretty_version' => '1.0.1',
  134. 'version' => '1.0.1.0',
  135. 'aliases' =>
  136. array (
  137. ),
  138. 'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363',
  139. ),
  140. 'psr/http-message-implementation' =>
  141. array (
  142. 'provided' =>
  143. array (
  144. 0 => '1.0',
  145. ),
  146. ),
  147. 'psr/log' =>
  148. array (
  149. 'pretty_version' => '1.1.4',
  150. 'version' => '1.1.4.0',
  151. 'aliases' =>
  152. array (
  153. ),
  154. 'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11',
  155. ),
  156. 'psr/log-implementation' =>
  157. array (
  158. 'provided' =>
  159. array (
  160. 0 => '1.0.0 || 2.0.0 || 3.0.0',
  161. ),
  162. ),
  163. 'ralouphie/getallheaders' =>
  164. array (
  165. 'pretty_version' => '3.0.3',
  166. 'version' => '3.0.3.0',
  167. 'aliases' =>
  168. array (
  169. ),
  170. 'reference' => '120b605dfeb996808c31b6477290a714d356e822',
  171. ),
  172. 'ratchet/pawl' =>
  173. array (
  174. 'pretty_version' => 'v0.4.1',
  175. 'version' => '0.4.1.0',
  176. 'aliases' =>
  177. array (
  178. ),
  179. 'reference' => 'af70198bab77a582b31169d3cc3982bed25c161f',
  180. ),
  181. 'ratchet/rfc6455' =>
  182. array (
  183. 'pretty_version' => 'v0.3.1',
  184. 'version' => '0.3.1.0',
  185. 'aliases' =>
  186. array (
  187. ),
  188. 'reference' => '7c964514e93456a52a99a20fcfa0de242a43ccdb',
  189. ),
  190. 'react/cache' =>
  191. array (
  192. 'pretty_version' => 'v1.2.0',
  193. 'version' => '1.2.0.0',
  194. 'aliases' =>
  195. array (
  196. ),
  197. 'reference' => 'd47c472b64aa5608225f47965a484b75c7817d5b',
  198. ),
  199. 'react/child-process' =>
  200. array (
  201. 'pretty_version' => 'v0.6.5',
  202. 'version' => '0.6.5.0',
  203. 'aliases' =>
  204. array (
  205. ),
  206. 'reference' => 'e71eb1aa55f057c7a4a0d08d06b0b0a484bead43',
  207. ),
  208. 'react/datagram' =>
  209. array (
  210. 'pretty_version' => 'v1.5.0',
  211. 'version' => '1.5.0.0',
  212. 'aliases' =>
  213. array (
  214. ),
  215. 'reference' => 'e4c5bf8daf44630f0a938f40fe2104b2b76ad2ff',
  216. ),
  217. 'react/dns' =>
  218. array (
  219. 'pretty_version' => 'v1.10.0',
  220. 'version' => '1.10.0.0',
  221. 'aliases' =>
  222. array (
  223. ),
  224. 'reference' => 'a5427e7dfa47713e438016905605819d101f238c',
  225. ),
  226. 'react/event-loop' =>
  227. array (
  228. 'pretty_version' => 'v1.3.0',
  229. 'version' => '1.3.0.0',
  230. 'aliases' =>
  231. array (
  232. ),
  233. 'reference' => '187fb56f46d424afb6ec4ad089269c72eec2e137',
  234. ),
  235. 'react/http' =>
  236. array (
  237. 'pretty_version' => 'v1.8.0',
  238. 'version' => '1.8.0.0',
  239. 'aliases' =>
  240. array (
  241. ),
  242. 'reference' => 'aa7512ee17258c88466de30f9cb44ec5f9df3ff3',
  243. ),
  244. 'react/partial' =>
  245. array (
  246. 'pretty_version' => 'v3.0.0',
  247. 'version' => '3.0.0.0',
  248. 'aliases' =>
  249. array (
  250. ),
  251. 'reference' => 'e06f034747561977670607888e0a99fd282308ab',
  252. ),
  253. 'react/promise' =>
  254. array (
  255. 'pretty_version' => 'v2.9.0',
  256. 'version' => '2.9.0.0',
  257. 'aliases' =>
  258. array (
  259. ),
  260. 'reference' => '234f8fd1023c9158e2314fa9d7d0e6a83db42910',
  261. ),
  262. 'react/promise-stream' =>
  263. array (
  264. 'pretty_version' => 'v1.5.0',
  265. 'version' => '1.5.0.0',
  266. 'aliases' =>
  267. array (
  268. ),
  269. 'reference' => 'e6d2805e09ad50c4896f65f5e8705fe4ee7731a3',
  270. ),
  271. 'react/promise-timer' =>
  272. array (
  273. 'pretty_version' => 'v1.9.0',
  274. 'version' => '1.9.0.0',
  275. 'aliases' =>
  276. array (
  277. ),
  278. 'reference' => 'aa7a73c74b8d8c0f622f5982ff7b0351bc29e495',
  279. ),
  280. 'react/socket' =>
  281. array (
  282. 'pretty_version' => 'v1.12.0',
  283. 'version' => '1.12.0.0',
  284. 'aliases' =>
  285. array (
  286. ),
  287. 'reference' => '81e1b4d7f5450ebd8d2e9a95bb008bb15ca95a7b',
  288. ),
  289. 'react/stream' =>
  290. array (
  291. 'pretty_version' => 'v1.2.0',
  292. 'version' => '1.2.0.0',
  293. 'aliases' =>
  294. array (
  295. ),
  296. 'reference' => '7a423506ee1903e89f1e08ec5f0ed430ff784ae9',
  297. ),
  298. 'ringcentral/psr7' =>
  299. array (
  300. 'pretty_version' => '1.3.0',
  301. 'version' => '1.3.0.0',
  302. 'aliases' =>
  303. array (
  304. ),
  305. 'reference' => '360faaec4b563958b673fb52bbe94e37f14bc686',
  306. ),
  307. 'symfony/deprecation-contracts' =>
  308. array (
  309. 'pretty_version' => 'v2.5.2',
  310. 'version' => '2.5.2.0',
  311. 'aliases' =>
  312. array (
  313. ),
  314. 'reference' => 'e8b495ea28c1d97b5e0c121748d6f9b53d075c66',
  315. ),
  316. 'symfony/options-resolver' =>
  317. array (
  318. 'pretty_version' => 'v5.4.19',
  319. 'version' => '5.4.19.0',
  320. 'aliases' =>
  321. array (
  322. ),
  323. 'reference' => 'b03c99236445492f20c61666e8f7e5d388b078e5',
  324. ),
  325. 'symfony/polyfill-mbstring' =>
  326. array (
  327. 'pretty_version' => 'v1.27.0',
  328. 'version' => '1.27.0.0',
  329. 'aliases' =>
  330. array (
  331. ),
  332. 'reference' => '8ad114f6b39e2c98a8b0e3bd907732c207c2b534',
  333. ),
  334. 'symfony/polyfill-php73' =>
  335. array (
  336. 'pretty_version' => 'v1.27.0',
  337. 'version' => '1.27.0.0',
  338. 'aliases' =>
  339. array (
  340. ),
  341. 'reference' => '9e8ecb5f92152187c4799efd3c96b78ccab18ff9',
  342. ),
  343. 'symfony/polyfill-php80' =>
  344. array (
  345. 'pretty_version' => 'v1.27.0',
  346. 'version' => '1.27.0.0',
  347. 'aliases' =>
  348. array (
  349. ),
  350. 'reference' => '7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936',
  351. ),
  352. 'symfony/translation' =>
  353. array (
  354. 'pretty_version' => 'v5.4.19',
  355. 'version' => '5.4.19.0',
  356. 'aliases' =>
  357. array (
  358. ),
  359. 'reference' => '83d487b13b7fb4c0a6ad079f4e4c9b4525e1b695',
  360. ),
  361. 'symfony/translation-contracts' =>
  362. array (
  363. 'pretty_version' => 'v2.5.2',
  364. 'version' => '2.5.2.0',
  365. 'aliases' =>
  366. array (
  367. ),
  368. 'reference' => '136b19dd05cdf0709db6537d058bcab6dd6e2dbe',
  369. ),
  370. 'symfony/translation-implementation' =>
  371. array (
  372. 'provided' =>
  373. array (
  374. 0 => '2.3',
  375. ),
  376. ),
  377. 'team-reflex/discord-php' =>
  378. array (
  379. 'pretty_version' => 'v7.3.4',
  380. 'version' => '7.3.4.0',
  381. 'aliases' =>
  382. array (
  383. ),
  384. 'reference' => '82843da4346878a432171f8d82e345389f7cc49b',
  385. ),
  386. 'trafficcophp/bytebuffer' =>
  387. array (
  388. 'pretty_version' => 'v0.3',
  389. 'version' => '0.3.0.0',
  390. 'aliases' =>
  391. array (
  392. ),
  393. 'reference' => 'e94e5c87c41bc79c0f738b0fa89bad11d27ae0b4',
  394. ),
  395. ),
  396. );
  397. private static $canGetVendors;
  398. private static $installedByVendor = array();
  399. /**
  400. * Returns a list of all package names which are present, either by being installed, replaced or provided
  401. *
  402. * @return string[]
  403. * @psalm-return list<string>
  404. */
  405. public static function getInstalledPackages()
  406. {
  407. $packages = array();
  408. foreach (self::getInstalled() as $installed) {
  409. $packages[] = array_keys($installed['versions']);
  410. }
  411. if (1 === \count($packages)) {
  412. return $packages[0];
  413. }
  414. return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
  415. }
  416. /**
  417. * Checks whether the given package is installed
  418. *
  419. * This also returns true if the package name is provided or replaced by another package
  420. *
  421. * @param string $packageName
  422. * @return bool
  423. */
  424. public static function isInstalled($packageName)
  425. {
  426. foreach (self::getInstalled() as $installed) {
  427. if (isset($installed['versions'][$packageName])) {
  428. return true;
  429. }
  430. }
  431. return false;
  432. }
  433. /**
  434. * Checks whether the given package satisfies a version constraint
  435. *
  436. * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
  437. *
  438. * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
  439. *
  440. * @param VersionParser $parser Install composer/semver to have access to this class and functionality
  441. * @param string $packageName
  442. * @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
  443. *
  444. * @return bool
  445. */
  446. public static function satisfies(VersionParser $parser, $packageName, $constraint)
  447. {
  448. $constraint = $parser->parseConstraints($constraint);
  449. $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
  450. return $provided->matches($constraint);
  451. }
  452. /**
  453. * Returns a version constraint representing all the range(s) which are installed for a given package
  454. *
  455. * It is easier to use this via isInstalled() with the $constraint argument if you need to check
  456. * whether a given version of a package is installed, and not just whether it exists
  457. *
  458. * @param string $packageName
  459. * @return string Version constraint usable with composer/semver
  460. */
  461. public static function getVersionRanges($packageName)
  462. {
  463. foreach (self::getInstalled() as $installed) {
  464. if (!isset($installed['versions'][$packageName])) {
  465. continue;
  466. }
  467. $ranges = array();
  468. if (isset($installed['versions'][$packageName]['pretty_version'])) {
  469. $ranges[] = $installed['versions'][$packageName]['pretty_version'];
  470. }
  471. if (array_key_exists('aliases', $installed['versions'][$packageName])) {
  472. $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
  473. }
  474. if (array_key_exists('replaced', $installed['versions'][$packageName])) {
  475. $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
  476. }
  477. if (array_key_exists('provided', $installed['versions'][$packageName])) {
  478. $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
  479. }
  480. return implode(' || ', $ranges);
  481. }
  482. throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
  483. }
  484. /**
  485. * @param string $packageName
  486. * @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
  487. */
  488. public static function getVersion($packageName)
  489. {
  490. foreach (self::getInstalled() as $installed) {
  491. if (!isset($installed['versions'][$packageName])) {
  492. continue;
  493. }
  494. if (!isset($installed['versions'][$packageName]['version'])) {
  495. return null;
  496. }
  497. return $installed['versions'][$packageName]['version'];
  498. }
  499. throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
  500. }
  501. /**
  502. * @param string $packageName
  503. * @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
  504. */
  505. public static function getPrettyVersion($packageName)
  506. {
  507. foreach (self::getInstalled() as $installed) {
  508. if (!isset($installed['versions'][$packageName])) {
  509. continue;
  510. }
  511. if (!isset($installed['versions'][$packageName]['pretty_version'])) {
  512. return null;
  513. }
  514. return $installed['versions'][$packageName]['pretty_version'];
  515. }
  516. throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
  517. }
  518. /**
  519. * @param string $packageName
  520. * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
  521. */
  522. public static function getReference($packageName)
  523. {
  524. foreach (self::getInstalled() as $installed) {
  525. if (!isset($installed['versions'][$packageName])) {
  526. continue;
  527. }
  528. if (!isset($installed['versions'][$packageName]['reference'])) {
  529. return null;
  530. }
  531. return $installed['versions'][$packageName]['reference'];
  532. }
  533. throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
  534. }
  535. /**
  536. * @return array
  537. * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[]}
  538. */
  539. public static function getRootPackage()
  540. {
  541. $installed = self::getInstalled();
  542. return $installed[0]['root'];
  543. }
  544. /**
  545. * Returns the raw installed.php data for custom implementations
  546. *
  547. * @return array[]
  548. * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[]}, versions: list<string, array{pretty_version: ?string, version: ?string, aliases: ?string[], reference: ?string, replaced: ?string[], provided: ?string[]}>}
  549. */
  550. public static function getRawData()
  551. {
  552. return self::$installed;
  553. }
  554. /**
  555. * Lets you reload the static array from another file
  556. *
  557. * This is only useful for complex integrations in which a project needs to use
  558. * this class but then also needs to execute another project's autoloader in process,
  559. * and wants to ensure both projects have access to their version of installed.php.
  560. *
  561. * A typical case would be PHPUnit, where it would need to make sure it reads all
  562. * the data it needs from this class, then call reload() with
  563. * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
  564. * the project in which it runs can then also use this class safely, without
  565. * interference between PHPUnit's dependencies and the project's dependencies.
  566. *
  567. * @param array[] $data A vendor/composer/installed.php data set
  568. * @return void
  569. *
  570. * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[]}, versions: list<string, array{pretty_version: ?string, version: ?string, aliases: ?string[], reference: ?string, replaced: ?string[], provided: ?string[]}>} $data
  571. */
  572. public static function reload($data)
  573. {
  574. self::$installed = $data;
  575. self::$installedByVendor = array();
  576. }
  577. /**
  578. * @return array[]
  579. */
  580. private static function getInstalled()
  581. {
  582. if (null === self::$canGetVendors) {
  583. self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
  584. }
  585. $installed = array();
  586. if (self::$canGetVendors) {
  587. // @phpstan-ignore-next-line
  588. foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
  589. if (isset(self::$installedByVendor[$vendorDir])) {
  590. $installed[] = self::$installedByVendor[$vendorDir];
  591. } elseif (is_file($vendorDir.'/composer/installed.php')) {
  592. $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
  593. }
  594. }
  595. }
  596. $installed[] = self::$installed;
  597. return $installed;
  598. }
  599. }