/** * Remove packages from the root install. * * @param $packages array Indexed array of package names to remove * * @throws \Bolt\Exception\PackageManagerException * * @return int 0 on success or a positive error code on failure */ public function execute(array $packages) { if (empty($packages)) { throw new PackageManagerException('No package specified for removal'); } $io = $this->app['extend.manager']->getIO(); $options = $this->app['extend.manager']->getOptions(); $jsonFile = new JsonFile($options['composerjson']); $composerDefinition = $jsonFile->read(); $composerBackup = file_get_contents($jsonFile->getPath()); $json = new JsonConfigSource($jsonFile); $type = $options['dev'] ? 'require-dev' : 'require'; // Remove packages from JSON foreach ($packages as $package) { if (isset($composerDefinition[$type][$package])) { $json->removeLink($type, $package); } } // Reload Composer config $composer = $this->app['extend.manager']->getFactory()->resetComposer(); $install = Installer::create($io, $composer); try { $install->setVerbose($options['verbose'])->setDevMode(!$options['updatenodev'])->setUpdate(true)->setUpdateWhitelist($packages)->setWhitelistDependencies($options['updatewithdependencies'])->setIgnorePlatformRequirements($options['ignoreplatformreqs']); $status = $install->run(); if ($status !== 0) { // Write out old JSON file file_put_contents($jsonFile->getPath(), $composerBackup); } } catch (\Exception $e) { $msg = __CLASS__ . '::' . __FUNCTION__ . ' recieved an error from Composer: ' . $e->getMessage() . ' in ' . $e->getFile() . '::' . $e->getLine(); $this->app['logger.system']->critical($msg, array('event' => 'exception', 'exception' => $e)); throw new PackageManagerException($e->getMessage(), $e->getCode(), $e); } return $status; }
protected function manipulateJson($method, $args, $fallback) { $args = func_get_args(); // remove method & fallback array_shift($args); $fallback = array_pop($args); if ($this->file->exists()) { $contents = file_get_contents($this->file->getPath()); } else { $contents = "{\n \"config\": {\n }\n}\n"; } $manipulator = new JsonManipulator($contents); $newFile = !$this->file->exists(); // try to update cleanly if (call_user_func_array(array($manipulator, $method), $args)) { file_put_contents($this->file->getPath(), $manipulator->getContents()); } else { // on failed clean update, call the fallback and rewrite the whole file $config = $this->file->read(); $this->array_unshift_ref($args, $config); call_user_func_array($fallback, $args); $this->file->write($config); } if ($newFile) { @chmod($this->file->getPath(), 0600); } }
protected function execute(InputInterface $input, OutputInterface $output) { $file = Factory::getComposerFile(); if (!file_exists($file) && !file_put_contents($file, "{\n}\n")) { $output->writeln('<error>' . $file . ' could not be created.</error>'); return 1; } if (!is_readable($file)) { $output->writeln('<error>' . $file . ' is not readable.</error>'); return 1; } if (!is_writable($file)) { $output->writeln('<error>' . $file . ' is not writable.</error>'); return 1; } $json = new JsonFile($file); $composer = $json->read(); $composerBackup = file_get_contents($json->getPath()); $requirements = $this->determineRequirements($input, $output, $input->getArgument('packages')); $requireKey = $input->getOption('dev') ? 'require-dev' : 'require'; $removeKey = $input->getOption('dev') ? 'require' : 'require-dev'; $baseRequirements = array_key_exists($requireKey, $composer) ? $composer[$requireKey] : array(); $requirements = $this->formatRequirements($requirements); // validate requirements format $versionParser = new VersionParser(); foreach ($requirements as $constraint) { $versionParser->parseConstraints($constraint); } if (!$this->updateFileCleanly($json, $baseRequirements, $requirements, $requireKey, $removeKey)) { foreach ($requirements as $package => $version) { $baseRequirements[$package] = $version; if (isset($composer[$removeKey][$package])) { unset($composer[$removeKey][$package]); } } $composer[$requireKey] = $baseRequirements; $json->write($composer); } $output->writeln('<info>' . $file . ' has been updated</info>'); if ($input->getOption('no-update')) { return 0; } $updateDevMode = !$input->getOption('update-no-dev'); // Update packages $composer = $this->getComposer(); $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); $io = $this->getIO(); $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'require', $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); $install = Installer::create($io, $composer); $install->setVerbose($input->getOption('verbose'))->setPreferSource($input->getOption('prefer-source'))->setPreferDist($input->getOption('prefer-dist'))->setDevMode($updateDevMode)->setUpdate(true)->setUpdateWhitelist(array_keys($requirements))->setWhitelistDependencies($input->getOption('update-with-dependencies')); $status = $install->run(); if ($status !== 0) { $output->writeln("\n" . '<error>Installation failed, reverting ' . $file . ' to its original content.</error>'); file_put_contents($json->getPath(), $composerBackup); } return $status; }
protected function execute(InputInterface $input, OutputInterface $output) { $packages = $input->getArgument('packages'); $file = Factory::getComposerFile(); $jsonFile = new JsonFile($file); $composer = $jsonFile->read(); $composerBackup = file_get_contents($jsonFile->getPath()); $json = new JsonConfigSource($jsonFile); $type = $input->getOption('dev') ? 'require-dev' : 'require'; $altType = !$input->getOption('dev') ? 'require-dev' : 'require'; $io = $this->getIO(); foreach ($packages as $package) { if (isset($composer[$type][$package])) { $json->removeLink($type, $package); } elseif (isset($composer[$altType][$package])) { $io->writeError('<warning>' . $package . ' could not be found in ' . $type . ' but it is present in ' . $altType . '</warning>'); if ($io->isInteractive()) { if ($io->askConfirmation('Do you want to remove it from ' . $altType . ' [<comment>yes</comment>]? ', true)) { $json->removeLink($altType, $package); } } } else { $io->writeError('<warning>' . $package . ' is not required in your composer.json and has not been removed</warning>'); } } if ($input->getOption('no-update')) { return 0; } // Update packages $composer = $this->getComposer(); $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'remove', $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); $install = Installer::create($io, $composer); $updateDevMode = !$input->getOption('update-no-dev'); $optimize = $input->getOption('optimize-autoloader') || $composer->getConfig()->get('optimize-autoloader'); $authoritative = $input->getOption('classmap-authoritative') || $composer->getConfig()->get('classmap-authoritative'); $install->setVerbose($input->getOption('verbose'))->setDevMode($updateDevMode)->setOptimizeAutoloader($optimize)->setClassMapAuthoritative($authoritative)->setUpdate(true)->setUpdateWhitelist($packages)->setWhitelistDependencies($input->getOption('update-with-dependencies'))->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')); $status = $install->run(); if ($status !== 0) { $io->writeError("\n" . '<error>Removal failed, reverting ' . $file . ' to its original content.</error>'); file_put_contents($jsonFile->getPath(), $composerBackup); } return $status; }
protected function execute(InputInterface $input, OutputInterface $output) { $file = Factory::getComposerFile(); if (!file_exists($file) && !file_put_contents($file, "{\n}\n")) { $output->writeln('<error>' . $file . ' could not be created.</error>'); return 1; } if (!is_readable($file)) { $output->writeln('<error>' . $file . ' is not readable.</error>'); return 1; } if (!is_writable($file)) { $output->writeln('<error>' . $file . ' is not writable.</error>'); return 1; } $dialog = $this->getHelperSet()->get('dialog'); $json = new JsonFile($file); $composer = $json->read(); $composerBackup = file_get_contents($json->getPath()); $requirements = $this->determineRequirements($input, $output, $input->getArgument('packages')); $requireKey = $input->getOption('dev') ? 'require-dev' : 'require'; $baseRequirements = array_key_exists($requireKey, $composer) ? $composer[$requireKey] : array(); $requirements = $this->formatRequirements($requirements); if (!$this->updateFileCleanly($json, $baseRequirements, $requirements, $requireKey)) { foreach ($requirements as $package => $version) { $baseRequirements[$package] = $version; } $composer[$requireKey] = $baseRequirements; $json->write($composer); } $output->writeln('<info>' . $file . ' has been updated</info>'); if ($input->getOption('no-update')) { return 0; } // Update packages $composer = $this->getComposer(); $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); $io = $this->getIO(); $install = Installer::create($io, $composer); $install->setVerbose($input->getOption('verbose'))->setPreferSource($input->getOption('prefer-source'))->setPreferDist($input->getOption('prefer-dist'))->setDevMode(true)->setUpdate(true)->setUpdateWhitelist(array_keys($requirements)); if (!$install->run()) { $output->writeln("\n" . '<error>Installation failed, reverting ' . $file . ' to its original content.</error>'); file_put_contents($json->getPath(), $composerBackup); return 1; } return 0; }
protected function manipulateJson($method, $args, $fallback) { $args = func_get_args(); // remove method & fallback array_shift($args); $fallback = array_pop($args); if ($this->file->exists()) { if (!is_writable($this->file->getPath())) { throw new \RuntimeException(sprintf('The file "%s" is not writable.', $this->file->getPath())); } if (!is_readable($this->file->getPath())) { throw new \RuntimeException(sprintf('The file "%s" is not readable.', $this->file->getPath())); } $contents = file_get_contents($this->file->getPath()); } elseif ($this->authConfig) { $contents = "{\n}\n"; } else { $contents = "{\n \"config\": {\n }\n}\n"; } $manipulator = new JsonManipulator($contents); $newFile = !$this->file->exists(); // override manipulator method for auth config files if ($this->authConfig && $method === 'addConfigSetting') { $method = 'addSubNode'; list($mainNode, $name) = explode('.', $args[0], 2); $args = array($mainNode, $name, $args[1]); } elseif ($this->authConfig && $method === 'removeConfigSetting') { $method = 'removeSubNode'; list($mainNode, $name) = explode('.', $args[0], 2); $args = array($mainNode, $name); } // try to update cleanly if (call_user_func_array(array($manipulator, $method), $args)) { file_put_contents($this->file->getPath(), $manipulator->getContents()); } else { // on failed clean update, call the fallback and rewrite the whole file $config = $this->file->read(); $this->arrayUnshiftRef($args, $config); call_user_func_array($fallback, $args); $this->file->write($config); } if ($newFile) { Silencer::call('chmod', $this->file->getPath(), 0600); } }
/** * Creates a Composer instance * * @param IOInterface $io IO instance * @param array|string|null $localConfig either a configuration array or a filename to read from, if null it will * read from the default filename * @param bool $disablePlugins Whether plugins should not be loaded * @throws \InvalidArgumentException * @throws \UnexpectedValueException * @return Composer */ public function createComposer(IOInterface $io, $localConfig = null, $disablePlugins = false) { // load Composer configuration if (null === $localConfig) { $localConfig = static::getComposerFile(); } if (is_string($localConfig)) { $composerFile = $localConfig; $file = new JsonFile($localConfig, new RemoteFilesystem($io)); if (!$file->exists()) { if ($localConfig === './composer.json' || $localConfig === 'composer.json') { $message = 'Composer could not find a composer.json file in ' . getcwd(); } else { $message = 'Composer could not find the config file: ' . $localConfig; } $instructions = 'To initialize a project, please create a composer.json file as described in the http://getcomposer.org/ "Getting Started" section'; throw new \InvalidArgumentException($message . PHP_EOL . $instructions); } $file->validateSchema(JsonFile::LAX_SCHEMA); $localConfig = $file->read(); } // Load config and override with local config/auth config $config = static::createConfig($io); $config->merge($localConfig); if (isset($composerFile)) { if ($io && $io->isDebug()) { $io->write('Loading config file ' . $composerFile); } $localAuthFile = new JsonFile(dirname(realpath($composerFile)) . '/auth.json'); if ($localAuthFile->exists()) { if ($io && $io->isDebug()) { $io->write('Loading config file ' . $localAuthFile->getPath()); } $config->merge(array('config' => $localAuthFile->read())); $config->setAuthConfigSource(new JsonConfigSource($localAuthFile, true)); } } // load auth configs into the IO instance $io->loadConfiguration($config); $vendorDir = $config->get('vendor-dir'); $binDir = $config->get('bin-dir'); // setup process timeout ProcessExecutor::setTimeout((int) $config->get('process-timeout')); // initialize composer $composer = new Composer(); $composer->setConfig($config); // initialize event dispatcher $dispatcher = new EventDispatcher($composer, $io); // initialize repository manager $rm = $this->createRepositoryManager($io, $config, $dispatcher); // load local repository $this->addLocalRepository($rm, $vendorDir); // load package $parser = new VersionParser(); $loader = new Package\Loader\RootPackageLoader($rm, $config, $parser, new ProcessExecutor($io)); $package = $loader->load($localConfig); // initialize installation manager $im = $this->createInstallationManager(); // Composer composition $composer->setPackage($package); $composer->setRepositoryManager($rm); $composer->setInstallationManager($im); // initialize download manager $dm = $this->createDownloadManager($io, $config, $dispatcher); $composer->setDownloadManager($dm); $composer->setEventDispatcher($dispatcher); // initialize autoload generator $generator = new AutoloadGenerator($dispatcher, $io); $composer->setAutoloadGenerator($generator); // add installers to the manager $this->createDefaultInstallers($im, $composer, $io); $globalRepository = $this->createGlobalRepository($config, $vendorDir); $pm = $this->createPluginManager($composer, $io, $globalRepository); $composer->setPluginManager($pm); if (!$disablePlugins) { $pm->loadInstalledPlugins(); } // purge packages if they have been deleted on the filesystem $this->purgePackages($rm, $im); // init locker if possible if (isset($composerFile)) { $lockFile = "json" === pathinfo($composerFile, PATHINFO_EXTENSION) ? substr($composerFile, 0, -4) . 'lock' : $composerFile . '.lock'; $locker = new Package\Locker($io, new JsonFile($lockFile, new RemoteFilesystem($io, $config)), $rm, $im, md5_file($composerFile)); $composer->setLocker($locker); } return $composer; }
/** * @return Config * * @throws \Exception */ protected function getConfig() { if (!isset($this->config)) { $configFilePath = $this->configPathFromInput ?: ConfigFactory::getHomeDir() . '/config.json'; $this->config = ConfigFactory::createConfig($configFilePath); $configFile = new JsonFile($configFilePath); $this->configPath = $configFile->getPath(); $this->configSource = new JsonConfigSource($configFile); // initialize the global file if it's not there if (!$configFile->exists()) { $path = $configFile->getPath(); $dir = dirname($path); if (!is_dir($dir)) { mkdir($dir, 0777, true); } touch($configFile->getPath()); $configFile->write(['config' => new \ArrayObject()]); @chmod($configFile->getPath(), 0600); } if (!$configFile->exists()) { throw new \RuntimeException('No config.json found in the current directory'); } } return $this->config; }
protected function updateFileCleanly(JsonFile $json, array $base, array $new, $requireKey, $removeKey, $sortPackages) { $extra = $this->extra; $contents = file_get_contents($json->getPath()); $manipulator = new JsonManipulator($contents); foreach ($new as $package => $constraint) { if (!$manipulator->addLink($requireKey, $package, $constraint, $sortPackages)) { return false; } if (!$manipulator->removeSubNode($removeKey, $package)) { return false; } } if (isset($extra['installer-modules']) && is_array($extra['installer-modules'])) { $manipulator->addSubNode('extra', 'installer-modules', $extra['installer-modules']); } $paths = $this->getComposer()->getPackage()->getExtra(); $paths = $paths['installer-paths']; if (isset($extra['installer-paths']) && is_array($extra['installer-paths'])) { foreach ($extra['installer-paths'] as $path => $list) { if (!is_array($list)) { continue; } if (isset($paths[$path])) { $result = array_merge($paths[$path], $list); $result = array_keys(array_flip($result)); $paths[$path] = $result; } else { $paths[$path] = $list; } } } if (isset($extra['system-modules']) && is_array($extra['system-modules'])) { $modules = $extra['system-modules']; foreach ($modules as &$module) { if (strpos($module, '/') === false) { $module = 'opis-colibri/' . $module; } } $path = 'system/modules/{$name}/'; if (isset($paths[$path])) { $result = array_merge($paths[$path], $modules); $result = array_keys(array_flip($result)); $paths[$path] = $result; } } $manipulator->addSubNode('extra', 'installer-paths', $paths); file_put_contents($json->getPath(), $manipulator->getContents()); return true; }
/** * Creates a Composer instance * * @param IOInterface $io IO instance * @param array|string|null $localConfig either a configuration array or a filename to read from, if null it will * read from the default filename * @param bool $disablePlugins Whether plugins should not be loaded * @param bool $fullLoad Whether to initialize everything or only main project stuff (used when loading the global composer) * @throws \InvalidArgumentException * @throws \UnexpectedValueException * @return Composer */ public function createComposer(IOInterface $io, $localConfig = null, $disablePlugins = false, $cwd = null, $fullLoad = true) { $cwd = $cwd ?: getcwd(); // load Composer configuration if (null === $localConfig) { $localConfig = static::getComposerFile(); } if (is_string($localConfig)) { $composerFile = $localConfig; $file = new JsonFile($localConfig, null, $io); if (!$file->exists()) { if ($localConfig === './composer.json' || $localConfig === 'composer.json') { $message = 'Composer could not find a composer.json file in ' . $cwd; } else { $message = 'Composer could not find the config file: ' . $localConfig; } $instructions = 'To initialize a project, please create a composer.json file as described in the https://getcomposer.org/ "Getting Started" section'; throw new \InvalidArgumentException($message . PHP_EOL . $instructions); } $file->validateSchema(JsonFile::LAX_SCHEMA); $jsonParser = new JsonParser(); try { $jsonParser->parse(file_get_contents($localConfig), JsonParser::DETECT_KEY_CONFLICTS); } catch (\Seld\JsonLint\DuplicateKeyException $e) { $details = $e->getDetails(); $io->writeError('<warning>Key ' . $details['key'] . ' is a duplicate in ' . $localConfig . ' at line ' . $details['line'] . '</warning>'); } $localConfig = $file->read(); } // Load config and override with local config/auth config $config = static::createConfig($io, $cwd); $config->merge($localConfig); if (isset($composerFile)) { $io->writeError('Loading config file ' . $composerFile, true, IOInterface::DEBUG); $localAuthFile = new JsonFile(dirname(realpath($composerFile)) . '/auth.json'); if ($localAuthFile->exists()) { $io->writeError('Loading config file ' . $localAuthFile->getPath(), true, IOInterface::DEBUG); $config->merge(array('config' => $localAuthFile->read())); $config->setAuthConfigSource(new JsonConfigSource($localAuthFile, true)); } } $vendorDir = $config->get('vendor-dir'); $binDir = $config->get('bin-dir'); // initialize composer $composer = new Composer(); $composer->setConfig($config); if ($fullLoad) { // load auth configs into the IO instance $io->loadConfiguration($config); } $rfs = self::createRemoteFilesystem($io, $config); // initialize event dispatcher $dispatcher = new EventDispatcher($composer, $io); $composer->setEventDispatcher($dispatcher); // initialize repository manager $rm = $this->createRepositoryManager($io, $config, $dispatcher, $rfs); $composer->setRepositoryManager($rm); // load local repository $this->addLocalRepository($io, $rm, $vendorDir); // force-set the version of the global package if not defined as // guessing it adds no value and only takes time if (!$fullLoad && !isset($localConfig['version'])) { $localConfig['version'] = '1.0.0'; } // load package $parser = new VersionParser(); $guesser = new VersionGuesser($config, new ProcessExecutor($io), $parser); $loader = new Package\Loader\RootPackageLoader($rm, $config, $parser, $guesser); $package = $loader->load($localConfig, 'Composer\\Package\\RootPackage', $cwd); $composer->setPackage($package); // initialize installation manager $im = $this->createInstallationManager(); $composer->setInstallationManager($im); if ($fullLoad) { // initialize download manager $dm = $this->createDownloadManager($io, $config, $dispatcher, $rfs); $composer->setDownloadManager($dm); // initialize autoload generator $generator = new AutoloadGenerator($dispatcher, $io); $composer->setAutoloadGenerator($generator); } // add installers to the manager (must happen after download manager is created since they read it out of $composer) $this->createDefaultInstallers($im, $composer, $io); if ($fullLoad) { $globalComposer = $this->createGlobalComposer($io, $config, $disablePlugins); $pm = $this->createPluginManager($io, $composer, $globalComposer, $disablePlugins); $composer->setPluginManager($pm); $pm->loadInstalledPlugins(); // once we have plugins and custom installers we can // purge packages from local repos if they have been deleted on the filesystem if ($rm->getLocalRepository()) { $this->purgePackages($rm->getLocalRepository(), $im); } } // init locker if possible if ($fullLoad && isset($composerFile)) { $lockFile = "json" === pathinfo($composerFile, PATHINFO_EXTENSION) ? substr($composerFile, 0, -4) . 'lock' : $composerFile . '.lock'; $locker = new Package\Locker($io, new JsonFile($lockFile, null, $io), $rm, $im, file_get_contents($composerFile)); $composer->setLocker($locker); } return $composer; }
/** * Cleanly update a Composer JSON file. * * @param JsonFile $json * @param array $new * @param string $requireKey * @param string $removeKey * @param boolean $sortPackages * @param boolean $postreset * * @return boolean */ private function updateFileCleanly(JsonFile $json, array $new, $requireKey, $removeKey, $sortPackages, $postreset) { $contents = file_get_contents($json->getPath()); $manipulator = new JsonManipulator($contents); foreach ($new as $package => $constraint) { if ($postreset) { $constraint = $this->findBestVersionForPackage($package); } if (!$manipulator->addLink($requireKey, $package, $constraint, $sortPackages)) { return false; } if (!$manipulator->removeSubNode($removeKey, $package)) { return false; } } file_put_contents($json->getPath(), $manipulator->getContents()); return true; }
/** * Loads previously dumped Packages in order to merge with updates. * * @return PackageInterface[] */ public function load() { $packages = []; $repoJson = new JsonFile($this->filename); $dirName = dirname($this->filename); if ($repoJson->exists()) { $loader = new ArrayLoader(); $packagesJson = $repoJson->read(); $jsonIncludes = isset($packagesJson['includes']) && is_array($packagesJson['includes']) ? $packagesJson['includes'] : []; if (isset($packagesJson['providers']) && is_array($packagesJson['providers']) && isset($packagesJson['providers-url'])) { $baseUrl = $this->homepage ? parse_url(rtrim($this->homepage, '/'), PHP_URL_PATH) . '/' : null; $baseUrlLength = strlen($baseUrl); foreach ($packagesJson['providers'] as $packageName => $provider) { $file = str_replace(['%package%', '%hash%'], [$packageName, $provider['sha256']], $packagesJson['providers-url']); if ($baseUrl && substr($file, 0, $baseUrlLength) === $baseUrl) { $file = substr($file, $baseUrlLength); } $jsonIncludes[$file] = $provider; } } foreach ($jsonIncludes as $includeFile => $includeConfig) { $includeJson = new JsonFile($dirName . '/' . $includeFile); if (!$includeJson->exists()) { $this->output->writeln(sprintf('<error>File \'%s\' does not exist, defined in "includes" in \'%s\'</error>', $includeJson->getPath(), $repoJson->getPath())); continue; } $jsonPackages = $includeJson->read(); $jsonPackages = isset($jsonPackages['packages']) && is_array($jsonPackages['packages']) ? $jsonPackages['packages'] : []; foreach ($jsonPackages as $jsonPackage) { if (is_array($jsonPackage)) { foreach ($jsonPackage as $jsonVersion) { if (is_array($jsonVersion)) { if (isset($jsonVersion['name']) && in_array($jsonVersion['name'], $this->packagesFilter)) { continue; } $package = $loader->load($jsonVersion); $packages[$package->getUniqueName()] = $package; } } } } } } return $packages; }
protected function updateFileCleanly(JsonFile $json, array $base, array $new, $requireKey, $removeKey, $sortPackages) { $contents = file_get_contents($json->getPath()); $manipulator = new JsonManipulator($contents); foreach ($new as $package => $constraint) { if (!$manipulator->addLink($requireKey, $package, $constraint, $sortPackages)) { return false; } if (!$manipulator->removeSubNode($removeKey, $package)) { return false; } } file_put_contents($json->getPath(), $manipulator->getContents()); return true; }
protected function execute(InputInterface $input, OutputInterface $output) { $packages = $input->getArgument('packages'); $packages = array_map('strtolower', $packages); $file = Factory::getComposerFile(); $jsonFile = new JsonFile($file); $composer = $jsonFile->read(); $composerBackup = file_get_contents($jsonFile->getPath()); $json = new JsonConfigSource($jsonFile); $type = $input->getOption('dev') ? 'require-dev' : 'require'; $altType = !$input->getOption('dev') ? 'require-dev' : 'require'; $io = $this->getIO(); if ($input->getOption('update-with-dependencies')) { $io->writeError('<warning>You are using the deprecated option "update-with-dependencies". This is now default behaviour. The --no-update-with-dependencies option can be used to remove a package without its dependencies.</warning>'); } // make sure name checks are done case insensitively foreach (array('require', 'require-dev') as $linkType) { if (isset($composer[$linkType])) { foreach ($composer[$linkType] as $name => $version) { $composer[$linkType][strtolower($name)] = $version; } } } foreach ($packages as $package) { if (isset($composer[$type][$package])) { $json->removeLink($type, $package); } elseif (isset($composer[$altType][$package])) { $io->writeError('<warning>' . $package . ' could not be found in ' . $type . ' but it is present in ' . $altType . '</warning>'); if ($io->isInteractive()) { if ($io->askConfirmation('Do you want to remove it from ' . $altType . ' [<comment>yes</comment>]? ', true)) { $json->removeLink($altType, $package); } } } else { $io->writeError('<warning>' . $package . ' is not required in your composer.json and has not been removed</warning>'); } } if ($input->getOption('no-update')) { return 0; } // Update packages $this->resetComposer(); $composer = $this->getComposer(true, $input->getOption('no-plugins')); $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'remove', $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); $install = Installer::create($io, $composer); $updateDevMode = !$input->getOption('update-no-dev'); $optimize = $input->getOption('optimize-autoloader') || $composer->getConfig()->get('optimize-autoloader'); $authoritative = $input->getOption('classmap-authoritative') || $composer->getConfig()->get('classmap-authoritative'); $install->setVerbose($input->getOption('verbose'))->setDevMode($updateDevMode)->setOptimizeAutoloader($optimize)->setClassMapAuthoritative($authoritative)->setUpdate(true)->setUpdateWhitelist($packages)->setWhitelistDependencies(!$input->getOption('no-update-with-dependencies'))->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))->setRunScripts(!$input->getOption('no-scripts')); $exception = null; try { $status = $install->run(); } catch (\Exception $exception) { $status = 1; } if ($status !== 0) { $io->writeError("\n" . '<error>Removal failed, reverting ' . $file . ' to its original content.</error>'); file_put_contents($jsonFile->getPath(), $composerBackup); } if ($exception) { throw $exception; } return $status; }
/** * Loads previously dumped Packages in order to merge with updates. * * @return PackageInterface[] */ public function load() { $packages = array(); $repoJson = new JsonFile($this->filename); $dirName = dirname($this->filename); if ($repoJson->exists()) { $loader = new ArrayLoader(); $jsonIncludes = $repoJson->read(); $jsonIncludes = isset($jsonIncludes['includes']) && is_array($jsonIncludes['includes']) ? $jsonIncludes['includes'] : array(); foreach ($jsonIncludes as $includeFile => $includeConfig) { $includeJson = new JsonFile($dirName . '/' . $includeFile); if (!$includeJson->exists()) { $this->output->writeln(sprintf('<error>File \'%s\' does not exist, defined in "includes" in \'%s\'</error>', $includeJson->getPath(), $repoJson->getPath())); continue; } $jsonPackages = $includeJson->read(); $jsonPackages = isset($jsonPackages['packages']) && is_array($jsonPackages['packages']) ? $jsonPackages['packages'] : array(); foreach ($jsonPackages as $jsonPackage) { if (is_array($jsonPackage)) { foreach ($jsonPackage as $jsonVersion) { if (is_array($jsonVersion)) { if (isset($jsonVersion['name']) && in_array($jsonVersion['name'], $this->packagesFilter)) { continue; } $package = $loader->load($jsonVersion); $packages[$package->getUniqueName()] = $package; } } } } } } return $packages; }
/** * @param JsonFile $json * @param array $base * @param array $new * @param $rootKey * * @return bool */ private function updateFileCleanly(JsonFile $json, array $base, array $new, $rootKey) { $contents = file_get_contents($json->getPath()); $manipulator = new JsonManipulator($contents); foreach ($new as $childKey => $childValue) { if (!$manipulator->addLink($rootKey, $childKey, $childValue)) { return false; } } file_put_contents($json->getPath(), $manipulator->getContents()); return true; }
private function loadProviderIncludes($dirName, $providerIncludes) { $loader = new ArrayLoader(); foreach ($providerIncludes as $key => $hash) { $includeFile = strtr($key, array('%hash%' => $hash['sha256'])); $includeJson = new JsonFile($dirName . '/' . $includeFile); if (!$includeJson->exists()) { $this->output->writeln(sprintf('<error>File \'%s\' does not exist, defined in "includes" in \'%s\'</error>', $includeJson->getPath(), $dirName)); continue; } $providers = $includeJson->read(); foreach ($providers['providers'] as $package => $hash) { $file = strtr($path, array('%package%' => $package, '%hash%' => $hash)); $json = new JsonFile($dirName . $file); if (!$json->exists()) { $this->output->writeln(sprintf('<error>File \'%s\' does not exist, defined in "includes" in \'%s\'</error>', $json->getPath(), $includeFile)); continue; } $packages = array_merge($packages, $this->loadPackages($loader, $json->read())); } } return $packages; }
/** * * @param array $extra * @param \Composer\IO\IOInterface $io * @return int */ private function updateJsonExtra($extra, $io) { $file = Factory::getComposerFile(); if (!file_exists($file) && !file_put_contents($file, "{\n}\n")) { $io->write('<error>' . $file . ' could not be created.</error>'); return 1; } if (!is_readable($file)) { $io->write('<error>' . $file . ' is not readable.</error>'); return 1; } if (!is_writable($file)) { $io->write('<error>' . $file . ' is not writable.</error>'); return 1; } $json = new JsonFile($file); $composer = $json->read(); $composerBackup = file_get_contents($json->getPath()); $extraKey = 'extra'; $baseExtra = array_key_exists($extraKey, $composer) ? $composer[$extraKey] : array(); if (!$this->updateFileCleanly($json, $baseExtra, $extra, $extraKey)) { foreach ($extra as $key => $value) { $baseExtra[$key] = $value; } $composer[$extraKey] = $baseExtra; $json->write($composer); } }
public function createComposer() { $cwd = sprintf('%s/%s', getcwd(), '.eva'); $factory = new Factory(); if (false) { $composer = $factory->createComposer($this->io, $config, true, $cwd); } // ----------------- // ----------------- // ----------------- $fullLoad = true; $composerFile = $cwd . '/manifest.composer.json'; $file = new JsonFile($composerFile); $file->validateSchema(JsonFile::LAX_SCHEMA); $localConfig = $file->read(); // ----------------- // ----------------- // ----------------- // Load config and override with local config/auth config // $config = Factory::createConfig($this->io, $cwd); $vendorDir = $cwd . '/manifests/vendor'; $config = $factory::createConfig($this->io, $cwd); $config->merge($localConfig); $config->merge(['config' => ['vendor-dir' => $vendorDir]]); $localAuthFile = new JsonFile(dirname(realpath($composerFile)) . '/auth.json'); if ($localAuthFile->exists()) { if ($this->io && $this->io->isDebug()) { $this->io->writeError('Loading config file ' . $localAuthFile->getPath()); } $config->merge(array('config' => $localAuthFile->read())); $config->setAuthConfigSource(new JsonConfigSource($localAuthFile, true)); } // initialize composer $composer = new Composer(); $composer->setConfig($config); // initialize event dispatcher $dispatcher = new EventDispatcher($composer, $this->io); $composer->setEventDispatcher($dispatcher); // initialize repository manager // $rm = $this->createRepositoryManager($io, $config, $dispatcher); // $composer->setRepositoryManager($rm); $rm = new RepositoryManager($this->io, $config, $dispatcher); $rm->setRepositoryClass('composer', ComposerRepository::class); $composer->setRepositoryManager($rm); // load local repository $rm->setLocalRepository(new InstalledFilesystemRepository(new JsonFile($vendorDir . '/composer/installed.json'))); // load package $parser = new VersionParser(); $guesser = new VersionGuesser($config, new ProcessExecutor($this->io), $parser); $loader = new RootPackageLoader($rm, $config, $parser, $guesser); $package = $loader->load($localConfig); $composer->setPackage($package); // initialize installation manager $im = new InstallationManager(); $composer->setInstallationManager($im); if ($fullLoad) { // initialize download manager $dm = $factory->createDownloadManager($this->io, $config, $dispatcher); $composer->setDownloadManager($dm); // initialize autoload generator $generator = new AutoloadGenerator($dispatcher, $this->io); $composer->setAutoloadGenerator($generator); } // add installers to the manager (must happen after download manager is created since they read it out of $composer) $im->addInstaller(new Installer\LibraryInstaller($this->io, $composer, null)); $im->addInstaller(new Installer\PearInstaller($this->io, $composer, 'pear-library')); $im->addInstaller(new Installer\PluginInstaller($this->io, $composer)); $im->addInstaller(new Installer\MetapackageInstaller($this->io)); // if ($fullLoad) { // $globalComposer = $this->createGlobalComposer($io, $config, $disablePlugins); // $pm = $this->createPluginManager($io, $composer, $globalComposer); // $composer->setPluginManager($pm); // // if (!$disablePlugins) { // $pm->loadInstalledPlugins(); // } // // // once we have plugins and custom installers we can // // purge packages from local repos if they have been deleted on the filesystem // if ($rm->getLocalRepository()) { // $this->purgePackages($rm->getLocalRepository(), $im); // } // } // init locker if possible if ($fullLoad && isset($composerFile)) { $lockFile = "json" === pathinfo($composerFile, PATHINFO_EXTENSION) ? substr($composerFile, 0, -4) . 'lock' : $composerFile . '.lock'; $locker = new Locker($this->io, new JsonFile($lockFile, new RemoteFilesystem($this->io, $config)), $rm, $im, file_get_contents($composerFile)); $composer->setLocker($locker); } // ----------------- // ----------------- // ----------------- return $composer; }
protected function execute(InputInterface $input, OutputInterface $output) { $file = Factory::getComposerFile(); $io = $this->getIO(); $newlyCreated = !file_exists($file); if (!file_exists($file) && !file_put_contents($file, "{\n}\n")) { $io->writeError('<error>' . $file . ' could not be created.</error>'); return 1; } if (!is_readable($file)) { $io->writeError('<error>' . $file . ' is not readable.</error>'); return 1; } if (!is_writable($file)) { $io->writeError('<error>' . $file . ' is not writable.</error>'); return 1; } if (filesize($file) === 0) { file_put_contents($file, "{\n}\n"); } $json = new JsonFile($file); $composerDefinition = $json->read(); $composerBackup = file_get_contents($json->getPath()); $composer = $this->getComposer(true, $input->getOption('no-plugins')); $repos = $composer->getRepositoryManager()->getRepositories(); $platformOverrides = $composer->getConfig()->get('platform') ?: array(); // initialize $this->repos as it is used by the parent InitCommand $this->repos = new CompositeRepository(array_merge(array(new PlatformRepository(array(), $platformOverrides)), $repos)); $phpVersion = $this->repos->findPackage('php', '*')->getVersion(); $requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'), $phpVersion); $requireKey = $input->getOption('dev') ? 'require-dev' : 'require'; $removeKey = $input->getOption('dev') ? 'require' : 'require-dev'; $baseRequirements = array_key_exists($requireKey, $composerDefinition) ? $composerDefinition[$requireKey] : array(); $requirements = $this->formatRequirements($requirements); // validate requirements format $versionParser = new VersionParser(); foreach ($requirements as $constraint) { $versionParser->parseConstraints($constraint); } $sortPackages = $input->getOption('sort-packages') || $composer->getConfig()->get('sort-packages'); if (!$this->updateFileCleanly($json, $baseRequirements, $requirements, $requireKey, $removeKey, $sortPackages)) { foreach ($requirements as $package => $version) { $baseRequirements[$package] = $version; if (isset($composerDefinition[$removeKey][$package])) { unset($composerDefinition[$removeKey][$package]); } } $composerDefinition[$requireKey] = $baseRequirements; $json->write($composerDefinition); } $io->writeError('<info>' . $file . ' has been ' . ($newlyCreated ? 'created' : 'updated') . '</info>'); if ($input->getOption('no-update')) { return 0; } $updateDevMode = !$input->getOption('update-no-dev'); $optimize = $input->getOption('optimize-autoloader') || $composer->getConfig()->get('optimize-autoloader'); $authoritative = $input->getOption('classmap-authoritative') || $composer->getConfig()->get('classmap-authoritative'); // Update packages $this->resetComposer(); $composer = $this->getComposer(true, $input->getOption('no-plugins')); $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'require', $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); $install = Installer::create($io, $composer); $install->setVerbose($input->getOption('verbose'))->setPreferSource($input->getOption('prefer-source'))->setPreferDist($input->getOption('prefer-dist'))->setDevMode($updateDevMode)->setOptimizeAutoloader($optimize)->setClassMapAuthoritative($authoritative)->setUpdate(true)->setUpdateWhitelist(array_keys($requirements))->setWhitelistDependencies($input->getOption('update-with-dependencies'))->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))->setPreferStable($input->getOption('prefer-stable'))->setPreferLowest($input->getOption('prefer-lowest')); $exception = null; try { $status = $install->run(); } catch (\Exception $exception) { $status = 1; } if ($status !== 0) { if ($newlyCreated) { $io->writeError("\n" . '<error>Installation failed, deleting ' . $file . '.</error>'); unlink($json->getPath()); } else { $io->writeError("\n" . '<error>Installation failed, reverting ' . $file . ' to its original content.</error>'); file_put_contents($json->getPath(), $composerBackup); } } if ($exception) { throw $exception; } return $status; }
/** * {@inheritDoc} */ protected function execute(InputInterface $input, OutputInterface $output) { // Open file in editor if ($input->getOption('editor')) { $editor = getenv('EDITOR'); if (!$editor) { if (defined('PHP_WINDOWS_VERSION_BUILD')) { $editor = 'notepad'; } else { foreach (array('vim', 'vi', 'nano', 'pico', 'ed') as $candidate) { if (exec('which ' . $candidate)) { $editor = $candidate; break; } } } } system($editor . ' ' . $this->configFile->getPath() . (defined('PHP_WINDOWS_VERSION_BUILD') ? '' : ' > `tty`')); return 0; } if (!$input->getOption('global')) { $this->config->merge($this->configFile->read()); } // List the configuration of the file settings if ($input->getOption('list')) { $this->listConfiguration($this->config->all(), $this->config->raw(), $output); return 0; } $settingKey = $input->getArgument('setting-key'); if (!$settingKey) { return 0; } // If the user enters in a config variable, parse it and save to file if (array() !== $input->getArgument('setting-value') && $input->getOption('unset')) { throw new \RuntimeException('You can not combine a setting value with --unset'); } // show the value if no value is provided if (array() === $input->getArgument('setting-value') && !$input->getOption('unset')) { $data = $this->config->all(); if (preg_match('/^repos?(?:itories)?(?:\\.(.+))?/', $settingKey, $matches)) { if (empty($matches[1])) { $value = isset($data['repositories']) ? $data['repositories'] : array(); } else { if (!isset($data['repositories'][$matches[1]])) { throw new \InvalidArgumentException('There is no ' . $matches[1] . ' repository defined'); } $value = $data['repositories'][$matches[1]]; } } elseif (strpos($settingKey, '.')) { $bits = explode('.', $settingKey); $data = $data['config']; foreach ($bits as $bit) { if (isset($data[$bit])) { $data = $data[$bit]; } elseif (isset($data[implode('.', $bits)])) { // last bit can contain domain names and such so try to join whatever is left if it exists $data = $data[implode('.', $bits)]; break; } else { throw new \RuntimeException($settingKey . ' is not defined'); } array_shift($bits); } $value = $data; } elseif (isset($data['config'][$settingKey])) { $value = $data['config'][$settingKey]; } else { throw new \RuntimeException($settingKey . ' is not defined'); } if (is_array($value)) { $value = json_encode($value); } $output->writeln($value); return 0; } $values = $input->getArgument('setting-value'); // what the user is trying to add/change // handle repositories if (preg_match('/^repos?(?:itories)?\\.(.+)/', $settingKey, $matches)) { if ($input->getOption('unset')) { return $this->configSource->removeRepository($matches[1]); } if (2 !== count($values)) { throw new \RuntimeException('You must pass the type and a url. Example: php composer.phar config repositories.foo vcs http://bar.com'); } return $this->configSource->addRepository($matches[1], array('type' => $values[0], 'url' => $values[1])); } // handle github-oauth if (preg_match('/^github-oauth\\.(.+)/', $settingKey, $matches)) { if ($input->getOption('unset')) { return $this->configSource->removeConfigSetting('github-oauth.' . $matches[1]); } if (1 !== count($values)) { throw new \RuntimeException('Too many arguments, expected only one token'); } return $this->configSource->addConfigSetting('github-oauth.' . $matches[1], $values[0]); } $booleanValidator = function ($val) { return in_array($val, array('true', 'false', '1', '0'), true); }; $booleanNormalizer = function ($val) { return $val !== 'false' && (bool) $val; }; // handle config values $uniqueConfigValues = array('process-timeout' => array('is_numeric', 'intval'), 'use-include-path' => array($booleanValidator, $booleanNormalizer), 'preferred-install' => array(function ($val) { return in_array($val, array('auto', 'source', 'dist'), true); }, function ($val) { return $val; }), 'notify-on-install' => array($booleanValidator, $booleanNormalizer), 'vendor-dir' => array('is_string', function ($val) { return $val; }), 'bin-dir' => array('is_string', function ($val) { return $val; }), 'cache-dir' => array('is_string', function ($val) { return $val; }), 'cache-files-dir' => array('is_string', function ($val) { return $val; }), 'cache-repo-dir' => array('is_string', function ($val) { return $val; }), 'cache-vcs-dir' => array('is_string', function ($val) { return $val; }), 'cache-ttl' => array('is_numeric', 'intval'), 'cache-files-ttl' => array('is_numeric', 'intval'), 'cache-files-maxsize' => array(function ($val) { return preg_match('/^\\s*([0-9.]+)\\s*(?:([kmg])(?:i?b)?)?\\s*$/i', $val) > 0; }, function ($val) { return $val; }), 'discard-changes' => array(function ($val) { return in_array($val, array('stash', 'true', 'false', '1', '0'), true); }, function ($val) { if ('stash' === $val) { return 'stash'; } return $val !== 'false' && (bool) $val; })); $multiConfigValues = array('github-protocols' => array(function ($vals) { if (!is_array($vals)) { return 'array expected'; } foreach ($vals as $val) { if (!in_array($val, array('git', 'https', 'http'))) { return 'valid protocols include: git, https, http'; } } return true; }, function ($vals) { return $vals; })); foreach ($uniqueConfigValues as $name => $callbacks) { if ($settingKey === $name) { if ($input->getOption('unset')) { return $this->configSource->removeConfigSetting($settingKey); } list($validator, $normalizer) = $callbacks; if (1 !== count($values)) { throw new \RuntimeException('You can only pass one value. Example: php composer.phar config process-timeout 300'); } if (true !== ($validation = $validator($values[0]))) { throw new \RuntimeException(sprintf('"%s" is an invalid value' . ($validation ? ' (' . $validation . ')' : ''), $values[0])); } return $this->configSource->addConfigSetting($settingKey, $normalizer($values[0])); } } foreach ($multiConfigValues as $name => $callbacks) { if ($settingKey === $name) { if ($input->getOption('unset')) { return $this->configSource->removeConfigSetting($settingKey); } list($validator, $normalizer) = $callbacks; if (true !== ($validation = $validator($values))) { throw new \RuntimeException(sprintf('%s is an invalid value' . ($validation ? ' (' . $validation . ')' : ''), json_encode($values))); } return $this->configSource->addConfigSetting($settingKey, $normalizer($values)); } } throw new \InvalidArgumentException('Setting ' . $settingKey . ' does not exist or is not supported by this command'); }
/** * {@inheritDoc} */ protected function execute(InputInterface $input, OutputInterface $output) { // Open file in editor if ($input->getOption('editor')) { $editor = escapeshellcmd(getenv('EDITOR')); if (!$editor) { if (Platform::isWindows()) { $editor = 'notepad'; } else { foreach (array('editor', 'vim', 'vi', 'nano', 'pico', 'ed') as $candidate) { if (exec('which ' . $candidate)) { $editor = $candidate; break; } } } } $file = $input->getOption('auth') ? $this->authConfigFile->getPath() : $this->configFile->getPath(); system($editor . ' ' . $file . (Platform::isWindows() ? '' : ' > `tty`')); return 0; } if (!$input->getOption('global')) { $this->config->merge($this->configFile->read()); $this->config->merge(array('config' => $this->authConfigFile->exists() ? $this->authConfigFile->read() : array())); } // List the configuration of the file settings if ($input->getOption('list')) { $this->listConfiguration($this->config->all(), $this->config->raw(), $output); return 0; } $settingKey = $input->getArgument('setting-key'); if (!$settingKey) { return 0; } // If the user enters in a config variable, parse it and save to file if (array() !== $input->getArgument('setting-value') && $input->getOption('unset')) { throw new \RuntimeException('You can not combine a setting value with --unset'); } // show the value if no value is provided if (array() === $input->getArgument('setting-value') && !$input->getOption('unset')) { $data = $this->config->all(); if (preg_match('/^repos?(?:itories)?(?:\\.(.+))?/', $settingKey, $matches)) { if (empty($matches[1])) { $value = isset($data['repositories']) ? $data['repositories'] : array(); } else { if (!isset($data['repositories'][$matches[1]])) { throw new \InvalidArgumentException('There is no ' . $matches[1] . ' repository defined'); } $value = $data['repositories'][$matches[1]]; } } elseif (strpos($settingKey, '.')) { $bits = explode('.', $settingKey); $data = $data['config']; $match = false; foreach ($bits as $bit) { $key = isset($key) ? $key . '.' . $bit : $bit; $match = false; if (isset($data[$key])) { $match = true; $data = $data[$key]; unset($key); } } if (!$match) { throw new \RuntimeException($settingKey . ' is not defined.'); } $value = $data; } elseif (isset($data['config'][$settingKey])) { $value = $this->config->get($settingKey, $input->getOption('absolute') ? 0 : Config::RELATIVE_PATHS); } else { throw new \RuntimeException($settingKey . ' is not defined'); } if (is_array($value)) { $value = json_encode($value); } $this->getIO()->write($value); return 0; } $values = $input->getArgument('setting-value'); // what the user is trying to add/change $booleanValidator = function ($val) { return in_array($val, array('true', 'false', '1', '0'), true); }; $booleanNormalizer = function ($val) { return $val !== 'false' && (bool) $val; }; // handle config values $uniqueConfigValues = array('process-timeout' => array('is_numeric', 'intval'), 'use-include-path' => array($booleanValidator, $booleanNormalizer), 'preferred-install' => array(function ($val) { return in_array($val, array('auto', 'source', 'dist'), true); }, function ($val) { return $val; }), 'store-auths' => array(function ($val) { return in_array($val, array('true', 'false', 'prompt'), true); }, function ($val) { if ('prompt' === $val) { return 'prompt'; } return $val !== 'false' && (bool) $val; }), 'notify-on-install' => array($booleanValidator, $booleanNormalizer), 'vendor-dir' => array('is_string', function ($val) { return $val; }), 'bin-dir' => array('is_string', function ($val) { return $val; }), 'archive-dir' => array('is_string', function ($val) { return $val; }), 'archive-format' => array('is_string', function ($val) { return $val; }), 'data-dir' => array('is_string', function ($val) { return $val; }), 'cache-dir' => array('is_string', function ($val) { return $val; }), 'cache-files-dir' => array('is_string', function ($val) { return $val; }), 'cache-repo-dir' => array('is_string', function ($val) { return $val; }), 'cache-vcs-dir' => array('is_string', function ($val) { return $val; }), 'cache-ttl' => array('is_numeric', 'intval'), 'cache-files-ttl' => array('is_numeric', 'intval'), 'cache-files-maxsize' => array(function ($val) { return preg_match('/^\\s*([0-9.]+)\\s*(?:([kmg])(?:i?b)?)?\\s*$/i', $val) > 0; }, function ($val) { return $val; }), 'bin-compat' => array(function ($val) { return in_array($val, array('auto', 'full')); }, function ($val) { return $val; }), 'discard-changes' => array(function ($val) { return in_array($val, array('stash', 'true', 'false', '1', '0'), true); }, function ($val) { if ('stash' === $val) { return 'stash'; } return $val !== 'false' && (bool) $val; }), 'autoloader-suffix' => array('is_string', function ($val) { return $val === 'null' ? null : $val; }), 'sort-packages' => array($booleanValidator, $booleanNormalizer), 'optimize-autoloader' => array($booleanValidator, $booleanNormalizer), 'classmap-authoritative' => array($booleanValidator, $booleanNormalizer), 'prepend-autoloader' => array($booleanValidator, $booleanNormalizer), 'disable-tls' => array($booleanValidator, $booleanNormalizer), 'secure-http' => array($booleanValidator, $booleanNormalizer), 'cafile' => array(function ($val) { return file_exists($val) && is_readable($val); }, function ($val) { return $val === 'null' ? null : $val; }), 'capath' => array(function ($val) { return is_dir($val) && is_readable($val); }, function ($val) { return $val === 'null' ? null : $val; }), 'github-expose-hostname' => array($booleanValidator, $booleanNormalizer)); $multiConfigValues = array('github-protocols' => array(function ($vals) { if (!is_array($vals)) { return 'array expected'; } foreach ($vals as $val) { if (!in_array($val, array('git', 'https', 'ssh'))) { return 'valid protocols include: git, https, ssh'; } } return true; }, function ($vals) { return $vals; }), 'github-domains' => array(function ($vals) { if (!is_array($vals)) { return 'array expected'; } return true; }, function ($vals) { return $vals; }), 'gitlab-domains' => array(function ($vals) { if (!is_array($vals)) { return 'array expected'; } return true; }, function ($vals) { return $vals; })); foreach ($uniqueConfigValues as $name => $callbacks) { if ($settingKey === $name) { if ($input->getOption('unset')) { return $this->configSource->removeConfigSetting($settingKey); } list($validator, $normalizer) = $callbacks; if (1 !== count($values)) { throw new \RuntimeException('You can only pass one value. Example: php composer.phar config process-timeout 300'); } if (true !== ($validation = $validator($values[0]))) { throw new \RuntimeException(sprintf('"%s" is an invalid value' . ($validation ? ' (' . $validation . ')' : ''), $values[0])); } return $this->configSource->addConfigSetting($settingKey, $normalizer($values[0])); } } foreach ($multiConfigValues as $name => $callbacks) { if ($settingKey === $name) { if ($input->getOption('unset')) { return $this->configSource->removeConfigSetting($settingKey); } list($validator, $normalizer) = $callbacks; if (true !== ($validation = $validator($values))) { throw new \RuntimeException(sprintf('%s is an invalid value' . ($validation ? ' (' . $validation . ')' : ''), json_encode($values))); } return $this->configSource->addConfigSetting($settingKey, $normalizer($values)); } } // handle repositories if (preg_match('/^repos?(?:itories)?\\.(.+)/', $settingKey, $matches)) { if ($input->getOption('unset')) { return $this->configSource->removeRepository($matches[1]); } if (2 === count($values)) { return $this->configSource->addRepository($matches[1], array('type' => $values[0], 'url' => $values[1])); } if (1 === count($values)) { $value = strtolower($values[0]); if (true === $booleanValidator($value)) { if (false === $booleanNormalizer($value)) { return $this->configSource->addRepository($matches[1], false); } } else { $value = JsonFile::parseJson($values[0]); return $this->configSource->addRepository($matches[1], $value); } } throw new \RuntimeException('You must pass the type and a url. Example: php composer.phar config repositories.foo vcs https://bar.com'); } // handle platform if (preg_match('/^platform\\.(.+)/', $settingKey, $matches)) { if ($input->getOption('unset')) { return $this->configSource->removeConfigSetting($settingKey); } return $this->configSource->addConfigSetting($settingKey, $values[0]); } // handle github-oauth if (preg_match('/^(github-oauth|gitlab-oauth|http-basic)\\.(.+)/', $settingKey, $matches)) { if ($input->getOption('unset')) { $this->authConfigSource->removeConfigSetting($matches[1] . '.' . $matches[2]); $this->configSource->removeConfigSetting($matches[1] . '.' . $matches[2]); return; } if ($matches[1] === 'github-oauth' || $matches[1] === 'gitlab-oauth') { if (1 !== count($values)) { throw new \RuntimeException('Too many arguments, expected only one token'); } $this->configSource->removeConfigSetting($matches[1] . '.' . $matches[2]); $this->authConfigSource->addConfigSetting($matches[1] . '.' . $matches[2], $values[0]); } elseif ($matches[1] === 'http-basic') { if (2 !== count($values)) { throw new \RuntimeException('Expected two arguments (username, password), got ' . count($values)); } $this->configSource->removeConfigSetting($matches[1] . '.' . $matches[2]); $this->authConfigSource->addConfigSetting($matches[1] . '.' . $matches[2], array('username' => $values[0], 'password' => $values[1])); } return; } throw new \InvalidArgumentException('Setting ' . $settingKey . ' does not exist or is not supported by this command'); }
public function execute() { $io = $this->getIO(); if (empty($this->arguments)) { $io->writeError('<error>No module was specified</error>'); return 1; } $modules = array_map(function ($value) { if (strpos($value, '/') === false) { $value = 'opis-colibri/' . $value; } if (strpos($value, ':') === false) { $value .= ':*'; } return $value; }, $this->arguments); $file = Factory::getComposerFile(); $json = new JsonFile($file); $composerDefinition = $json->read(); $composerBackup = file_get_contents($json->getPath()); $composer = $this->getComposer(); $repos = $composer->getRepositoryManager()->getRepositories(); $this->repos = new CompositeRepository(array_merge(array(new PlatformRepository()), $repos)); $requirements = $this->determineRequirements($modules); $requireKey = !$this->devMode ? 'require-dev' : 'require'; $removeKey = !$this->devMode ? 'require' : 'require-dev'; $baseRequirements = array_key_exists($requireKey, $composerDefinition) ? $composerDefinition[$requireKey] : array(); $requirements = $this->formatRequirements($requirements); $this->validateRequirements($requirements); $manager = $this->getComposer()->getRepositoryManager(); $io->write('Searching for modules..'); foreach ($requirements as $name => $version) { $pack = $manager->findPackage($name, $version); if ($pack == null) { $io->writeError('<error>Module ' . $name . ' doesn\'t exist</error>'); return 1; } if ($pack->getType() !== 'opis-colibri-module') { $io->writeError('<error>' . $name . ' is not a valid module</error>'); return 1; } $io->write('Found module ' . $pack->getName()); } $sortPackages = false; if (!$this->updateFileCleanly($json, $baseRequirements, $requirements, $requireKey, $removeKey, $sortPackages)) { foreach ($requirements as $package => $version) { $baseRequirements[$package] = $version; if (isset($composerDefinition[$removeKey][$package])) { unset($composerDefinition[$removeKey][$package]); } } $composerDefinition[$requireKey] = $baseRequirements; $json->write($composerDefinition); } $this->resetComposer(); $composer = $this->getComposer(); $install = Installer::create($io, $composer); $status = $install->setDevMode($this->devMode)->setUpdate(true)->setUpdateWhitelist(array_keys($requirements))->run(); if ($status !== 0) { $io->writeError('<error>Installation failed, reverting ' . $file . ' to its original content.</error>'); file_put_contents($json->getPath(), $composerBackup); } return $status; }