* Jordi Boggiano * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Composer\Command; use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Request; use Composer\DependencyResolver\DefaultPolicy; use Composer\DependencyResolver\Solver; use Composer\Repository\PlatformRepository; use Composer\Package\MemoryPackage; use Composer\Package\LinkConstraint\VersionConstraint; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; /** * @author Jordi Boggiano * @author Ryan Weaver */ class InstallCommand extends Command { protected function configure() { $this ->setName('install') ->setDescription('Parses the composer.json file and downloads the needed dependencies.') ->setHelp(<<install command reads the composer.json file from the current directory, processes it, and downloads and installs all the libraries and dependencies outlined in that file. php composer install EOT ) ; } protected function execute(InputInterface $input, OutputInterface $output) { // TODO this needs a parameter to enable installing from source (i.e. git clone, instead of downloading archives) $sourceInstall = false; $config = $this->loadConfig(); $output->writeln('Loading repositories'); if (isset($config['repositories'])) { foreach ($config['repositories'] as $name => $spec) { $this->getComposer()->addRepository($name, $spec); } } $pool = new Pool; $repoInstalled = new PlatformRepository; $pool->addRepository($repoInstalled); // TODO check the lock file to see what's currently installed // $repoInstalled->addPackage(new MemoryPackage('$Package', '$Version')); $output->writeln('Loading package list'); foreach ($this->getComposer()->getRepositories() as $repository) { $pool->addRepository($repository); } $request = new Request($pool); $output->writeln('Building up request'); // TODO there should be an update flag or dedicated update command // TODO check lock file to remove packages that disappeared from the requirements foreach ($config['require'] as $name => $version) { if ('latest' === $version) { $request->install($name); } else { preg_match('#^([>=<~]*)([\d.]+.*)$#', $version, $match); if (!$match[1]) { $match[1] = '='; } $constraint = new VersionConstraint($match[1], $match[2]); $request->install($name, $constraint); } } $output->writeln('Solving dependencies'); $policy = new DefaultPolicy; $solver = new Solver($policy, $pool, $repoInstalled); $transaction = $solver->solve($request); $lock = array(); foreach ($transaction as $task) { switch ($task['job']) { case 'install': $package = $task['package']; $output->writeln('> Installing '.$package->getPrettyName()); if ($sourceInstall) { // TODO } else { if ($package->getDistType()) { $downloaderType = $package->getDistType(); $type = 'dist'; } elseif ($package->getSourceType()) { $output->writeln('Package '.$package->getPrettyName().' has no dist url, installing from source instead.'); $downloaderType = $package->getSourceType(); $type = 'source'; } else { throw new \UnexpectedValueException('Package '.$package->getPrettyName().' has no source or dist URL.'); } $downloader = $this->getComposer()->getDownloader($downloaderType); $installer = $this->getComposer()->getInstaller($package->getType()); if (!$installer->install($package, $downloader, $type)) { throw new \LogicException($package->getPrettyName().' could not be installed.'); } } $lock[$package->getName()] = array('version' => $package->getVersion()); break; default: throw new \UnexpectedValueException('Unhandled job type : '.$task['job']); } } $output->writeln('> Done'); $this->storeLockFile($lock, $output); } protected function loadConfig() { if (!file_exists('composer.json')) { throw new \UnexpectedValueException('composer.json config file not found in '.getcwd()); } $config = json_decode(file_get_contents('composer.json'), true); if (!$config) { switch (json_last_error()) { case JSON_ERROR_NONE: $msg = 'No error has occurred, is your composer.json file empty?'; break; case JSON_ERROR_DEPTH: $msg = 'The maximum stack depth has been exceeded'; break; case JSON_ERROR_STATE_MISMATCH: $msg = 'Invalid or malformed JSON'; break; case JSON_ERROR_CTRL_CHAR: $msg = 'Control character error, possibly incorrectly encoded'; break; case JSON_ERROR_SYNTAX: $msg = 'Syntax error'; break; case JSON_ERROR_UTF8: $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; break; } throw new \UnexpectedValueException('Incorrect composer.json file: '.$msg); } return $config; } protected function storeLockFile(array $content, OutputInterface $output) { file_put_contents('composer.lock', json_encode($content, JSON_FORCE_OBJECT)."\n"); $output->writeln('> composer.lock dumped'); } }