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