1<?php 2 3/* 4 * This file is part of Component Installer. 5 * 6 * (c) Rob Loach (http://robloach.net) 7 * 8 * For the full copyright and license information, please view the LICENSE.md 9 * file that was distributed with this source code. 10 */ 11 12namespace ComponentInstaller; 13 14use Composer\Installer\LibraryInstaller; 15use Composer\Script\Event; 16use Composer\Package\PackageInterface; 17use Composer\Package\AliasPackage; 18 19/** 20 * Component Installer for Composer. 21 */ 22class Installer extends LibraryInstaller 23{ 24 25 /** 26 * The location where Components are to be installed. 27 */ 28 protected $componentDir; 29 30 /** 31 * {@inheritDoc} 32 * 33 * Components are supported by all packages. This checks wheteher or not the 34 * entire package is a "component", as well as injects the script to act 35 * on components embedded in packages that are not just "component" types. 36 */ 37 public function supports($packageType) 38 { 39 // Components are supported by all package types. We will just act on 40 // the root package's scripts if available. 41 $rootPackage = isset($this->composer) ? $this->composer->getPackage() : null; 42 if (isset($rootPackage)) { 43 // Ensure we get the root package rather than its alias. 44 while ($rootPackage instanceof AliasPackage) { 45 $rootPackage = $rootPackage->getAliasOf(); 46 } 47 48 // Make sure the root package can override the available scripts. 49 if (method_exists($rootPackage, 'setScripts')) { 50 $scripts = $rootPackage->getScripts(); 51 // Act on the "post-autoload-dump" command so that we can act on all 52 // the installed packages. 53 $scripts['post-autoload-dump']['component-installer'] = 'ComponentInstaller\\Installer::postAutoloadDump'; 54 $rootPackage->setScripts($scripts); 55 } 56 } 57 58 // State support for "component" package types. 59 return $packageType == 'component'; 60 } 61 62 /** 63 * Gets the destination Component directory. 64 * 65 * @param PackageInterface $package 66 * @return string 67 * The path to where the final Component should be installed. 68 */ 69 public function getComponentPath(PackageInterface $package) 70 { 71 // Parse the pretty name for the vendor and package name. 72 $name = $prettyName = $package->getPrettyName(); 73 if (strpos($prettyName, '/') !== false) { 74 list($vendor, $name) = explode('/', $prettyName); 75 unset($vendor); 76 } 77 78 // First look for an override in root package's extra, then try the package's extra 79 $rootPackage = $this->composer->getPackage(); 80 $rootExtras = $rootPackage ? $rootPackage->getExtra() : array(); 81 $customComponents = isset($rootExtras['component']) ? $rootExtras['component'] : array(); 82 83 if (isset($customComponents[$prettyName]) && is_array($customComponents[$prettyName])) { 84 $component = $customComponents[$prettyName]; 85 } 86 else { 87 $extra = $package->getExtra(); 88 $component = isset($extra['component']) ? $extra['component'] : array(); 89 } 90 91 // Allow the component to define its own name. 92 if (isset($component['name'])) { 93 $name = $component['name']; 94 } 95 96 // Find where the package should be located. 97 return $this->getComponentDir() . DIRECTORY_SEPARATOR . $name; 98 } 99 100 /** 101 * Initialize the Component directory, as well as the vendor directory. 102 */ 103 protected function initializeVendorDir() 104 { 105 $this->componentDir = $this->getComponentDir(); 106 $this->filesystem->ensureDirectoryExists($this->componentDir); 107 parent::initializeVendorDir(); 108 } 109 110 /** 111 * Retrieves the Installer's provided component directory. 112 */ 113 public function getComponentDir() 114 { 115 $config = $this->composer->getConfig(); 116 return $config->has('component-dir') ? $config->get('component-dir') : 'components'; 117 } 118 119 /** 120 * Remove both the installed code and files from the Component directory. 121 * 122 * @param PackageInterface $package 123 */ 124 public function removeCode(PackageInterface $package) 125 { 126 $this->removeComponent($package); 127 parent::removeCode($package); 128 } 129 130 /** 131 * Remove a Component's files from the Component directory. 132 * 133 * @param PackageInterface $package 134 * @return bool 135 */ 136 public function removeComponent(PackageInterface $package) 137 { 138 $path = $this->getComponentPath($package); 139 return $this->filesystem->remove($path); 140 } 141 142 /** 143 * Before installing the Component, be sure its destination is clear first. 144 * 145 * @param PackageInterface $package 146 */ 147 public function installCode(PackageInterface $package) 148 { 149 $this->removeComponent($package); 150 parent::installCode($package); 151 } 152 153 /** 154 * Script callback; Acted on after the autoloader is dumped. 155 * 156 * @param Event $event 157 */ 158 public static function postAutoloadDump(Event $event) 159 { 160 // Retrieve basic information about the environment and present a 161 // message to the user. 162 $composer = $event->getComposer(); 163 $io = $event->getIO(); 164 $io->write('<info>Compiling component files</info>'); 165 166 // Set up all the processes. 167 $processes = array( 168 // Copy the assets to the Components directory. 169 "ComponentInstaller\\Process\\CopyProcess", 170 // Build the require.js file. 171 "ComponentInstaller\\Process\\RequireJsProcess", 172 // Build the require.css file. 173 "ComponentInstaller\\Process\\RequireCssProcess", 174 // Compile the require-built.js file. 175 "ComponentInstaller\\Process\\BuildJsProcess", 176 ); 177 178 // Initialize and execute each process in sequence. 179 foreach ($processes as $class) { 180 if(!class_exists($class)){ 181 $io->write("<warning>Process class '$class' not found, skipping this process</warning>"); 182 continue; 183 } 184 185 /** @var \ComponentInstaller\Process\Process $process */ 186 $process = new $class($composer, $io); 187 // When an error occurs during initialization, end the process. 188 if (!$process->init()) { 189 $io->write("<warning>An error occurred while initializing the '$class' process.</warning>"); 190 break; 191 } 192 $process->process(); 193 } 194 } 195} 196