From 1aec1c1fc8b8ac3d2934d819f38d05e54caf552e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 29 Feb 2016 17:32:32 +0000 Subject: [PATCH] Add --repository flag to init command, and rename --repository-url to --repository in create-project, fixes #4200, closes #4207, closes #2604, fixes #2920 --- doc/03-cli.md | 11 +- src/Composer/Command/ArchiveCommand.php | 3 +- src/Composer/Command/ConfigCommand.php | 5 +- src/Composer/Command/CreateProjectCommand.php | 30 ++-- src/Composer/Command/HomeCommand.php | 5 +- src/Composer/Command/InitCommand.php | 33 +++- src/Composer/Command/SearchCommand.php | 3 +- src/Composer/Command/ShowCommand.php | 5 +- src/Composer/Factory.php | 62 +------- src/Composer/IO/IOInterface.php | 5 +- .../Package/Loader/RootPackageLoader.php | 3 +- src/Composer/Repository/RepositoryFactory.php | 148 ++++++++++++++++++ 12 files changed, 218 insertions(+), 95 deletions(-) create mode 100644 src/Composer/Repository/RepositoryFactory.php diff --git a/doc/03-cli.md b/doc/03-cli.md index fb9f633be..8f621d11a 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -50,6 +50,11 @@ php composer.phar init in format `foo/bar:1.0.0`. * **--require-dev:** Development requirements, see **--require**. * **--stability (-s):** Value for the `minimum-stability` field. +* **--repository:** Provide one (or more) custom repositories. They will be stored + in the generated composer.json, and used for auto-completion when prompting for + the list of requires. Every repository can be either an HTTP URL pointing + to a `composer` repository or a JSON string which similar to what the + [repositories](04-schema.md#repositories) key accepts. ## install @@ -541,9 +546,11 @@ By default the command checks for the packages on packagist.org. ### Options -* **--repository-url:** Provide a custom repository to search for the package, +* **--repository:** Provide a custom repository to search for the package, which will be used instead of packagist. Can be either an HTTP URL pointing - to a `composer` repository, or a path to a local `packages.json` file. + to a `composer` repository, a path to a local `packages.json` file, or a + JSON string which similar to what the [repositories](04-schema.md#repositories) + key accepts. * **--stability (-s):** Minimum stability of package. Defaults to `stable`. * **--prefer-source:** Install packages from `source` when available. * **--prefer-dist:** Install packages from `dist` when available. diff --git a/src/Composer/Command/ArchiveCommand.php b/src/Composer/Command/ArchiveCommand.php index 104877d8d..442b0db78 100644 --- a/src/Composer/Command/ArchiveCommand.php +++ b/src/Composer/Command/ArchiveCommand.php @@ -16,6 +16,7 @@ use Composer\Factory; use Composer\IO\IOInterface; use Composer\Config; use Composer\Repository\CompositeRepository; +use Composer\Repository\RepositoryFactory; use Composer\Script\ScriptEvents; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; @@ -126,7 +127,7 @@ EOT $localRepo = $composer->getRepositoryManager()->getLocalRepository(); $repo = new CompositeRepository(array_merge(array($localRepo), $composer->getRepositoryManager()->getRepositories())); } else { - $defaultRepos = Factory::createDefaultRepositories($this->getIO()); + $defaultRepos = RepositoryFactory::default($this->getIO()); $io->writeError('No composer.json found in the current directory, searching packages from ' . implode(', ', array_keys($defaultRepos))); $repo = new CompositeRepository($defaultRepos); } diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index 2d982ceb8..a3e590dc2 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -447,10 +447,7 @@ EOT return $this->configSource->addRepository($matches[1], false); } } else { - $value = json_decode($values[0], true); - if (JSON_ERROR_NONE !== json_last_error()) { - throw new \InvalidArgumentException(sprintf('%s is not valid JSON.', $values[0])); - } + $value = JsonFile::parseJson($values[0]); return $this->configSource->addRepository($matches[1], $value); } diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 44c04e0c8..744161727 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -22,6 +22,7 @@ use Composer\Package\BasePackage; use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Operation\InstallOperation; use Composer\Package\Version\VersionSelector; +use Composer\Repository\RepositoryFactory; use Composer\Repository\ComposerRepository; use Composer\Repository\CompositeRepository; use Composer\Repository\FilesystemRepository; @@ -60,7 +61,8 @@ class CreateProjectCommand extends BaseCommand new InputOption('stability', 's', InputOption::VALUE_REQUIRED, 'Minimum-stability allowed (unless a version is specified).'), new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'), - new InputOption('repository-url', null, InputOption::VALUE_REQUIRED, 'Pick a different repository url to look for the package.'), + new InputOption('repository', null, InputOption::VALUE_REQUIRED, 'Pick a different repository (as url or json config) to look for the package.'), + new InputOption('repository-url', null, InputOption::VALUE_REQUIRED, 'DEPRECATED: Use --repository instead.'), new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of require-dev packages (enabled by default, only present for BC).'), new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'), new InputOption('no-plugins', null, InputOption::VALUE_NONE, 'Whether to disable plugins.'), @@ -92,7 +94,7 @@ To setup a developer workable version you should create the project using the so controlled code by appending the '--prefer-source' flag. To install a package from another repository than the default one you -can pass the '--repository-url=https://myrepository.org' flag. +can pass the '--repository=https://myrepository.org' flag. EOT ) @@ -125,7 +127,7 @@ EOT $preferSource, $preferDist, !$input->getOption('no-dev'), - $input->getOption('repository-url'), + $input->getOption('repository') || $input->getOption('repository-url'), $input->getOption('no-plugins'), $input->getOption('no-scripts'), $input->getOption('keep-vcs'), @@ -135,7 +137,7 @@ EOT ); } - public function installProject(IOInterface $io, Config $config, InputInterface $input, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false, $noInstall = false, $ignorePlatformReqs = false) + public function installProject(IOInterface $io, Config $config, InputInterface $input, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repository = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false, $noInstall = false, $ignorePlatformReqs = false) { $oldCwd = getcwd(); @@ -143,7 +145,7 @@ EOT $io->loadConfiguration($config); if ($packageName !== null) { - $installedFromVcs = $this->installRootPackage($io, $config, $packageName, $directory, $packageVersion, $stability, $preferSource, $preferDist, $installDevPackages, $repositoryUrl, $disablePlugins, $noScripts, $keepVcs, $noProgress); + $installedFromVcs = $this->installRootPackage($io, $config, $packageName, $directory, $packageVersion, $stability, $preferSource, $preferDist, $installDevPackages, $repository, $disablePlugins, $noScripts, $keepVcs, $noProgress); } else { $installedFromVcs = false; } @@ -239,22 +241,12 @@ EOT return 0; } - protected function installRootPackage(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false) + protected function installRootPackage(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repository = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false) { - if (null === $repositoryUrl) { - $sourceRepo = new CompositeRepository(Factory::createDefaultRepositories($io, $config)); - } elseif ("json" === pathinfo($repositoryUrl, PATHINFO_EXTENSION) && file_exists($repositoryUrl)) { - $json = new JsonFile($repositoryUrl, Factory::createRemoteFilesystem($io, $config)); - $data = $json->read(); - if (!empty($data['packages']) || !empty($data['includes']) || !empty($data['provider-includes'])) { - $sourceRepo = new ComposerRepository(array('url' => 'file://' . strtr(realpath($repositoryUrl), '\\', '/')), $io, $config); - } else { - $sourceRepo = new FilesystemRepository($json); - } - } elseif (0 === strpos($repositoryUrl, 'http')) { - $sourceRepo = new ComposerRepository(array('url' => $repositoryUrl), $io, $config); + if (null === $repository) { + $sourceRepo = new CompositeRepository(RepositoryFactory::default($io, $config)); } else { - throw new \InvalidArgumentException("Invalid repository url given. Has to be a .json file or an http url."); + $sourceRepo = RepositoryFactory::fromString($io, $config, $repository, true); } $parser = new VersionParser(); diff --git a/src/Composer/Command/HomeCommand.php b/src/Composer/Command/HomeCommand.php index 706308bc4..a5c3fe0c7 100644 --- a/src/Composer/Command/HomeCommand.php +++ b/src/Composer/Command/HomeCommand.php @@ -16,6 +16,7 @@ use Composer\Factory; use Composer\Package\CompletePackageInterface; use Composer\Repository\RepositoryInterface; use Composer\Repository\ArrayRepository; +use Composer\Repository\RepositoryFactory; use Composer\Util\Platform; use Composer\Util\ProcessExecutor; use Symfony\Component\Console\Input\InputArgument; @@ -153,8 +154,6 @@ EOT ); } - $defaultRepos = Factory::createDefaultRepositories($this->getIO()); - - return $defaultRepos; + return RepositoryFactory::default($this->getIO()); } } diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 818c8ba14..08d8c7c91 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -15,6 +15,7 @@ namespace Composer\Command; use Composer\DependencyResolver\Pool; use Composer\Json\JsonFile; use Composer\Factory; +use Composer\Repository\RepositoryFactory; use Composer\Package\BasePackage; use Composer\Package\Version\VersionParser; use Composer\Package\Version\VersionSelector; @@ -61,6 +62,7 @@ class InitCommand extends BaseCommand new InputOption('require-dev', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Package to require for development with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"'), new InputOption('stability', 's', InputOption::VALUE_REQUIRED, 'Minimum stability (empty or one of: '.implode(', ', array_keys(BasePackage::$stabilities)).')'), new InputOption('license', 'l', InputOption::VALUE_REQUIRED, 'License of package'), + new InputOption('repository', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Add custom repositories, either by URL or using JSON arrays'), )) ->setHelp(<<init command creates a basic composer.json file @@ -78,8 +80,9 @@ EOT */ protected function execute(InputInterface $input, OutputInterface $output) { - $whitelist = array('name', 'description', 'author', 'type', 'homepage', 'require', 'require-dev', 'stability', 'license'); + $io = $this->getIO(); + $whitelist = array('name', 'description', 'author', 'type', 'homepage', 'require', 'require-dev', 'stability', 'license'); $options = array_filter(array_intersect_key($input->getOptions(), array_flip($whitelist))); if (isset($options['author'])) { @@ -87,6 +90,14 @@ EOT unset($options['author']); } + $repositories = $input->getOption('repository'); + if ($repositories) { + $config = Factory::createConfig($io); + foreach ($repositories as $repo) { + $options['repositories'][] = RepositoryFactory::configFromString($io, $config, $repo); + } + } + if (isset($options['stability'])) { $options['minimum-stability'] = $options['stability']; unset($options['stability']); @@ -106,7 +117,6 @@ EOT $file = new JsonFile('composer.json'); $json = $file->encode($options); - $io = $this->getIO(); if ($input->isInteractive()) { $io->writeError(array('', $json, '')); @@ -145,6 +155,23 @@ EOT $io = $this->getIO(); $formatter = $this->getHelperSet()->get('formatter'); + // initialize repos if configured + $repositories = $input->getOption('repository'); + if ($repositories) { + $config = Factory::createConfig($io); + $repos = array(new PlatformRepository); + foreach ($repositories as $repo) { + $repos[] = RepositoryFactory::fromString($io, $config, $repo); + } + $repos[] = RepositoryFactory::createRepo($io, $config, array( + 'type' => 'composer', + 'url' => 'https://packagist.org', + )); + + $this->repos = new CompositeRepository($repos); + unset($repos, $config, $repositories); + } + $io->writeError(array( '', $formatter->formatBlock('Welcome to the Composer config generator', 'bg=blue;fg=white', true), @@ -318,7 +345,7 @@ EOT if (!$this->repos) { $this->repos = new CompositeRepository(array_merge( array(new PlatformRepository), - Factory::createDefaultRepositories($this->getIO()) + RepositoryFactory::default($this->getIO()) )); } diff --git a/src/Composer/Command/SearchCommand.php b/src/Composer/Command/SearchCommand.php index d79feed26..5f0fb648b 100644 --- a/src/Composer/Command/SearchCommand.php +++ b/src/Composer/Command/SearchCommand.php @@ -18,6 +18,7 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; +use Composer\Repository\RepositoryFactory; use Composer\Repository\RepositoryInterface; use Composer\Factory; use Composer\Plugin\CommandEvent; @@ -62,7 +63,7 @@ EOT $installedRepo = new CompositeRepository(array($localRepo, $platformRepo)); $repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories())); } else { - $defaultRepos = Factory::createDefaultRepositories($io); + $defaultRepos = RepositoryFactory::default($io); $io->writeError('No composer.json found in the current directory, showing packages from ' . implode(', ', array_keys($defaultRepos))); $installedRepo = $platformRepo; $repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos)); diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index a126456e9..9dad767f9 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -31,6 +31,7 @@ use Composer\Repository\CompositeRepository; use Composer\Repository\ComposerRepository; use Composer\Repository\PlatformRepository; use Composer\Repository\RepositoryInterface; +use Composer\Repository\RepositoryFactory; use Composer\Spdx\SpdxLicenses; /** @@ -106,7 +107,7 @@ EOT if ($composer) { $repos = new CompositeRepository($composer->getRepositoryManager()->getRepositories()); } else { - $defaultRepos = Factory::createDefaultRepositories($io); + $defaultRepos = RepositoryFactory::default($io); $repos = new CompositeRepository($defaultRepos); $io->writeError('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos))); } @@ -115,7 +116,7 @@ EOT $installedRepo = new CompositeRepository(array($localRepo, $platformRepo)); $repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories())); } elseif ($input->getOption('all')) { - $defaultRepos = Factory::createDefaultRepositories($io); + $defaultRepos = RepositoryFactory::default($io); $io->writeError('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos))); $installedRepo = $platformRepo; $repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos)); diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 2085adbce..e40d6b1bd 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -18,6 +18,7 @@ use Composer\IO\IOInterface; use Composer\Package\Archiver; use Composer\Package\Version\VersionGuesser; use Composer\Repository\RepositoryManager; +use Composer\Repository\RepositoryFactory; use Composer\Repository\WritableRepositoryInterface; use Composer\Util\Filesystem; use Composer\Util\Platform; @@ -221,39 +222,12 @@ class Factory ); } + /** + * @deprecated Use Composer\Repository\RepositoryFactory::default instead + */ public static function createDefaultRepositories(IOInterface $io = null, Config $config = null, RepositoryManager $rm = null) { - $repos = array(); - - if (!$config) { - $config = static::createConfig($io); - } - if (!$rm) { - if (!$io) { - throw new \InvalidArgumentException('This function requires either an IOInterface or a RepositoryManager'); - } - $factory = new static; - $rm = $factory->createRepositoryManager($io, $config, null, self::createRemoteFilesystem($io, $config)); - } - - foreach ($config->getRepositories() as $index => $repo) { - if (is_string($repo)) { - throw new \UnexpectedValueException('"repositories" should be an array of repository definitions, only a single repository was given'); - } - if (!is_array($repo)) { - throw new \UnexpectedValueException('Repository "'.$index.'" ('.json_encode($repo).') should be an array, '.gettype($repo).' given'); - } - if (!isset($repo['type'])) { - throw new \UnexpectedValueException('Repository "'.$index.'" ('.json_encode($repo).') must have a type defined'); - } - $name = is_int($index) && isset($repo['url']) ? preg_replace('{^https?://}i', '', $repo['url']) : $index; - while (isset($repos[$name])) { - $name .= '2'; - } - $repos[$name] = $rm->createRepository($repo['type'], $repo); - } - - return $repos; + return RepositoryFactory::default($io, $config, $rm); } /** @@ -336,7 +310,7 @@ class Factory $composer->setEventDispatcher($dispatcher); // initialize repository manager - $rm = $this->createRepositoryManager($io, $config, $dispatcher, $rfs); + $rm = RepositoryFactory::manager($io, $config, $dispatcher, $rfs); $composer->setRepositoryManager($rm); // load local repository @@ -399,30 +373,6 @@ class Factory return $composer; } - /** - * @param IOInterface $io - * @param Config $config - * @param EventDispatcher $eventDispatcher - * @return Repository\RepositoryManager - */ - protected function createRepositoryManager(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, RemoteFilesystem $rfs = null) - { - $rm = new RepositoryManager($io, $config, $eventDispatcher, $rfs); - $rm->setRepositoryClass('composer', 'Composer\Repository\ComposerRepository'); - $rm->setRepositoryClass('vcs', 'Composer\Repository\VcsRepository'); - $rm->setRepositoryClass('package', 'Composer\Repository\PackageRepository'); - $rm->setRepositoryClass('pear', 'Composer\Repository\PearRepository'); - $rm->setRepositoryClass('git', 'Composer\Repository\VcsRepository'); - $rm->setRepositoryClass('gitlab', 'Composer\Repository\VcsRepository'); - $rm->setRepositoryClass('svn', 'Composer\Repository\VcsRepository'); - $rm->setRepositoryClass('perforce', 'Composer\Repository\VcsRepository'); - $rm->setRepositoryClass('hg', 'Composer\Repository\VcsRepository'); - $rm->setRepositoryClass('artifact', 'Composer\Repository\ArtifactRepository'); - $rm->setRepositoryClass('path', 'Composer\Repository\PathRepository'); - - return $rm; - } - /** * @param Repository\RepositoryManager $rm * @param string $vendorDir diff --git a/src/Composer/IO/IOInterface.php b/src/Composer/IO/IOInterface.php index 380e2a00c..19d34d4bc 100644 --- a/src/Composer/IO/IOInterface.php +++ b/src/Composer/IO/IOInterface.php @@ -155,13 +155,12 @@ interface IOInterface * @param string|array $question The question to ask * @param array $choices List of choices to pick from * @param bool|string $default The default answer if the user enters nothing - * @param bool|int $attempts Max number of times to ask before giving up (false by default, which means infinite) + * @param bool|int $attempts Max number of times to ask before giving up (false by default, which means infinite) * @param string $errorMessage Message which will be shown if invalid value from choice list would be picked * @param bool $multiselect Select more than one value separated by comma * - * @return int|string|array The selected value or values (the key of the choices array) - * * @throws \InvalidArgumentException + * @return int|string|array The selected value or values (the key of the choices array) */ public function select($question, $choices, $default, $attempts = false, $errorMessage = 'Value "%s" is invalid', $multiselect = false); diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index a215f35eb..b6617623d 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -17,6 +17,7 @@ use Composer\Package\PackageInterface; use Composer\Package\AliasPackage; use Composer\Config; use Composer\Factory; +use Composer\Repository\RepositoryFactory; use Composer\Package\Version\VersionGuesser; use Composer\Package\Version\VersionParser; use Composer\Repository\RepositoryManager; @@ -141,7 +142,7 @@ class RootPackageLoader extends ArrayLoader $realPackage->setPreferStable((bool) $config['prefer-stable']); } - $repos = Factory::createDefaultRepositories(null, $this->config, $this->manager); + $repos = RepositoryFactory::default(null, $this->config, $this->manager); foreach ($repos as $repo) { $this->manager->addRepository($repo); } diff --git a/src/Composer/Repository/RepositoryFactory.php b/src/Composer/Repository/RepositoryFactory.php new file mode 100644 index 000000000..dc2538c02 --- /dev/null +++ b/src/Composer/Repository/RepositoryFactory.php @@ -0,0 +1,148 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +use Composer\Factory; +use Composer\IO\IOInterface; +use Composer\Config; +use Composer\EventDispatcher\EventDispatcher; +use Composer\Util\RemoteFilesystem; +use Composer\Json\JsonFile; + +/** + * @author Jordi Boggiano + */ +class RepositoryFactory +{ + /** + * @return array + */ + public static function configFromString(IOInterface $io, Config $config, $repository, $allowFilesystem = false) + { + if ("json" === pathinfo($repository, PATHINFO_EXTENSION)) { + $json = new JsonFile($repository, Factory::createRemoteFilesystem($io, $config)); + $data = $json->read(); + if (!empty($data['packages']) || !empty($data['includes']) || !empty($data['provider-includes'])) { + $repoConfig = array('type' => 'composer', 'url' => 'file://' . strtr(realpath($repository), '\\', '/')); + } elseif ($allowFilesystem) { + $repoConfig = array('type' => 'filesystem', 'json' => $json); + } else { + throw new \InvalidArgumentException("Invalid repository URL ($repository) given. This file does not contain a valid composer repository."); + } + } elseif (0 === strpos($repository, 'http')) { + $repoConfig = array('type' => 'composer', 'url' => $repository); + } elseif ('{' === substr($repository, 0, 1)) { + // assume it is a json object that makes a repo config + $repoConfig = JsonFile::parseJson($repository); + } else { + throw new \InvalidArgumentException("Invalid repository url ($repository) given. Has to be a .json file, an http url or a JSON object."); + } + + return $repoConfig; + } + + /** + * @return RepositoryInterface + */ + public static function fromString(IOInterface $io, Config $config, $repository, $allowFilesystem = false) + { + $repoConfig = static::configFromString($io, $config, $repository, $allowFilesystem); + + return static::createRepo($io, $config, $repoConfig); + } + + /** + * @return RepositoryInterface + */ + public static function createRepo($io, $config, array $repoConfig) + { + $rm = static::manager($io, $config, null, Factory::createRemoteFilesystem($io, $config)); + $repos = static::createRepos($rm, array($repoConfig)); + + return reset($repos); + } + + /** + * @return RepositoryInterface[] + */ + public static function default(IOInterface $io = null, Config $config = null, RepositoryManager $rm = null) + { + if (!$config) { + $config = Factory::createConfig($io); + } + if (!$rm) { + if (!$io) { + throw new \InvalidArgumentException('This function requires either an IOInterface or a RepositoryManager'); + } + $rm = static::manager($io, $config, null, Factory::createRemoteFilesystem($io, $config)); + } + + return static::createRepos($rm, $config->getRepositories()); + } + + /** + * @param IOInterface $io + * @param Config $config + * @param EventDispatcher $eventDispatcher + * @param RemoteFilesystem $rfs + * @return RepositoryManager + */ + public static function manager(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, RemoteFilesystem $rfs = null) + { + $rm = new RepositoryManager($io, $config, $eventDispatcher, $rfs); + $rm->setRepositoryClass('composer', 'Composer\Repository\ComposerRepository'); + $rm->setRepositoryClass('vcs', 'Composer\Repository\VcsRepository'); + $rm->setRepositoryClass('package', 'Composer\Repository\PackageRepository'); + $rm->setRepositoryClass('pear', 'Composer\Repository\PearRepository'); + $rm->setRepositoryClass('git', 'Composer\Repository\VcsRepository'); + $rm->setRepositoryClass('gitlab', 'Composer\Repository\VcsRepository'); + $rm->setRepositoryClass('svn', 'Composer\Repository\VcsRepository'); + $rm->setRepositoryClass('perforce', 'Composer\Repository\VcsRepository'); + $rm->setRepositoryClass('hg', 'Composer\Repository\VcsRepository'); + $rm->setRepositoryClass('artifact', 'Composer\Repository\ArtifactRepository'); + $rm->setRepositoryClass('path', 'Composer\Repository\PathRepository'); + + return $rm; + } + + /** + * @return RepositoryInterface[] + */ + private static function createRepos(RepositoryManager $rm, array $repoConfigs) + { + $repos = array(); + + foreach ($repoConfigs as $index => $repo) { + if (is_string($repo)) { + throw new \UnexpectedValueException('"repositories" should be an array of repository definitions, only a single repository was given'); + } + if (!is_array($repo)) { + throw new \UnexpectedValueException('Repository "'.$index.'" ('.json_encode($repo).') should be an array, '.gettype($repo).' given'); + } + if (!isset($repo['type'])) { + throw new \UnexpectedValueException('Repository "'.$index.'" ('.json_encode($repo).') must have a type defined'); + } + $name = is_int($index) && isset($repo['url']) ? preg_replace('{^https?://}i', '', $repo['url']) : $index; + while (isset($repos[$name])) { + $name .= '2'; + } + if ($repo['type'] === 'filesystem') { + $repos[$name] = new FilesystemRepository($repo['json']); + } else { + $repos[$name] = $rm->createRepository($repo['type'], $repo); + } + } + + return $repos; + } +}