@@ -28,18 +28,23 @@ class InstalledVersions
2828{
2929 /**
3030 * @var mixed[]|null
31- * @psalm-var array{root: array{name: string, version : string, reference : string, pretty_version : string, aliases : string[], dev: bool, install_path : string, type: string }, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases ?: string[], reference ?: string, replaced ?: string[], provided ?: string[], install_path ?: string, type ?: string}>}|array{}|null
31+ * @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
3232 */
3333 private static $ installed ;
3434
35+ /**
36+ * @var bool
37+ */
38+ private static $ installedIsLocalDir ;
39+
3540 /**
3641 * @var bool|null
3742 */
3843 private static $ canGetVendors ;
3944
4045 /**
4146 * @var array[]
42- * @psalm-var array<string, array{root: array{name: string, version : string, reference : string, pretty_version : string, aliases : string[], dev: bool, install_path : string, type: string }, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases ?: string[], reference ?: string, replaced ?: string[], provided ?: string[], install_path ?: string, type ?: string}>}>
47+ * @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[] }>}>
4348 */
4449 private static $ installedByVendor = array ();
4550
@@ -98,7 +103,7 @@ public static function isInstalled($packageName, $includeDevRequirements = true)
98103 {
99104 foreach (self ::getInstalled () as $ installed ) {
100105 if (isset ($ installed ['versions ' ][$ packageName ])) {
101- return $ includeDevRequirements || empty ($ installed ['versions ' ][$ packageName ]['dev_requirement ' ]);
106+ return $ includeDevRequirements || ! isset ($ installed ['versions ' ][$ packageName ]['dev_requirement ' ]) || $ installed [ ' versions ' ][ $ packageName ][ ' dev_requirement ' ] === false ;
102107 }
103108 }
104109
@@ -119,7 +124,7 @@ public static function isInstalled($packageName, $includeDevRequirements = true)
119124 */
120125 public static function satisfies (VersionParser $ parser , $ packageName , $ constraint )
121126 {
122- $ constraint = $ parser ->parseConstraints ($ constraint );
127+ $ constraint = $ parser ->parseConstraints (( string ) $ constraint );
123128 $ provided = $ parser ->parseConstraints (self ::getVersionRanges ($ packageName ));
124129
125130 return $ provided ->matches ($ constraint );
@@ -243,7 +248,7 @@ public static function getInstallPath($packageName)
243248
244249 /**
245250 * @return array
246- * @psalm-return array{name: string, version : string, reference : string, pretty_version : string, aliases : string[], dev: bool, install_path : string, type: string }
251+ * @psalm-return array{name: string, pretty_version : string, version : string, reference : string|null, type : string, install_path: string, aliases : string[], dev: bool }
247252 */
248253 public static function getRootPackage ()
249254 {
@@ -257,7 +262,7 @@ public static function getRootPackage()
257262 *
258263 * @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.
259264 * @return array[]
260- * @psalm-return array{root: array{name: string, version : string, reference : string, pretty_version : string, aliases : string[], dev: bool, install_path : string, type: string }, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases ?: string[], reference ?: string, replaced ?: string[], provided ?: string[], install_path ?: string, type ?: string}>}
265+ * @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[] }>}
261266 */
262267 public static function getRawData ()
263268 {
@@ -280,7 +285,7 @@ public static function getRawData()
280285 * Returns the raw data of all installed.php which are currently loaded for custom implementations
281286 *
282287 * @return array[]
283- * @psalm-return list<array{root: array{name: string, version : string, reference : string, pretty_version : string, aliases : string[], dev: bool, install_path : string, type: string }, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases ?: string[], reference ?: string, replaced ?: string[], provided ?: string[], install_path ?: string, type ?: string}>}>
288+ * @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[] }>}>
284289 */
285290 public static function getAllRawData ()
286291 {
@@ -303,17 +308,23 @@ public static function getAllRawData()
303308 * @param array[] $data A vendor/composer/installed.php data set
304309 * @return void
305310 *
306- * @psalm-param array{root: array{name: string, version : string, reference : string, pretty_version : string, aliases : string[], dev: bool, install_path : string, type: string }, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases ?: string[], reference ?: string, replaced ?: string[], provided ?: string[], install_path ?: string, type ?: string}>} $data
311+ * @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
307312 */
308313 public static function reload ($ data )
309314 {
310315 self ::$ installed = $ data ;
311316 self ::$ installedByVendor = array ();
317+
318+ // when using reload, we disable the duplicate protection to ensure that self::$installed data is
319+ // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
320+ // so we have to assume it does not, and that may result in duplicate data being returned when listing
321+ // all installed packages for example
322+ self ::$ installedIsLocalDir = false ;
312323 }
313324
314325 /**
315326 * @return array[]
316- * @psalm-return list<array{root: array{name: string, version : string, reference : string, pretty_version : string, aliases : string[], dev: bool, install_path : string, type: string }, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases ?: string[], reference ?: string, replaced ?: string[], provided ?: string[], install_path ?: string, type ?: string}>}>
327+ * @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[] }>}>
317328 */
318329 private static function getInstalled ()
319330 {
@@ -322,30 +333,45 @@ private static function getInstalled()
322333 }
323334
324335 $ installed = array ();
336+ $ copiedLocalDir = false ;
325337
326338 if (self ::$ canGetVendors ) {
339+ $ selfDir = strtr (__DIR__ , '\\' , '/ ' );
327340 foreach (ClassLoader::getRegisteredLoaders () as $ vendorDir => $ loader ) {
341+ $ vendorDir = strtr ($ vendorDir , '\\' , '/ ' );
328342 if (isset (self ::$ installedByVendor [$ vendorDir ])) {
329343 $ installed [] = self ::$ installedByVendor [$ vendorDir ];
330344 } elseif (is_file ($ vendorDir .'/composer/installed.php ' )) {
331- $ installed [] = self ::$ installedByVendor [$ vendorDir ] = require $ vendorDir .'/composer/installed.php ' ;
332- if (null === self ::$ installed && strtr ($ vendorDir .'/composer ' , '\\' , '/ ' ) === strtr (__DIR__ , '\\' , '/ ' )) {
333- self ::$ installed = $ installed [count ($ installed ) - 1 ];
345+ /** @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 */
346+ $ required = require $ vendorDir .'/composer/installed.php ' ;
347+ self ::$ installedByVendor [$ vendorDir ] = $ required ;
348+ $ installed [] = $ required ;
349+ if (self ::$ installed === null && $ vendorDir .'/composer ' === $ selfDir ) {
350+ self ::$ installed = $ required ;
351+ self ::$ installedIsLocalDir = true ;
334352 }
335353 }
354+ if (self ::$ installedIsLocalDir && $ vendorDir .'/composer ' === $ selfDir ) {
355+ $ copiedLocalDir = true ;
356+ }
336357 }
337358 }
338359
339360 if (null === self ::$ installed ) {
340361 // only require the installed.php file if this file is loaded from its dumped location,
341362 // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
342363 if (substr (__DIR__ , -8 , 1 ) !== 'C ' ) {
343- self ::$ installed = require __DIR__ . '/installed.php ' ;
364+ /** @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 */
365+ $ required = require __DIR__ . '/installed.php ' ;
366+ self ::$ installed = $ required ;
344367 } else {
345368 self ::$ installed = array ();
346369 }
347370 }
348- $ installed [] = self ::$ installed ;
371+
372+ if (self ::$ installed !== array () && !$ copiedLocalDir ) {
373+ $ installed [] = self ::$ installed ;
374+ }
349375
350376 return $ installed ;
351377 }
0 commit comments