diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index fb649c812..e60dc63ab 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -12,8 +12,6 @@ namespace Composer\Console; -use Composer\Console\Helper\WrapperInterface; - use Symfony\Component\Console\Application as BaseApplication; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -21,7 +19,6 @@ use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Symfony\Component\Finder\Finder; -use Composer\Console\Helper\Wrapper; use Composer\Command; use Composer\Composer; use Composer\Installer; @@ -29,6 +26,8 @@ use Composer\Downloader; use Composer\Repository; use Composer\Package; use Composer\Json\JsonFile; +use Composer\IO\IOInterface; +use Composer\IO\ConsoleIO; /** * The console application that handles the commands @@ -40,7 +39,7 @@ use Composer\Json\JsonFile; class Application extends BaseApplication { protected $composer; - protected $wrapper; + protected $io; public function __construct() { @@ -68,8 +67,7 @@ class Application extends BaseApplication public function doRun(InputInterface $input, OutputInterface $output) { $this->registerCommands(); - - $this->wrapper = new Wrapper($input, $output); + $this->io = new ConsoleIO($input, $output, $this->getHelperSet()); return parent::doRun($input, $output); } @@ -80,7 +78,7 @@ class Application extends BaseApplication public function getComposer() { if (null === $this->composer) { - $this->composer = self::bootstrapComposer(null, $this->wrapper); + $this->composer = self::bootstrapComposer(null, $this->io); } return $this->composer; @@ -91,7 +89,7 @@ class Application extends BaseApplication * * @return Composer */ - public static function bootstrapComposer($composerFile = null, WrapperInterface $wrapper) + public static function bootstrapComposer($composerFile = null, IOInterface $io) { // load Composer configuration if (null === $composerFile) { @@ -129,7 +127,7 @@ class Application extends BaseApplication $binDir = getenv('COMPOSER_BIN_DIR') ?: $packageConfig['config']['bin-dir']; // initialize repository manager - $rm = new Repository\RepositoryManager($wrapper); + $rm = new Repository\RepositoryManager($io); $rm->setLocalRepository(new Repository\FilesystemRepository(new JsonFile($vendorDir.'/.composer/installed.json'))); $rm->setRepositoryClass('composer', 'Composer\Repository\ComposerRepository'); $rm->setRepositoryClass('vcs', 'Composer\Repository\VcsRepository'); @@ -141,8 +139,8 @@ class Application extends BaseApplication $dm->setDownloader('git', new Downloader\GitDownloader()); $dm->setDownloader('svn', new Downloader\SvnDownloader()); $dm->setDownloader('hg', new Downloader\HgDownloader()); - $dm->setDownloader('pear', new Downloader\PearDownloader($wrapper)); - $dm->setDownloader('zip', new Downloader\ZipDownloader($wrapper)); + $dm->setDownloader('pear', new Downloader\PearDownloader($io)); + $dm->setDownloader('zip', new Downloader\ZipDownloader($io)); // initialize installation manager $im = new Installer\InstallationManager($vendorDir); diff --git a/src/Composer/Console/Helper/Wrapper.php b/src/Composer/Console/Helper/Wrapper.php deleted file mode 100644 index 6600386e6..000000000 --- a/src/Composer/Console/Helper/Wrapper.php +++ /dev/null @@ -1,160 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Console\Helper; - -use Composer\Console\Helper\WrapperInterface; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\ConsoleOutputInterface; -use Symfony\Component\Console\Helper\HelperInterface; - -/** - * Helper wrapper. - * - * @author François Pluchino - */ -class Wrapper implements WrapperInterface -{ - protected $input; - protected $output; - protected $helper; - - /** - * Constructor. - * - * @param InputInterface $input The input instance - * @param ConsoleOutputInterface $output The output instance - * @param HelperInterface $helper The helper instance - */ - public function __construct(InputInterface $input, ConsoleOutputInterface $output, HelperInterface $helper = null) - { - $this->input = $input; - $this->output = $output; - $this->helper = $helper; - } - - /** - * {@inheritDoc} - */ - public function getInput() - { - return $this->input; - } - - /** - * {@inheritDoc} - */ - public function setInput(InputInterface $input) - { - $this->input = $input; - } - - /** - * {@inheritDoc} - */ - public function getOutput() - { - return $this->output; - } - - /** - * {@inheritDoc} - */ - public function setOutput(ConsoleOutputInterface $output) - { - $this->output = $output; - } - - /** - * {@inheritDoc} - */ - public function getHelper() - { - return $this->helper; - } - - /** - * {@inheritDoc} - */ - public function setHelper(HelperInterface $helper) - { - $this->helper = $helper; - } - - /** - * {@inheritDoc} - */ - public function overwrite($messages, $size = 80, $newline = false, $type = 0) - { - for ($place = $size; $place > 0; $place--) { - $this->getOutput()->write("\x08"); - } - - $this->getOutput()->write($messages, false, $type); - - for ($place = ($size - strlen($messages)); $place > 0; $place--) { - $this->getOutput()->write(' '); - } - - // clean up the end line - for ($place = ($size - strlen($messages)); $place > 0; $place--) { - $this->getOutput()->write("\x08"); - } - - if ($newline) { - $this->getOutput()->writeln(''); - } - } - - /** - * {@inheritDoc} - */ - public function overwriteln($messages, $size = 80, $type = 0) - { - $this->overwrite($messages, $size, true, $type); - } - - /** - * {@inheritDoc} - */ - public function promptSilent($title = '') - { - // for windows OS - if (preg_match('/^win/i', PHP_OS)) { - $vbscript = sys_get_temp_dir() . '/prompt_password.vbs'; - file_put_contents($vbscript, - 'wscript.echo(Inputbox("' . addslashes($title) . '","' - . addslashes($title) . '", ""))'); - $command = "cscript //nologo " . escapeshellarg($vbscript); - $value = rtrim(shell_exec($command)); - unlink($vbscript); - $this->getOutput()->writeln(''); - - return $value; - } - - // for other OS - else { - $command = "/usr/bin/env bash -c 'echo OK'"; - - if (rtrim(shell_exec($command)) !== 'OK') { - throw new \RuntimeException("Can't invoke bash for silent prompt"); - } - - $command = "/usr/bin/env bash -c 'read -s mypassword && echo \$mypassword'"; - $value = rtrim(shell_exec($command)); - $this->getOutput()->writeln(''); - - return $value; - } - } -} diff --git a/src/Composer/Console/Helper/WrapperInterface.php b/src/Composer/Console/Helper/WrapperInterface.php deleted file mode 100644 index f0fe8b922..000000000 --- a/src/Composer/Console/Helper/WrapperInterface.php +++ /dev/null @@ -1,95 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Console\Helper; - -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\ConsoleOutputInterface; -use Symfony\Component\Console\Helper\HelperInterface; - -/** - * Helper wrapper interface. - * - * @author François Pluchino - */ -interface WrapperInterface -{ - /** - * Returns an InputInterface instance. - * - * @return InputInterface "InputArgument", "InputOption", "InputDefinition" - */ - function getInput(); - - /** - * Set an InputInterface instance. - * - * @param InputInterface $input The input - */ - function setInput(InputInterface $input); - - /** - * Returns an ConsoleOutput instance. - * - * @return ConsoleOutputInterface - */ - function getOutput(); - - /** - * Set an ConsoleOutput instance. - * - * @param ConsoleOutputInterface $output The output - */ - function setOutput(ConsoleOutputInterface $output); - - /** - * Returns an HelperInterface instance. - * - * @return HelperInterface - */ - function getHelper(); - - /** - * Set an HelperInterface instance. - * - * @param HelperInterface $helper The helper - */ - function setHelper(HelperInterface $helper); - - /** - * Overwrites a previous message to the output. - * - * @param string|array $messages The message as an array of lines of a single string - * @param integer $size The size of line - * @param Boolean $newline Whether to add a newline or not - * @param integer $type The type of output - */ - public function overwrite($messages, $size = 80, $newline = false, $type = 0); - - /** - * Overwrites a previous message to the output and adds a newline at the end. - * - * @param string|array $messages The message as an array of lines of a single string - * @param integer $size The size of line - * @param integer $type The type of output - */ - public function overwriteln($messages, $size = 80, $type = 0); - - /** - * Interactively prompts for input without echoing to the terminal. - * - * @param string $title The title of prompt (used only for windows) - * - * @return string The value - */ - public function promptSilent($title = ''); -} diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 504460644..102b627e7 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -11,7 +11,7 @@ namespace Composer\Downloader; -use Composer\Console\Helper\WrapperInterface; +use Composer\IO\IOInterface; use Composer\Package\PackageInterface; /** @@ -23,17 +23,17 @@ use Composer\Package\PackageInterface; */ abstract class FileDownloader implements DownloaderInterface { - protected $wrapper; + protected $io; protected $bytesMax; /** * Constructor. * - * @param WrapperInterface $wrapper The Wrapper instance + * @param IOInterface $io The IO instance */ - public function __construct(WrapperInterface $wrapper) + public function __construct(IOInterface $io) { - $this->wrapper = $wrapper; + $this->io = $io; } /** @@ -66,7 +66,7 @@ abstract class FileDownloader implements DownloaderInterface $fileName = rtrim($path.'/'.md5(time().rand()).'.'.pathinfo($url, PATHINFO_EXTENSION), '.'); - $this->wrapper->getOutput()->writeln(" - Package " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); + $this->io->writeln(" - Package " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); if (!extension_loaded('openssl') && (0 === strpos($url, 'https:') || 0 === strpos($url, 'http://github.com'))) { // bypass https for github if openssl is disabled @@ -98,9 +98,9 @@ abstract class FileDownloader implements DownloaderInterface stream_context_set_params($ctx, array("notification" => array($this, 'callbackDownload'))); + $this->io->overwrite(" Downloading: connection...", 80); copy($url, $fileName, $ctx); - - $this->wrapper->overwriteln(" Downloading: OK", 80); + $this->io->overwriteln(" Downloading", 80); if (!file_exists($fileName)) { throw new \UnexpectedValueException($url.' could not be saved to '.$fileName.', make sure the' @@ -111,11 +111,11 @@ abstract class FileDownloader implements DownloaderInterface throw new \UnexpectedValueException('The checksum verification of the archive failed (downloaded from '.$url.')'); } - $this->wrapper->getOutput()->writeln(' Unpacking archive'); + $this->io->writeln(' Unpacking archive'); $this->extract($fileName, $path); - $this->wrapper->getOutput()->writeln(' Cleaning up'); + $this->io->writeln(' Cleaning up'); unlink($fileName); // If we have only a one dir inside it suppose to be a package itself @@ -130,8 +130,8 @@ abstract class FileDownloader implements DownloaderInterface rmdir($contentDir); } - $this->wrapper->overwrite(''); - $this->wrapper->getOutput()->writeln(''); + $this->io->overwrite(''); + $this->io->writeln(''); } /** @@ -190,7 +190,7 @@ abstract class FileDownloader implements DownloaderInterface $progression = round($progression, 0); if (in_array($progression, $levels)) { - $this->wrapper->overwrite(" Downloading: $progression%", 80); + $this->io->overwrite(" Downloading: $progression%", 80); } } diff --git a/src/Composer/IO/ConsoleIO.php b/src/Composer/IO/ConsoleIO.php new file mode 100644 index 000000000..cf2b2165f --- /dev/null +++ b/src/Composer/IO/ConsoleIO.php @@ -0,0 +1,374 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\IO; + +use Composer\IO\IOInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Helper\HelperSet; + +/** + * The Input/Output helper. + * + * @author François Pluchino + */ +class ConsoleIO implements IOInterface +{ + protected $input; + protected $output; + protected $helperSet; + protected $authentifications; + protected $lastUsername; + protected $lastPassword; + + /** + * Constructor. + * + * @param InputInterface $input The input instance + * @param ConsoleOutputInterface $output The output instance + * @param HelperSet $helperSet The helperSet instance + */ + public function __construct(InputInterface $input, ConsoleOutputInterface $output, HelperSet $helperSet) + { + $this->input = $input; + $this->output = $output; + $this->helperSet = $helperSet; + } + + /** + * {@inheritDoc} + */ + public function getFirstArgument() + { + return $this->input->getFirstArgument(); + } + + /** + * {@inheritDoc} + */ + public function hasParameterOption($values) + { + return $this->input->hasParameterOption($values); + } + + /** + * {@inheritDoc} + */ + public function getParameterOption($values, $default = false) + { + return $this->input->getParameterOption($values, $default); + } + + /** + * {@inheritDoc} + */ + public function bind(InputDefinition $definition) + { + $this->input->bind($definition); + } + + /** + * {@inheritDoc} + */ + public function validate() + { + $this->input->validate(); + } + + /** + * {@inheritDoc} + */ + public function getArguments() + { + return $this->input->getArguments(); + } + + /** + * {@inheritDoc} + */ + public function getArgument($name) + { + return $this->input->getArgument($name); + } + + /** + * {@inheritDoc} + */ + public function getOptions() + { + return $this->input->getOptions(); + } + + /** + * {@inheritDoc} + */ + public function getOption($name) + { + return $this->input->getOption($name); + } + + /** + * {@inheritDoc} + */ + public function isInteractive() + { + return $this->input->isInteractive(); + } + + /** + * {@inheritDoc} + */ + public function getErrorOutput() + { + return $this->output->getErrorOutput(); + } + + /** + * {@inheritDoc} + */ + public function setErrorOutput(OutputInterface $error) + { + $this->output->setErrorOutput($error); + } + + /** + * {@inheritDoc} + */ + public function write($messages, $newline = false, $type = 0) + { + $this->output->write($messages, $newline, $type); + } + + /** + * {@inheritDoc} + */ + public function writeln($messages, $type = 0) + { + $this->output->writeln($messages, $type); + } + + /** + * {@inheritDoc} + */ + public function overwrite($messages, $size = 80, $newline = false, $type = 0) + { + for ($place = $size; $place > 0; $place--) { + $this->write("\x08"); + } + + $this->write($messages, false, $type); + + for ($place = ($size - strlen($messages)); $place > 0; $place--) { + $this->write(' '); + } + + // clean up the end line + for ($place = ($size - strlen($messages)); $place > 0; $place--) { + $this->write("\x08"); + } + + if ($newline) { + $this->writeln(''); + } + } + + /** + * {@inheritDoc} + */ + public function overwriteln($messages, $size = 80, $type = 0) + { + $this->overwrite($messages, $size, true, $type); + } + + /** + * {@inheritDoc} + */ + public function setVerbosity($level) + { + $this->output->setVerbosity($level); + } + + /** + * {@inheritDoc} + */ + public function getVerbosity() + { + return $this->output->getVerbosity(); + } + + /** + * {@inheritDoc} + */ + public function setDecorated($decorated) + { + $this->output->setDecorated($decorated); + } + + /** + * {@inheritDoc} + */ + public function isDecorated() + { + return $this->output->isDecorated(); + } + + /** + * {@inheritDoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->output->setFormatter($formatter); + } + + /** + * {@inheritDoc} + */ + public function getFormatter() + { + return $this->output->getFormatter(); + } + + /** + * {@inheritDoc} + */ + public function ask($question, $default = null) + { + return $this->helperSet->get('dialog')->ask($this->output, $question, $default); + } + + /** + * {@inheritDoc} + */ + public function askConfirmation($question, $default = true) + { + return $this->helperSet->get('dialog')->askConfirmation($this->output, $question, $default); + } + + public function askAndValidate($question, $validator, $attempts = false, $default = null) + { + return $this->helperSet->get('dialog')->askAndValidate($this->output, $question, $validator, $attempts, $default); + } + + /** + * {@inheritDoc} + */ + public function askAndHideAnswer($question) + { + // for windows OS (does not hide the answer in the popup, but it never appears in the STDIN history) + if (preg_match('/^win/i', PHP_OS)) { + $vbscript = sys_get_temp_dir() . '/prompt_password.vbs'; + file_put_contents($vbscript, + 'wscript.echo(Inputbox("' . addslashes($question) . '","' + . addslashes($question) . '", ""))'); + $command = "cscript //nologo " . escapeshellarg($vbscript); + + $this->write($question); + + $value = rtrim(shell_exec($command)); + unlink($vbscript); + + for ($i = 0; $i < strlen($value); ++$i) { + $this->write('*'); + } + + $this->writeln(''); + + return $value; + } + + // for other OS with shell_exec (hide the answer) + else if (rtrim(shell_exec($command)) === 'OK') { + $command = "/usr/bin/env bash -c 'echo OK'"; + + $this->write($question); + + $command = "/usr/bin/env bash -c 'read -s mypassword && echo \$mypassword'"; + $value = rtrim(shell_exec($command)); + + for ($i = 0; $i < strlen($value); ++$i) { + $this->write('*'); + } + + $this->writeln(''); + + return $value; + } + + // for other OS without shell_exec (does not hide the answer) + $this->writeln(''); + + return $this->ask($question); + } + + /** + * {@inheritDoc} + */ + public function getLastUsername() + { + return $this->lastUsername; + } + + /** + * {@inheritDoc} + */ + public function getLastPassword() + { + return $this->lastPassword; + } + + /** + * {@inheritDoc} + */ + public function getAuthentifications() + { + if (null === $this->authentifications) { + $this->authentifications = array(); + } + + return $this->authentifications; + } + + /** + * {@inheritDoc} + */ + public function hasAuthentification($repositoryName) + { + $auths = $this->getAuthentifications(); + return isset($auths[$repositoryName]) ? true : false; + } + + /** + * {@inheritDoc} + */ + public function getAuthentification($repositoryName) + { + $auths = $this->getAuthentifications(); + return isset($auths[$repositoryName]) ? $auths[$repositoryName] : array('username' => null, 'password' => null); + } + + /** + * {@inheritDoc} + */ + public function setAuthentification($repositoryName, $username, $password = null) + { + $auths = $this->getAuthentifications(); + $auths[$repositoryName] = array('username' => $username, 'password' => $password); + + $this->authentifications = $auths; + $this->lastUsername = $username; + $this->lastPassword = $password; + } +} diff --git a/src/Composer/IO/IOInterface.php b/src/Composer/IO/IOInterface.php new file mode 100644 index 000000000..b05d9db68 --- /dev/null +++ b/src/Composer/IO/IOInterface.php @@ -0,0 +1,143 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\IO; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Helper\HelperSet; + +/** + * The Input/Output helper interface. + * + * @author François Pluchino + */ +interface IOInterface extends InputInterface, ConsoleOutputInterface +{ + /** + * Overwrites a previous message to the output. + * + * @param string|array $messages The message as an array of lines of a single string + * @param integer $size The size of line + * @param Boolean $newline Whether to add a newline or not + * @param integer $type The type of output + */ + public function overwrite($messages, $size = 80, $newline = false, $type = 0); + + /** + * Overwrites a previous message to the output and adds a newline at the end. + * + * @param string|array $messages The message as an array of lines of a single string + * @param integer $size The size of line + * @param integer $type The type of output + */ + public function overwriteln($messages, $size = 80, $type = 0); + + /** + * Asks a question to the user. + * + * @param string|array $question The question to ask + * @param string $default The default answer if none is given by the user + * + * @return string The user answer + * + * @throws \RuntimeException If there is no data to read in the input stream + */ + public function ask($question, $default = null); + + /** + * Asks a confirmation to the user. + * + * The question will be asked until the user answers by nothing, yes, or no. + * + * @param string|array $question The question to ask + * @param Boolean $default The default answer if the user enters nothing + * + * @return Boolean true if the user has confirmed, false otherwise + */ + public function askConfirmation($question, $default = true); + + /** + * Asks for a value and validates the response. + * + * The validator receives the data to validate. It must return the + * validated data when the data is valid and throw an exception + * otherwise. + * + * @param string|array $question The question to ask + * @param callback $validator A PHP callback + * @param integer $attempts Max number of times to ask before giving up (false by default, which means infinite) + * @param string $default The default answer if none is given by the user + * + * @return mixed + * + * @throws \Exception When any of the validators return an error + */ + public function askAndValidate($question, $validator, $attempts = false, $default = null); + + /** + * Asks a question to the user and hide the answer. + * + * @param string $question The question to ask + * + * @return string The answer + */ + public function askAndHideAnswer($question); + + /** + * Get the last username entered. + * + * @return string The username + */ + public function getLastUsername(); + + /** + * Get the last password entered. + * + * @return string The password + */ + public function getLastPassword(); + + /** + * Get all authentification informations entered. + * + * @return array The map of authentification + */ + public function getAuthentifications(); + + /** + * Verify if the repository has a authentification informations. + * + * @param string $repositoryName The unique name of repository + * + * @return boolean + */ + public function hasAuthentification($repositoryName); + + /** + * Get the username and password of repository. + * + * @param string $repositoryName The unique name of repository + * + * @return array The 'username' and 'password' + */ + public function getAuthentification($repositoryName); + + /** + * Set the authentification informations for the repository. + * + * @param string $repositoryName The unique name of repository + * @param string $username The username + * @param string $password The password + */ + public function setAuthentification($repositoryName, $username, $password = null); +} diff --git a/src/Composer/Repository/RepositoryManager.php b/src/Composer/Repository/RepositoryManager.php index b6c902b6a..1ed0f18d0 100644 --- a/src/Composer/Repository/RepositoryManager.php +++ b/src/Composer/Repository/RepositoryManager.php @@ -12,7 +12,7 @@ namespace Composer\Repository; -use Composer\Console\Helper\WrapperInterface; +use Composer\IO\IOInterface; /** * Repositories manager. @@ -26,11 +26,11 @@ class RepositoryManager private $localRepository; private $repositories = array(); private $repositoryClasses = array(); - private $wrapper; + private $io; - public function __construct(WrapperInterface $wrapper) + public function __construct(IOInterface $io) { - $this->wrapper = $wrapper; + $this->io = $io; } /** @@ -75,7 +75,7 @@ class RepositoryManager } $class = $this->repositoryClasses[$type]; - return new $class($config, $this->wrapper); + return new $class($config, $this->io); } /** diff --git a/src/Composer/Repository/Vcs/GitBitbucketDriver.php b/src/Composer/Repository/Vcs/GitBitbucketDriver.php index 7718fcaa8..20e060e68 100644 --- a/src/Composer/Repository/Vcs/GitBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/GitBitbucketDriver.php @@ -13,11 +13,10 @@ namespace Composer\Repository\Vcs; use Composer\Json\JsonFile; -use Composer\Console\Helper\WrapperInterface; +use Composer\IO\IOInterface; /** * @author Per Bernhardt - * @author François Pluchino */ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface { @@ -28,13 +27,13 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface protected $rootIdentifier; protected $infoCache = array(); - public function __construct($url, WrapperInterface $wrapper) + public function __construct($url, IOInterface $io) { preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url, $match); $this->owner = $match[1]; $this->repository = $match[2]; - parent::__construct($url, $wrapper); + parent::__construct($url, $io); } /** @@ -50,7 +49,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getRootIdentifier() { if (null === $this->rootIdentifier) { - $repoData = json_decode(file_get_contents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository), true); + $repoData = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository), true); $this->rootIdentifier = !empty($repoData['main_branch']) ? $repoData['main_branch'] : 'master'; } @@ -92,7 +91,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getComposerInformation($identifier) { if (!isset($this->infoCache[$identifier])) { - $composer = @file_get_contents($this->getScheme() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/raw/'.$identifier.'/composer.json'); + $composer = $this->getContents($this->getScheme() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/raw/'.$identifier.'/composer.json'); if (!$composer) { throw new \UnexpectedValueException('Failed to retrieve composer information for identifier '.$identifier.' in '.$this->getUrl()); } @@ -100,7 +99,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface $composer = JsonFile::parseJson($composer); if (!isset($composer['time'])) { - $changeset = json_decode(file_get_contents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier), true); + $changeset = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier), true); $composer['time'] = $changeset['timestamp']; } $this->infoCache[$identifier] = $composer; @@ -115,7 +114,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getTags() { if (null === $this->tags) { - $tagsData = json_decode(file_get_contents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); + $tagsData = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); $this->tags = array(); foreach ($tagsData as $tag => $data) { $this->tags[$tag] = $data['raw_node']; @@ -131,7 +130,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getBranches() { if (null === $this->branches) { - $branchData = json_decode(file_get_contents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches'), true); + $branchData = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches'), true); $this->branches = array(); foreach ($branchData as $branch => $data) { $this->branches[$branch] = $data['raw_node']; diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index 971b13f73..a4e2f418d 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -3,11 +3,10 @@ namespace Composer\Repository\Vcs; use Composer\Json\JsonFile; -use Composer\Console\Helper\WrapperInterface; +use Composer\IO\IOInterface; /** * @author Jordi Boggiano - * @author François Pluchino */ class GitDriver extends VcsDriver implements VcsDriverInterface { @@ -16,11 +15,11 @@ class GitDriver extends VcsDriver implements VcsDriverInterface protected $rootIdentifier; protected $infoCache = array(); - public function __construct($url, WrapperInterface $wrapper) + public function __construct($url, IOInterface $io) { $this->tmpDir = sys_get_temp_dir() . '/composer-' . preg_replace('{[^a-z0-9]}i', '-', $url) . '/'; - parent::__construct($url, $wrapper); + parent::__construct($url, $io); } /** diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 9954f72c0..13071a594 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -3,11 +3,10 @@ namespace Composer\Repository\Vcs; use Composer\Json\JsonFile; -use Composer\Console\Helper\WrapperInterface; +use Composer\IO\IOInterface; /** * @author Jordi Boggiano - * @author François Pluchino */ class GitHubDriver extends VcsDriver implements VcsDriverInterface { @@ -18,13 +17,13 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface protected $rootIdentifier; protected $infoCache = array(); - public function __construct($url, WrapperInterface $wrapper) + public function __construct($url, IOInterface $io) { preg_match('#^(?:https?|git)://github\.com/([^/]+)/(.+?)(?:\.git)?$#', $url, $match); $this->owner = $match[1]; $this->repository = $match[2]; - parent::__construct($url, $wrapper); + parent::__construct($url, $io); } /** @@ -40,7 +39,7 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface public function getRootIdentifier() { if (null === $this->rootIdentifier) { - $repoData = json_decode(file_get_contents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository), true); + $repoData = json_decode($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository), true); $this->rootIdentifier = $repoData['master_branch'] ?: 'master'; } @@ -82,7 +81,7 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface public function getComposerInformation($identifier) { if (!isset($this->infoCache[$identifier])) { - $composer = @file_get_contents($this->getScheme() . '://raw.github.com/'.$this->owner.'/'.$this->repository.'/'.$identifier.'/composer.json'); + $composer = $this->getContents($this->getScheme() . '://raw.github.com/'.$this->owner.'/'.$this->repository.'/'.$identifier.'/composer.json'); if (!$composer) { throw new \UnexpectedValueException('Failed to retrieve composer information for identifier '.$identifier.' in '.$this->getUrl()); } @@ -90,7 +89,7 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface $composer = JsonFile::parseJson($composer); if (!isset($composer['time'])) { - $commit = json_decode(file_get_contents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/commits/'.$identifier), true); + $commit = json_decode($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/commits/'.$identifier), true); $composer['time'] = $commit['commit']['committer']['date']; } $this->infoCache[$identifier] = $composer; @@ -105,7 +104,7 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface public function getTags() { if (null === $this->tags) { - $tagsData = json_decode(file_get_contents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/tags'), true); + $tagsData = json_decode($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/tags'), true); $this->tags = array(); foreach ($tagsData as $tag) { $this->tags[$tag['name']] = $tag['commit']['sha']; @@ -121,7 +120,7 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface public function getBranches() { if (null === $this->branches) { - $branchData = json_decode(file_get_contents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/branches'), true); + $branchData = json_decode($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/branches'), true); $this->branches = array(); foreach ($branchData as $branch) { $this->branches[$branch['name']] = $branch['commit']['sha']; diff --git a/src/Composer/Repository/Vcs/HgBitbucketDriver.php b/src/Composer/Repository/Vcs/HgBitbucketDriver.php index 1105804f2..6d2e8b066 100644 --- a/src/Composer/Repository/Vcs/HgBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/HgBitbucketDriver.php @@ -13,11 +13,10 @@ namespace Composer\Repository\Vcs; use Composer\Json\JsonFile; -use Composer\Console\Helper\WrapperInterface; +use Composer\IO\IOInterface; /** * @author Per Bernhardt - * @author François Pluchino */ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface { @@ -28,13 +27,13 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface protected $rootIdentifier; protected $infoCache = array(); - public function __construct($url, WrapperInterface $wrapper) + public function __construct($url, IOInterface $io) { preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url, $match); $this->owner = $match[1]; $this->repository = $match[2]; - parent::__construct($url, $wrapper); + parent::__construct($url, $io); } /** @@ -50,7 +49,7 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getRootIdentifier() { if (null === $this->rootIdentifier) { - $repoData = json_decode(file_get_contents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); + $repoData = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); $this->rootIdentifier = $repoData['tip']['raw_node']; } @@ -92,7 +91,7 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getComposerInformation($identifier) { if (!isset($this->infoCache[$identifier])) { - $composer = @file_get_contents($this->getScheme() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/raw/'.$identifier.'/composer.json'); + $composer = $this->getContents($this->getScheme() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/raw/'.$identifier.'/composer.json'); if (!$composer) { throw new \UnexpectedValueException('Failed to retrieve composer information for identifier '.$identifier.' in '.$this->getUrl()); } @@ -100,7 +99,7 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface $composer = JsonFile::parseJson($composer); if (!isset($composer['time'])) { - $changeset = json_decode(file_get_contents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier), true); + $changeset = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier), true); $composer['time'] = $changeset['timestamp']; } $this->infoCache[$identifier] = $composer; @@ -115,7 +114,7 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getTags() { if (null === $this->tags) { - $tagsData = json_decode(file_get_contents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); + $tagsData = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); $this->tags = array(); foreach ($tagsData as $tag => $data) { $this->tags[$tag] = $data['raw_node']; @@ -131,7 +130,7 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getBranches() { if (null === $this->branches) { - $branchData = json_decode(file_get_contents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches'), true); + $branchData = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches'), true); $this->branches = array(); foreach ($branchData as $branch => $data) { $this->branches[$branch] = $data['raw_node']; diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index 928723a46..3d84dfc8e 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -13,11 +13,10 @@ namespace Composer\Repository\Vcs; use Composer\Json\JsonFile; -use Composer\Console\Helper\WrapperInterface; +use Composer\IO\IOInterface; /** * @author Per Bernhardt - * @author François Pluchino */ class HgDriver extends VcsDriver implements VcsDriverInterface { @@ -26,11 +25,11 @@ class HgDriver extends VcsDriver implements VcsDriverInterface protected $rootIdentifier; protected $infoCache = array(); - public function __construct($url, WrapperInterface $wrapper) + public function __construct($url, IOInterface $io) { $this->tmpDir = sys_get_temp_dir() . '/composer-' . preg_replace('{[^a-z0-9]}i', '-', $url) . '/'; - parent::__construct($url, $wrapper); + parent::__construct($url, $io); } /** diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 9d2941d0e..482799495 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -3,11 +3,10 @@ namespace Composer\Repository\Vcs; use Composer\Json\JsonFile; -use Composer\Console\Helper\WrapperInterface; +use Composer\IO\IOInterface; /** * @author Jordi Boggiano - * @author François Pluchino */ class SvnDriver extends VcsDriver implements VcsDriverInterface { @@ -16,9 +15,9 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface protected $branches; protected $infoCache = array(); - public function __construct($url, WrapperInterface $wrapper) + public function __construct($url, IOInterface $io) { - parent::__construct($this->baseUrl = rtrim($url, '/'), $wrapper); + parent::__construct($this->baseUrl = rtrim($url, '/'), $io); if (false !== ($pos = strrpos($url, '/trunk'))) { $this->baseUrl = substr($url, 0, $pos); diff --git a/src/Composer/Repository/Vcs/VcsDriver.php b/src/Composer/Repository/Vcs/VcsDriver.php index 3c0682f6e..9ad139aea 100644 --- a/src/Composer/Repository/Vcs/VcsDriver.php +++ b/src/Composer/Repository/Vcs/VcsDriver.php @@ -12,29 +12,28 @@ namespace Composer\Repository\Vcs; +use Composer\IO\IOInterface; + /** * A driver implementation for driver with authentification interaction. * * @author François Pluchino */ - -use Composer\Console\Helper\WrapperInterface; - abstract class VcsDriver { protected $url; - protected $wrapper; + protected $io; /** * Constructor. * - * @param string $url The URL - * @param WrapperInterface $wrapper The Wrapper instance + * @param string $url The URL + * @param IOInterface $io The IO instance */ - public function __construct($url, WrapperInterface $wrapper) + public function __construct($url, IOInterface $io) { $this->url = $url; - $this->wrapper = $wrapper; + $this->io = $io; } /** @@ -49,4 +48,78 @@ abstract class VcsDriver } return 'http'; } + + /** + * Get the remote content. + * + * @param string $url The URL of content + * + * @return mixed The result + */ + protected function getContents($url) + { + $auth = $this->io->getAuthentification($this->url); + + // curl options + $defaults = array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_BINARYTRANSFER => true, + CURLOPT_BUFFERSIZE => 64000, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_NOPROGRESS => true, + CURLOPT_URL => $url, + CURLOPT_HTTPGET => true, + CURLOPT_SSL_VERIFYPEER => false + ); + + // add authorization to curl options + if ($this->io->hasAuthentification($this->url)) { + $defaults[CURLOPT_USERPWD] = $auth['username'] . ':' . $auth['password']; + + } else if (null !== $this->io->getLastUsername()) { + $defaults[CURLOPT_USERPWD] = $this->io->getLastUsername() . ':' . $this->io->getLastPassword(); + } + + // init curl + $ch = curl_init(); + curl_setopt_array($ch, $defaults); + + // run curl + $curl_result = curl_exec($ch); + $curl_info = curl_getinfo($ch); + $curl_errorCode = curl_errno($ch); + $curl_error = curl_error($ch); + $code = $curl_info['http_code']; + $code = null ? 0 : $code; + + //close streams + curl_close($ch); + + // for private repository returning 404 error when the authentification is incorrect + $ps = 404 === $code && null === $this->io->getLastUsername() && null === $auth['username']; + + if (401 === $code || $ps) { + if (!$this->io->isInteractive()) { + $mess = "The '$url' URL not found"; + + if (401 === $code || $ps) { + $mess = "The '$url' URL required the authentification.\nYou must be used the interactive console"; + } + + throw new \LogicException($mess); + } + + $this->io->writeln("Authorization required for " . $this->owner.'/' . $this->repository . ":"); + $username = $this->io->ask(' Username: '); + $password = $this->io->askAndHideAnswer(' Password: '); + $this->io->setAuthentification($this->url, $username, $password); + + return $this->getContents($url); + + } else if (404 === $code) { + throw new \LogicException("The '$url' URL not found"); + } + + return $curl_result; + } } diff --git a/src/Composer/Repository/Vcs/VcsDriverInterface.php b/src/Composer/Repository/Vcs/VcsDriverInterface.php index 124530905..9e391b2c3 100644 --- a/src/Composer/Repository/Vcs/VcsDriverInterface.php +++ b/src/Composer/Repository/Vcs/VcsDriverInterface.php @@ -2,6 +2,8 @@ namespace Composer\Repository\Vcs; +use Composer\Console\Helper\Wrapper; + /** * @author Jordi Boggiano */ diff --git a/src/Composer/Repository/VcsRepository.php b/src/Composer/Repository/VcsRepository.php index 075a713b9..e33c9804c 100644 --- a/src/Composer/Repository/VcsRepository.php +++ b/src/Composer/Repository/VcsRepository.php @@ -5,20 +5,19 @@ namespace Composer\Repository; use Composer\Repository\Vcs\VcsDriverInterface; use Composer\Package\Version\VersionParser; use Composer\Package\Loader\ArrayLoader; -use Composer\Console\Helper\WrapperInterface; +use Composer\IO\IOInterface; /** * @author Jordi Boggiano - * @author François Pluchino */ class VcsRepository extends ArrayRepository { protected $url; protected $packageName; protected $debug; - protected $wrapper; + protected $io; - public function __construct(WrapperInterface $wrapper, array $config, array $drivers = null) + public function __construct(array $config, IOInterface $io, array $drivers = null) { if (!filter_var($config['url'], FILTER_VALIDATE_URL)) { throw new \UnexpectedValueException('Invalid url given for PEAR repository: '.$config['url']); @@ -34,7 +33,7 @@ class VcsRepository extends ArrayRepository ); $this->url = $config['url']; - $this->wrapper = $wrapper; + $this->io = $io; } public function setDebug($debug) @@ -46,7 +45,7 @@ class VcsRepository extends ArrayRepository { foreach ($this->drivers as $driver) { if ($driver::supports($this->url)) { - $driver = new $driver($this->url, $this->wrapper); + $driver = new $driver($this->url, $this->io); $driver->initialize(); return $driver; } @@ -54,7 +53,7 @@ class VcsRepository extends ArrayRepository foreach ($this->drivers as $driver) { if ($driver::supports($this->url, true)) { - $driver = new $driver($this->url, $this->wrapper); + $driver = new $driver($this->url, $this->io); $driver->initialize(); return $driver; }