فهرست منبع

Convert Wrapper class to ConsoleIO class

François Pluchino 13 سال پیش
والد
کامیت
a10f92aafe

+ 9 - 11
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);

+ 0 - 160
src/Composer/Console/Helper/Wrapper.php

@@ -1,160 +0,0 @@
-<?php
-
-/*
- * This file is part of Composer.
- *
- * (c) Nils Adermann <naderman@naderman.de>
- *     Jordi Boggiano <j.boggiano@seld.be>
- *
- * 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 <francois.pluchino@opendisplay.com>
- */
-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;
-        }
-    }
-}

+ 0 - 95
src/Composer/Console/Helper/WrapperInterface.php

@@ -1,95 +0,0 @@
-<?php
-
-/*
- * This file is part of Composer.
- *
- * (c) Nils Adermann <naderman@naderman.de>
- *     Jordi Boggiano <j.boggiano@seld.be>
- *
- * 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 <francois.pluchino@opendisplay.com>
- */
-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 = '');
-}

+ 13 - 13
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 <comment>" . $package->getName() . "</comment> (<info>" . $package->getPrettyVersion() . "</info>)");
+        $this->io->writeln("  - Package <info>" . $package->getName() . "</info> (<comment>" . $package->getPrettyVersion() . "</comment>)");
 
         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: <comment>connection...</comment>", 80);
         copy($url, $fileName, $ctx);
-
-        $this->wrapper->overwriteln("    Downloading: <comment>OK</comment>", 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: <comment>$progression%</comment>", 80);
+                        $this->io->overwrite("    Downloading: <comment>$progression%</comment>", 80);
                     }
                 }
 

+ 374 - 0
src/Composer/IO/ConsoleIO.php

@@ -0,0 +1,374 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * 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 <francois.pluchino@opendisplay.com>
+ */
+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;
+    }
+}

+ 143 - 0
src/Composer/IO/IOInterface.php

@@ -0,0 +1,143 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * 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 <francois.pluchino@opendisplay.com>
+ */
+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);
+}

+ 5 - 5
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);
     }
 
     /**

+ 8 - 9
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 <plb@webfactory.de>
- * @author François Pluchino <francois.pluchino@opendisplay.com>
  */
 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'];

+ 3 - 4
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 <j.boggiano@seld.be>
- * @author François Pluchino <francois.pluchino@opendisplay.com>
  */
 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);
     }
 
     /**

+ 8 - 9
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 <j.boggiano@seld.be>
- * @author François Pluchino <francois.pluchino@opendisplay.com>
  */
 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'];

+ 8 - 9
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 <plb@webfactory.de>
- * @author François Pluchino <francois.pluchino@opendisplay.com>
  */
 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'];

+ 3 - 4
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 <plb@webfactory.de>
- * @author François Pluchino <francois.pluchino@opendisplay.com>
  */
 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);
     }
 
     /**

+ 3 - 4
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 <j.boggiano@seld.be>
- * @author François Pluchino <francois.pluchino@opendisplay.com>
  */
 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);

+ 81 - 8
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 <francois.pluchino@opendisplay.com>
  */
-
-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 <info>" . $this->owner.'/' . $this->repository . "</info>:");
+            $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;
+    }
 }

+ 2 - 0
src/Composer/Repository/Vcs/VcsDriverInterface.php

@@ -2,6 +2,8 @@
 
 namespace Composer\Repository\Vcs;
 
+use Composer\Console\Helper\Wrapper;
+
 /**
  * @author Jordi Boggiano <j.boggiano@seld.be>
  */

+ 6 - 7
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 <j.boggiano@seld.be>
- * @author François Pluchino <francois.pluchino@opendisplay.com>
  */
 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;
             }