2626 */
2727class InstalledVersions
2828{
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+
2935 /**
3036 * @var mixed[]|null
3137 * @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
3238 */
3339 private static $ installed ;
3440
41+ /**
42+ * @var bool
43+ */
44+ private static $ installedIsLocalDir ;
45+
3546 /**
3647 * @var bool|null
3748 */
@@ -309,6 +320,24 @@ public static function reload($data)
309320 {
310321 self ::$ installed = $ data ;
311322 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 ;
312341 }
313342
314343 /**
@@ -322,19 +351,27 @@ private static function getInstalled()
322351 }
323352
324353 $ installed = array ();
354+ $ copiedLocalDir = false ;
325355
326356 if (self ::$ canGetVendors ) {
357+ $ selfDir = self ::getSelfDir ();
327358 foreach (ClassLoader::getRegisteredLoaders () as $ vendorDir => $ loader ) {
359+ $ vendorDir = strtr ($ vendorDir , '\\' , '/ ' );
328360 if (isset (self ::$ installedByVendor [$ vendorDir ])) {
329361 $ installed [] = self ::$ installedByVendor [$ vendorDir ];
330362 } elseif (is_file ($ vendorDir .'/composer/installed.php ' )) {
331363 /** @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 */
332364 $ required = require $ vendorDir .'/composer/installed.php ' ;
333- $ installed [] = self ::$ installedByVendor [$ vendorDir ] = $ required ;
334- if (null === self ::$ installed && strtr ($ vendorDir .'/composer ' , '\\' , '/ ' ) === strtr (__DIR__ , '\\' , '/ ' )) {
335- self ::$ installed = $ installed [count ($ installed ) - 1 ];
365+ self ::$ installedByVendor [$ vendorDir ] = $ required ;
366+ $ installed [] = $ required ;
367+ if (self ::$ installed === null && $ vendorDir .'/composer ' === $ selfDir ) {
368+ self ::$ installed = $ required ;
369+ self ::$ installedIsLocalDir = true ;
336370 }
337371 }
372+ if (self ::$ installedIsLocalDir && $ vendorDir .'/composer ' === $ selfDir ) {
373+ $ copiedLocalDir = true ;
374+ }
338375 }
339376 }
340377
@@ -350,7 +387,7 @@ private static function getInstalled()
350387 }
351388 }
352389
353- if (self ::$ installed !== array ()) {
390+ if (self ::$ installed !== array () && ! $ copiedLocalDir ) {
354391 $ installed [] = self ::$ installed ;
355392 }
356393
0 commit comments