1<?php 2 3namespace Composer\Installers; 4 5use Composer\Composer; 6use Composer\Installer\BinaryInstaller; 7use Composer\Installer\LibraryInstaller; 8use Composer\IO\IOInterface; 9use Composer\Package\PackageInterface; 10use Composer\Repository\InstalledRepositoryInterface; 11use Composer\Util\Filesystem; 12use React\Promise\PromiseInterface; 13 14class Installer extends LibraryInstaller 15{ 16 17 /** 18 * Package types to installer class map 19 * 20 * @var array 21 */ 22 private $supportedTypes = array( 23 'aimeos' => 'AimeosInstaller', 24 'asgard' => 'AsgardInstaller', 25 'attogram' => 'AttogramInstaller', 26 'agl' => 'AglInstaller', 27 'annotatecms' => 'AnnotateCmsInstaller', 28 'bitrix' => 'BitrixInstaller', 29 'bonefish' => 'BonefishInstaller', 30 'cakephp' => 'CakePHPInstaller', 31 'chef' => 'ChefInstaller', 32 'civicrm' => 'CiviCrmInstaller', 33 'ccframework' => 'ClanCatsFrameworkInstaller', 34 'cockpit' => 'CockpitInstaller', 35 'codeigniter' => 'CodeIgniterInstaller', 36 'concrete5' => 'Concrete5Installer', 37 'craft' => 'CraftInstaller', 38 'croogo' => 'CroogoInstaller', 39 'dframe' => 'DframeInstaller', 40 'dokuwiki' => 'DokuWikiInstaller', 41 'dolibarr' => 'DolibarrInstaller', 42 'decibel' => 'DecibelInstaller', 43 'drupal' => 'DrupalInstaller', 44 'elgg' => 'ElggInstaller', 45 'eliasis' => 'EliasisInstaller', 46 'ee3' => 'ExpressionEngineInstaller', 47 'ee2' => 'ExpressionEngineInstaller', 48 'ezplatform' => 'EzPlatformInstaller', 49 'fuel' => 'FuelInstaller', 50 'fuelphp' => 'FuelphpInstaller', 51 'grav' => 'GravInstaller', 52 'hurad' => 'HuradInstaller', 53 'tastyigniter' => 'TastyIgniterInstaller', 54 'imagecms' => 'ImageCMSInstaller', 55 'itop' => 'ItopInstaller', 56 'joomla' => 'JoomlaInstaller', 57 'kanboard' => 'KanboardInstaller', 58 'kirby' => 'KirbyInstaller', 59 'known' => 'KnownInstaller', 60 'kodicms' => 'KodiCMSInstaller', 61 'kohana' => 'KohanaInstaller', 62 'lms' => 'LanManagementSystemInstaller', 63 'laravel' => 'LaravelInstaller', 64 'lavalite' => 'LavaLiteInstaller', 65 'lithium' => 'LithiumInstaller', 66 'magento' => 'MagentoInstaller', 67 'majima' => 'MajimaInstaller', 68 'mantisbt' => 'MantisBTInstaller', 69 'mako' => 'MakoInstaller', 70 'maya' => 'MayaInstaller', 71 'mautic' => 'MauticInstaller', 72 'mediawiki' => 'MediaWikiInstaller', 73 'miaoxing' => 'MiaoxingInstaller', 74 'microweber' => 'MicroweberInstaller', 75 'modulework' => 'MODULEWorkInstaller', 76 'modx' => 'ModxInstaller', 77 'modxevo' => 'MODXEvoInstaller', 78 'moodle' => 'MoodleInstaller', 79 'october' => 'OctoberInstaller', 80 'ontowiki' => 'OntoWikiInstaller', 81 'oxid' => 'OxidInstaller', 82 'osclass' => 'OsclassInstaller', 83 'pxcms' => 'PxcmsInstaller', 84 'phpbb' => 'PhpBBInstaller', 85 'pimcore' => 'PimcoreInstaller', 86 'piwik' => 'PiwikInstaller', 87 'plentymarkets'=> 'PlentymarketsInstaller', 88 'ppi' => 'PPIInstaller', 89 'puppet' => 'PuppetInstaller', 90 'radphp' => 'RadPHPInstaller', 91 'phifty' => 'PhiftyInstaller', 92 'porto' => 'PortoInstaller', 93 'processwire' => 'ProcessWireInstaller', 94 'redaxo' => 'RedaxoInstaller', 95 'redaxo5' => 'Redaxo5Installer', 96 'reindex' => 'ReIndexInstaller', 97 'roundcube' => 'RoundcubeInstaller', 98 'shopware' => 'ShopwareInstaller', 99 'sitedirect' => 'SiteDirectInstaller', 100 'silverstripe' => 'SilverStripeInstaller', 101 'smf' => 'SMFInstaller', 102 'starbug' => 'StarbugInstaller', 103 'sydes' => 'SyDESInstaller', 104 'sylius' => 'SyliusInstaller', 105 'symfony1' => 'Symfony1Installer', 106 'tao' => 'TaoInstaller', 107 'thelia' => 'TheliaInstaller', 108 'tusk' => 'TuskInstaller', 109 'typo3-cms' => 'TYPO3CmsInstaller', 110 'typo3-flow' => 'TYPO3FlowInstaller', 111 'userfrosting' => 'UserFrostingInstaller', 112 'vanilla' => 'VanillaInstaller', 113 'whmcs' => 'WHMCSInstaller', 114 'winter' => 'WinterInstaller', 115 'wolfcms' => 'WolfCMSInstaller', 116 'wordpress' => 'WordPressInstaller', 117 'yawik' => 'YawikInstaller', 118 'zend' => 'ZendInstaller', 119 'zikula' => 'ZikulaInstaller', 120 'prestashop' => 'PrestashopInstaller' 121 ); 122 123 /** 124 * Installer constructor. 125 * 126 * Disables installers specified in main composer extra installer-disable 127 * list 128 * 129 * @param IOInterface $io 130 * @param Composer $composer 131 * @param string $type 132 * @param Filesystem|null $filesystem 133 * @param BinaryInstaller|null $binaryInstaller 134 */ 135 public function __construct( 136 IOInterface $io, 137 Composer $composer, 138 $type = 'library', 139 Filesystem $filesystem = null, 140 BinaryInstaller $binaryInstaller = null 141 ) { 142 parent::__construct($io, $composer, $type, $filesystem, 143 $binaryInstaller); 144 $this->removeDisabledInstallers(); 145 } 146 147 /** 148 * {@inheritDoc} 149 */ 150 public function getInstallPath(PackageInterface $package) 151 { 152 $type = $package->getType(); 153 $frameworkType = $this->findFrameworkType($type); 154 155 if ($frameworkType === false) { 156 throw new \InvalidArgumentException( 157 'Sorry the package type of this package is not yet supported.' 158 ); 159 } 160 161 $class = 'Composer\\Installers\\' . $this->supportedTypes[$frameworkType]; 162 $installer = new $class($package, $this->composer, $this->getIO()); 163 164 return $installer->getInstallPath($package, $frameworkType); 165 } 166 167 public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package) 168 { 169 $installPath = $this->getPackageBasePath($package); 170 $io = $this->io; 171 $outputStatus = function () use ($io, $installPath) { 172 $io->write(sprintf('Deleting %s - %s', $installPath, !file_exists($installPath) ? '<comment>deleted</comment>' : '<error>not deleted</error>')); 173 }; 174 175 $promise = parent::uninstall($repo, $package); 176 177 // Composer v2 might return a promise here 178 if ($promise instanceof PromiseInterface) { 179 return $promise->then($outputStatus); 180 } 181 182 // If not, execute the code right away as parent::uninstall executed synchronously (composer v1, or v2 without async) 183 $outputStatus(); 184 185 return null; 186 } 187 188 /** 189 * {@inheritDoc} 190 */ 191 public function supports($packageType) 192 { 193 $frameworkType = $this->findFrameworkType($packageType); 194 195 if ($frameworkType === false) { 196 return false; 197 } 198 199 $locationPattern = $this->getLocationPattern($frameworkType); 200 201 return preg_match('#' . $frameworkType . '-' . $locationPattern . '#', $packageType, $matches) === 1; 202 } 203 204 /** 205 * Finds a supported framework type if it exists and returns it 206 * 207 * @param string $type 208 * @return string|false 209 */ 210 protected function findFrameworkType($type) 211 { 212 krsort($this->supportedTypes); 213 214 foreach ($this->supportedTypes as $key => $val) { 215 if ($key === substr($type, 0, strlen($key))) { 216 return substr($type, 0, strlen($key)); 217 } 218 } 219 220 return false; 221 } 222 223 /** 224 * Get the second part of the regular expression to check for support of a 225 * package type 226 * 227 * @param string $frameworkType 228 * @return string 229 */ 230 protected function getLocationPattern($frameworkType) 231 { 232 $pattern = false; 233 if (!empty($this->supportedTypes[$frameworkType])) { 234 $frameworkClass = 'Composer\\Installers\\' . $this->supportedTypes[$frameworkType]; 235 /** @var BaseInstaller $framework */ 236 $framework = new $frameworkClass(null, $this->composer, $this->getIO()); 237 $locations = array_keys($framework->getLocations()); 238 $pattern = $locations ? '(' . implode('|', $locations) . ')' : false; 239 } 240 241 return $pattern ? : '(\w+)'; 242 } 243 244 /** 245 * Get I/O object 246 * 247 * @return IOInterface 248 */ 249 private function getIO() 250 { 251 return $this->io; 252 } 253 254 /** 255 * Look for installers set to be disabled in composer's extra config and 256 * remove them from the list of supported installers. 257 * 258 * Globals: 259 * - true, "all", and "*" - disable all installers. 260 * - false - enable all installers (useful with 261 * wikimedia/composer-merge-plugin or similar) 262 * 263 * @return void 264 */ 265 protected function removeDisabledInstallers() 266 { 267 $extra = $this->composer->getPackage()->getExtra(); 268 269 if (!isset($extra['installer-disable']) || $extra['installer-disable'] === false) { 270 // No installers are disabled 271 return; 272 } 273 274 // Get installers to disable 275 $disable = $extra['installer-disable']; 276 277 // Ensure $disabled is an array 278 if (!is_array($disable)) { 279 $disable = array($disable); 280 } 281 282 // Check which installers should be disabled 283 $all = array(true, "all", "*"); 284 $intersect = array_intersect($all, $disable); 285 if (!empty($intersect)) { 286 // Disable all installers 287 $this->supportedTypes = array(); 288 } else { 289 // Disable specified installers 290 foreach ($disable as $key => $installer) { 291 if (is_string($installer) && key_exists($installer, $this->supportedTypes)) { 292 unset($this->supportedTypes[$installer]); 293 } 294 } 295 } 296 } 297} 298