From fa793649fa165da920b3acb3e74aa4788c0065ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Tue, 10 Jan 2012 15:44:13 +0100 Subject: [PATCH 01/16] Add a VcsDriver abstract class. All XxxDriver extends this abstract class now. --- .../Repository/Vcs/GitBitbucketDriver.php | 23 +++++----- src/Composer/Repository/Vcs/GitHubDriver.php | 20 ++++---- .../Repository/Vcs/HgBitbucketDriver.php | 22 ++++----- src/Composer/Repository/Vcs/HgDriver.php | 10 ++-- src/Composer/Repository/Vcs/SvnDriver.php | 6 +-- src/Composer/Repository/Vcs/VcsDriver.php | 46 +++++++++++++++++++ 6 files changed, 88 insertions(+), 39 deletions(-) create mode 100644 src/Composer/Repository/Vcs/VcsDriver.php diff --git a/src/Composer/Repository/Vcs/GitBitbucketDriver.php b/src/Composer/Repository/Vcs/GitBitbucketDriver.php index 0578a8e85..1255704c2 100644 --- a/src/Composer/Repository/Vcs/GitBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/GitBitbucketDriver.php @@ -17,9 +17,9 @@ use Composer\Json\JsonFile; /** * @author Per Bernhardt */ -class GitBitbucketDriver implements VcsDriverInterface +class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface { - protected $url; + //protected $url; protected $owner; protected $repository; protected $tags; @@ -29,10 +29,11 @@ class GitBitbucketDriver implements VcsDriverInterface public function __construct($url) { - $this->url = $url; - preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url, $match); + preg_match('#^(?:https?|http)://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url, $match); $this->owner = $match[1]; $this->repository = $match[2]; + + parent::__construct($url); } /** @@ -48,7 +49,7 @@ class GitBitbucketDriver implements VcsDriverInterface public function getRootIdentifier() { if (null === $this->rootIdentifier) { - $repoData = json_decode(file_get_contents('https://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository), true); + $repoData = json_decode(file_get_contents($this->getHttpSupport() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository), true); $this->rootIdentifier = !empty($repoData['main_branch']) ? $repoData['main_branch'] : 'master'; } @@ -79,7 +80,7 @@ class GitBitbucketDriver implements VcsDriverInterface public function getDist($identifier) { $label = array_search($identifier, $this->getTags()) ?: $identifier; - $url = 'https://bitbucket.org/'.$this->owner.'/'.$this->repository.'/get/'.$label.'.zip'; + $url = $this->getHttpSupport() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/get/'.$label.'.zip'; return array('type' => 'zip', 'url' => $url, 'reference' => $label, 'shasum' => ''); } @@ -90,7 +91,7 @@ class GitBitbucketDriver implements VcsDriverInterface public function getComposerInformation($identifier) { if (!isset($this->infoCache[$identifier])) { - $composer = @file_get_contents('https://bitbucket.org/'.$this->owner.'/'.$this->repository.'/raw/'.$identifier.'/composer.json'); + $composer = @file_get_contents($this->getHttpSupport() . '://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()); } @@ -98,7 +99,7 @@ class GitBitbucketDriver implements VcsDriverInterface $composer = JsonFile::parseJson($composer); if (!isset($composer['time'])) { - $changeset = json_decode(file_get_contents('https://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier), true); + $changeset = json_decode(file_get_contents($this->getHttpSupport() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier), true); $composer['time'] = $changeset['timestamp']; } $this->infoCache[$identifier] = $composer; @@ -113,7 +114,7 @@ class GitBitbucketDriver implements VcsDriverInterface public function getTags() { if (null === $this->tags) { - $tagsData = json_decode(file_get_contents('https://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); + $tagsData = json_decode(file_get_contents($this->getHttpSupport() . '://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']; @@ -129,7 +130,7 @@ class GitBitbucketDriver implements VcsDriverInterface public function getBranches() { if (null === $this->branches) { - $branchData = json_decode(file_get_contents('https://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches'), true); + $branchData = json_decode(file_get_contents($this->getHttpSupport() . '://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']; @@ -158,6 +159,6 @@ class GitBitbucketDriver implements VcsDriverInterface */ public static function supports($url, $deep = false) { - return preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url, $match); + return preg_match('#^(?:https?|http)://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url, $match); } } diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 44227ef91..2168154cf 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -7,7 +7,7 @@ use Composer\Json\JsonFile; /** * @author Jordi Boggiano */ -class GitHubDriver implements VcsDriverInterface +class GitHubDriver extends VcsDriver implements VcsDriverInterface { protected $owner; protected $repository; @@ -18,9 +18,11 @@ class GitHubDriver implements VcsDriverInterface public function __construct($url) { - preg_match('#^(?:https?|git)://github\.com/([^/]+)/(.+?)(?:\.git)?$#', $url, $match); + preg_match('#^(?:https?|http|git)://github\.com/([^/]+)/(.+?)(?:\.git)?$#', $url, $match); $this->owner = $match[1]; $this->repository = $match[2]; + + parent::__construct($url); } /** @@ -36,7 +38,7 @@ class GitHubDriver implements VcsDriverInterface public function getRootIdentifier() { if (null === $this->rootIdentifier) { - $repoData = json_decode(file_get_contents('https://api.github.com/repos/'.$this->owner.'/'.$this->repository), true); + $repoData = json_decode(file_get_contents($this->getHttpSupport() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository), true); $this->rootIdentifier = $repoData['master_branch'] ?: 'master'; } @@ -48,7 +50,7 @@ class GitHubDriver implements VcsDriverInterface */ public function getUrl() { - return 'http://github.com/'.$this->owner.'/'.$this->repository.'.git'; + return $this->url; } /** @@ -67,7 +69,7 @@ class GitHubDriver implements VcsDriverInterface public function getDist($identifier) { $label = array_search($identifier, $this->getTags()) ?: $identifier; - $url = 'http://github.com/'.$this->owner.'/'.$this->repository.'/zipball/'.$label; + $url = $this->getHttpSupport() . '://github.com/'.$this->owner.'/'.$this->repository.'/zipball/'.$label; return array('type' => 'zip', 'url' => $url, 'reference' => $label, 'shasum' => ''); } @@ -78,7 +80,7 @@ class GitHubDriver implements VcsDriverInterface public function getComposerInformation($identifier) { if (!isset($this->infoCache[$identifier])) { - $composer = @file_get_contents('https://raw.github.com/'.$this->owner.'/'.$this->repository.'/'.$identifier.'/composer.json'); + $composer = @file_get_contents($this->getHttpSupport() . '://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()); } @@ -86,7 +88,7 @@ class GitHubDriver implements VcsDriverInterface $composer = JsonFile::parseJson($composer); if (!isset($composer['time'])) { - $commit = json_decode(file_get_contents('https://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/commits/'.$identifier), true); + $commit = json_decode(file_get_contents($this->getHttpSupport() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/commits/'.$identifier), true); $composer['time'] = $commit['commit']['committer']['date']; } $this->infoCache[$identifier] = $composer; @@ -101,7 +103,7 @@ class GitHubDriver implements VcsDriverInterface public function getTags() { if (null === $this->tags) { - $tagsData = json_decode(file_get_contents('https://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/tags'), true); + $tagsData = json_decode(file_get_contents($this->getHttpSupport() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/tags'), true); $this->tags = array(); foreach ($tagsData as $tag) { $this->tags[$tag['name']] = $tag['commit']['sha']; @@ -117,7 +119,7 @@ class GitHubDriver implements VcsDriverInterface public function getBranches() { if (null === $this->branches) { - $branchData = json_decode(file_get_contents('https://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/branches'), true); + $branchData = json_decode(file_get_contents($this->getHttpSupport() . '://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 515e90d36..721f7fd52 100644 --- a/src/Composer/Repository/Vcs/HgBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/HgBitbucketDriver.php @@ -17,9 +17,8 @@ use Composer\Json\JsonFile; /** * @author Per Bernhardt */ -class HgBitbucketDriver implements VcsDriverInterface +class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface { - protected $url; protected $owner; protected $repository; protected $tags; @@ -29,10 +28,11 @@ class HgBitbucketDriver implements VcsDriverInterface public function __construct($url) { - $this->url = $url; - preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url, $match); + preg_match('#^(?:https?|http)://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url, $match); $this->owner = $match[1]; $this->repository = $match[2]; + + parent::__construct($url); } /** @@ -48,7 +48,7 @@ class HgBitbucketDriver implements VcsDriverInterface public function getRootIdentifier() { if (null === $this->rootIdentifier) { - $repoData = json_decode(file_get_contents('https://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); + $repoData = json_decode(file_get_contents($this->getHttpSupport() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); $this->rootIdentifier = $repoData['tip']['raw_node']; } @@ -79,7 +79,7 @@ class HgBitbucketDriver implements VcsDriverInterface public function getDist($identifier) { $label = array_search($identifier, $this->getTags()) ?: $identifier; - $url = 'https://bitbucket.org/'.$this->owner.'/'.$this->repository.'/get/'.$label.'.zip'; + $url = $this->getHttpSupport() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/get/'.$label.'.zip'; return array('type' => 'zip', 'url' => $url, 'reference' => $label, 'shasum' => ''); } @@ -90,7 +90,7 @@ class HgBitbucketDriver implements VcsDriverInterface public function getComposerInformation($identifier) { if (!isset($this->infoCache[$identifier])) { - $composer = @file_get_contents('https://bitbucket.org/'.$this->owner.'/'.$this->repository.'/raw/'.$identifier.'/composer.json'); + $composer = @file_get_contents($this->getHttpSupport() . '://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()); } @@ -98,7 +98,7 @@ class HgBitbucketDriver implements VcsDriverInterface $composer = JsonFile::parseJson($composer); if (!isset($composer['time'])) { - $changeset = json_decode(file_get_contents('https://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier), true); + $changeset = json_decode(file_get_contents($this->getHttpSupport() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier), true); $composer['time'] = $changeset['timestamp']; } $this->infoCache[$identifier] = $composer; @@ -113,7 +113,7 @@ class HgBitbucketDriver implements VcsDriverInterface public function getTags() { if (null === $this->tags) { - $tagsData = json_decode(file_get_contents('https://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); + $tagsData = json_decode(file_get_contents($this->getHttpSupport() . '://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']; @@ -129,7 +129,7 @@ class HgBitbucketDriver implements VcsDriverInterface public function getBranches() { if (null === $this->branches) { - $branchData = json_decode(file_get_contents('https://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches'), true); + $branchData = json_decode(file_get_contents($this->getHttpSupport() . '://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']; @@ -158,6 +158,6 @@ class HgBitbucketDriver implements VcsDriverInterface */ public static function supports($url, $deep = false) { - return preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url, $match); + return preg_match('#^(?:https?|http)://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url, $match); } } diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index c6f64cced..b3057c398 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -17,9 +17,8 @@ use Composer\Json\JsonFile; /** * @author Per Bernhardt */ -class HgDriver implements VcsDriverInterface +class HgDriver extends VcsDriver implements VcsDriverInterface { - protected $url; protected $tags; protected $branches; protected $rootIdentifier; @@ -27,8 +26,9 @@ class HgDriver implements VcsDriverInterface public function __construct($url) { - $this->url = $url; $this->tmpDir = sys_get_temp_dir() . '/composer-' . preg_replace('{[^a-z0-9]}i', '-', $url) . '/'; + + parent::__construct($url); } /** @@ -58,7 +58,7 @@ class HgDriver implements VcsDriverInterface exec(sprintf('cd %s && hg tip --template "{node}"', $tmpDir), $output); $this->rootIdentifier = $output[0]; } - + return $this->rootIdentifier; } @@ -122,7 +122,7 @@ class HgDriver implements VcsDriverInterface { if (null === $this->tags) { $tags = array(); - + exec(sprintf('cd %s && hg tags', escapeshellarg($this->tmpDir)), $output); foreach ($output as $tag) { if (preg_match('(^([^\s]+)\s+\d+:(.*)$)', $tag, $match)) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index c968dfab7..830382385 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -7,9 +7,8 @@ use Composer\Json\JsonFile; /** * @author Jordi Boggiano */ -class SvnDriver implements VcsDriverInterface +class SvnDriver extends VcsDriver implements VcsDriverInterface { - protected $url; protected $baseUrl; protected $tags; protected $branches; @@ -17,7 +16,8 @@ class SvnDriver implements VcsDriverInterface public function __construct($url) { - $this->url = $this->baseUrl = rtrim($url, '/'); + parent::__construct($this->baseUrl = rtrim($url, '/')); + 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 new file mode 100644 index 000000000..0b2dc824b --- /dev/null +++ b/src/Composer/Repository/Vcs/VcsDriver.php @@ -0,0 +1,46 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository\Vcs; + +/** + * A driver implementation + * + * @author François Pluchino + */ +abstract class VcsDriver +{ + protected $url; + + /** + * Constructor + * + * @param string $url The URL + */ + public function __construct($url) + { + $this->url = $url; + } + + /** + * Get the https or http protocol. + * + * @return string The correct type of protocol + */ + protected function getHttpSupport() + { + if (extension_loaded('openssl')) { + return 'https'; + } + return 'http'; + } +} From 013d145bcdf51e1aa5c6994e22fec5a0caf7f107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Tue, 10 Jan 2012 18:50:16 +0100 Subject: [PATCH 02/16] Add InputInterface and OutputInterface --- src/Composer/Console/Application.php | 18 ++++++++++++------ src/Composer/Downloader/FileDownloader.php | 17 +++++++++++++++++ src/Composer/Repository/ComposerRepository.php | 10 +++++++++- src/Composer/Repository/PackageRepository.php | 13 +++++++++++-- src/Composer/Repository/PearRepository.php | 9 ++++++++- src/Composer/Repository/RepositoryManager.php | 14 +++++++++++++- .../Repository/Vcs/GitBitbucketDriver.php | 7 +++++-- src/Composer/Repository/Vcs/GitDriver.php | 11 +++++++---- src/Composer/Repository/Vcs/GitHubDriver.php | 7 +++++-- .../Repository/Vcs/HgBitbucketDriver.php | 7 +++++-- src/Composer/Repository/Vcs/HgDriver.php | 7 +++++-- src/Composer/Repository/Vcs/SvnDriver.php | 7 +++++-- src/Composer/Repository/Vcs/VcsDriver.php | 14 ++++++++++++-- src/Composer/Repository/VcsRepository.php | 13 ++++++++++--- 14 files changed, 124 insertions(+), 30 deletions(-) diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index af68185be..0d24dfe7c 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -32,10 +32,13 @@ use Composer\Json\JsonFile; * * @author Ryan Weaver * @author Jordi Boggiano + * @author François Pluchino */ class Application extends BaseApplication { protected $composer; + protected $input; + protected $output; public function __construct() { @@ -64,6 +67,9 @@ class Application extends BaseApplication { $this->registerCommands(); + $this->input = $input; + $this->output = $output; + return parent::doRun($input, $output); } @@ -73,7 +79,7 @@ class Application extends BaseApplication public function getComposer() { if (null === $this->composer) { - $this->composer = self::bootstrapComposer(); + $this->composer = self::bootstrapComposer(null, $this->input, $this->output); } return $this->composer; @@ -84,7 +90,7 @@ class Application extends BaseApplication * * @return Composer */ - public static function bootstrapComposer($composerFile = null) + public static function bootstrapComposer($composerFile = null, InputInterface $input = null, OutputInterface $output = null) { // load Composer configuration if (null === $composerFile) { @@ -122,7 +128,7 @@ class Application extends BaseApplication $binDir = getenv('COMPOSER_BIN_DIR') ?: $packageConfig['config']['bin-dir']; // initialize repository manager - $rm = new Repository\RepositoryManager(); + $rm = new Repository\RepositoryManager($input, $output); $rm->setLocalRepository(new Repository\FilesystemRepository(new JsonFile($vendorDir.'/.composer/installed.json'))); $rm->setRepositoryClass('composer', 'Composer\Repository\ComposerRepository'); $rm->setRepositoryClass('vcs', 'Composer\Repository\VcsRepository'); @@ -134,8 +140,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()); - $dm->setDownloader('zip', new Downloader\ZipDownloader()); + $dm->setDownloader('pear', new Downloader\PearDownloader($input, $output)); + $dm->setDownloader('zip', new Downloader\ZipDownloader($input, $output)); // initialize installation manager $im = new Installer\InstallationManager($vendorDir); @@ -148,7 +154,7 @@ class Application extends BaseApplication // load default repository unless it's explicitly disabled if (!isset($packageConfig['repositories']['packagist']) || $packageConfig['repositories']['packagist'] !== false) { - $rm->addRepository(new Repository\ComposerRepository(array('url' => 'http://packagist.org'))); + $rm->addRepository(new Repository\ComposerRepository($input, $output, array('url' => 'http://packagist.org'))); } // init locker diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index aeb655be4..08769ff60 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -12,6 +12,8 @@ namespace Composer\Downloader; use Composer\Package\PackageInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; /** * Base downloader for file packages @@ -21,6 +23,21 @@ use Composer\Package\PackageInterface; */ abstract class FileDownloader implements DownloaderInterface { + protected $intput; + protected $output; + + /** + * Constructor. + * + * @param InputInterface $input The Input instance + * @param OutputInterface $output The Output instance + */ + public function __construct(InputInterface $input, OutputInterface $output) + { + $this->intput = $input; + $this->output = $output; + } + /** * {@inheritDoc} */ diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index fadfd6a7c..8a78af773 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -15,17 +15,25 @@ namespace Composer\Repository; use Composer\Package\Loader\ArrayLoader; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Json\JsonFile; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; /** * @author Jordi Boggiano + * @author François Pluchino */ class ComposerRepository extends ArrayRepository { protected $url; protected $packages; + protected $input; + protected $output; - public function __construct(array $config) + public function __construct(InputInterface $input, OutputInterface $output, array $config) { + $this->input = $input; + $this->output = $output; + if (!preg_match('{^\w+://}', $config['url'])) { // assume http as the default protocol $config['url'] = 'http://'.$config['url']; diff --git a/src/Composer/Repository/PackageRepository.php b/src/Composer/Repository/PackageRepository.php index 01b1b24c2..113e8c0b7 100644 --- a/src/Composer/Repository/PackageRepository.php +++ b/src/Composer/Repository/PackageRepository.php @@ -16,24 +16,33 @@ use Composer\Json\JsonFile; use Composer\Package\PackageInterface; use Composer\Package\Loader\ArrayLoader; use Composer\Package\Dumper\ArrayDumper; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; /** * Package repository. * * @author Jordi Boggiano + * @author François Pluchino */ class PackageRepository extends ArrayRepository { private $config; + private $input; + private $output; /** * Initializes filesystem repository. * - * @param array $config package definition + * @param InputInterface $input The Input instance + * @param OutputInterface $output The Output instance + * @param array $config package definition */ - public function __construct(array $config) + public function __construct(InputInterface $input, OutputInterface $output, array $config) { $this->config = $config; + $this->input = $input; + $this->output = $output; } /** diff --git a/src/Composer/Repository/PearRepository.php b/src/Composer/Repository/PearRepository.php index b19620b3a..ae26e5579 100644 --- a/src/Composer/Repository/PearRepository.php +++ b/src/Composer/Repository/PearRepository.php @@ -13,16 +13,21 @@ namespace Composer\Repository; use Composer\Package\Loader\ArrayLoader; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; /** * @author Benjamin Eberlei * @author Jordi Boggiano + * @author François Pluchino */ class PearRepository extends ArrayRepository { protected $url; + private $input; + private $output; - public function __construct(array $config) + public function __construct(InputInterface $input, OutputInterface $output, array $config) { if (!preg_match('{^https?://}', $config['url'])) { $config['url'] = 'http://'.$config['url']; @@ -32,6 +37,8 @@ class PearRepository extends ArrayRepository } $this->url = $config['url']; + $this->input = $input; + $this->output = $output; } protected function initialize() diff --git a/src/Composer/Repository/RepositoryManager.php b/src/Composer/Repository/RepositoryManager.php index a73934d3f..1157ae493 100644 --- a/src/Composer/Repository/RepositoryManager.php +++ b/src/Composer/Repository/RepositoryManager.php @@ -12,17 +12,29 @@ namespace Composer\Repository; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; + /** * Repositories manager. * * @author Jordi Boggiano * @author Konstantin Kudryashov + * @author François Pluchino */ class RepositoryManager { private $localRepository; private $repositories = array(); private $repositoryClasses = array(); + private $input; + private $output; + + public function __construct(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } /** * Searches for a package by it's name and version in managed repositories. @@ -66,7 +78,7 @@ class RepositoryManager } $class = $this->repositoryClasses[$type]; - return new $class($config); + return new $class($this->input, $this->output, $config); } /** diff --git a/src/Composer/Repository/Vcs/GitBitbucketDriver.php b/src/Composer/Repository/Vcs/GitBitbucketDriver.php index 1255704c2..75bf2d7b2 100644 --- a/src/Composer/Repository/Vcs/GitBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/GitBitbucketDriver.php @@ -13,9 +13,12 @@ namespace Composer\Repository\Vcs; use Composer\Json\JsonFile; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; /** * @author Per Bernhardt + * @author François Pluchino */ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface { @@ -27,13 +30,13 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface protected $rootIdentifier; protected $infoCache = array(); - public function __construct($url) + public function __construct($url, InputInterface $input, OutputInterface $output) { preg_match('#^(?:https?|http)://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url, $match); $this->owner = $match[1]; $this->repository = $match[2]; - parent::__construct($url); + parent::__construct($url, $input, $output); } /** diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index 1da392667..09afc0c8e 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -3,22 +3,25 @@ namespace Composer\Repository\Vcs; use Composer\Json\JsonFile; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; /** * @author Jordi Boggiano + * @author François Pluchino */ -class GitDriver implements VcsDriverInterface +class GitDriver extends VcsDriver implements VcsDriverInterface { - protected $url; protected $tags; protected $branches; protected $rootIdentifier; protected $infoCache = array(); - public function __construct($url) + public function __construct($url, InputInterface $input, OutputInterface $output) { - $this->url = $url; $this->tmpDir = sys_get_temp_dir() . '/composer-' . preg_replace('{[^a-z0-9]}i', '-', $url) . '/'; + + parent::__construct($url, $input, $output); } /** diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 2168154cf..e7c0efbb7 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -3,9 +3,12 @@ namespace Composer\Repository\Vcs; use Composer\Json\JsonFile; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; /** * @author Jordi Boggiano + * @author François Pluchino */ class GitHubDriver extends VcsDriver implements VcsDriverInterface { @@ -16,13 +19,13 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface protected $rootIdentifier; protected $infoCache = array(); - public function __construct($url) + public function __construct($url, InputInterface $input, OutputInterface $output) { preg_match('#^(?:https?|http|git)://github\.com/([^/]+)/(.+?)(?:\.git)?$#', $url, $match); $this->owner = $match[1]; $this->repository = $match[2]; - parent::__construct($url); + parent::__construct($url, $input, $output); } /** diff --git a/src/Composer/Repository/Vcs/HgBitbucketDriver.php b/src/Composer/Repository/Vcs/HgBitbucketDriver.php index 721f7fd52..0c3b59ced 100644 --- a/src/Composer/Repository/Vcs/HgBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/HgBitbucketDriver.php @@ -13,9 +13,12 @@ namespace Composer\Repository\Vcs; use Composer\Json\JsonFile; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; /** * @author Per Bernhardt + * @author François Pluchino */ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface { @@ -26,13 +29,13 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface protected $rootIdentifier; protected $infoCache = array(); - public function __construct($url) + public function __construct($url, InputInterface $input, OutputInterface $output) { preg_match('#^(?:https?|http)://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url, $match); $this->owner = $match[1]; $this->repository = $match[2]; - parent::__construct($url); + parent::__construct($url, $input, $output); } /** diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index b3057c398..366cd7b88 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -13,9 +13,12 @@ namespace Composer\Repository\Vcs; use Composer\Json\JsonFile; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; /** * @author Per Bernhardt + * @author François Pluchino */ class HgDriver extends VcsDriver implements VcsDriverInterface { @@ -24,11 +27,11 @@ class HgDriver extends VcsDriver implements VcsDriverInterface protected $rootIdentifier; protected $infoCache = array(); - public function __construct($url) + public function __construct($url, InputInterface $input, OutputInterface $output) { $this->tmpDir = sys_get_temp_dir() . '/composer-' . preg_replace('{[^a-z0-9]}i', '-', $url) . '/'; - parent::__construct($url); + parent::__construct($url, $input, $output); } /** diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 830382385..f6a27feac 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -3,9 +3,12 @@ namespace Composer\Repository\Vcs; use Composer\Json\JsonFile; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; /** * @author Jordi Boggiano + * @author François Pluchino */ class SvnDriver extends VcsDriver implements VcsDriverInterface { @@ -14,9 +17,9 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface protected $branches; protected $infoCache = array(); - public function __construct($url) + public function __construct($url, InputInterface $input, OutputInterface $output) { - parent::__construct($this->baseUrl = rtrim($url, '/')); + parent::__construct($this->baseUrl = rtrim($url, '/'), $input, $output); 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 0b2dc824b..703f7b16c 100644 --- a/src/Composer/Repository/Vcs/VcsDriver.php +++ b/src/Composer/Repository/Vcs/VcsDriver.php @@ -17,18 +17,28 @@ namespace Composer\Repository\Vcs; * * @author François Pluchino */ + +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; + abstract class VcsDriver { protected $url; + protected $input; + protected $output; /** * Constructor * - * @param string $url The URL + * @param string $url The URL + * @param InputInterface $input The Input instance + * @param OutputInterface $output The output instance */ - public function __construct($url) + public function __construct($url, InputInterface $input, OutputInterface $output) { $this->url = $url; + $this->input = $input; + $this->output = $output; } /** diff --git a/src/Composer/Repository/VcsRepository.php b/src/Composer/Repository/VcsRepository.php index 9b66963bc..52802c5a5 100644 --- a/src/Composer/Repository/VcsRepository.php +++ b/src/Composer/Repository/VcsRepository.php @@ -5,17 +5,22 @@ namespace Composer\Repository; use Composer\Repository\Vcs\VcsDriverInterface; use Composer\Package\Version\VersionParser; use Composer\Package\Loader\ArrayLoader; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; /** * @author Jordi Boggiano + * @author François Pluchino */ class VcsRepository extends ArrayRepository { protected $url; protected $packageName; - protected $debug; + protected $debug; + protected $input; + protected $output; - public function __construct(array $config, array $drivers = null) + public function __construct(InputInterface $input, OutputInterface $output, array $config, array $drivers = null) { if (!filter_var($config['url'], FILTER_VALIDATE_URL)) { throw new \UnexpectedValueException('Invalid url given for PEAR repository: '.$config['url']); @@ -31,6 +36,8 @@ class VcsRepository extends ArrayRepository ); $this->url = $config['url']; + $this->input = $input; + $this->output = $output; } public function setDebug($debug) @@ -42,7 +49,7 @@ class VcsRepository extends ArrayRepository { foreach ($this->drivers as $driver) { if ($driver::supports($this->url)) { - $driver = new $driver($this->url); + $driver = new $driver($this->url, $this->input, $this->output); $driver->initialize(); return $driver; } From 2f0162053ddff8a216e947a593e7fbcac0c2cc5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Tue, 10 Jan 2012 20:06:15 +0100 Subject: [PATCH 03/16] Add OutputConsole and format the download output --- src/Composer/Console/Application.php | 2 +- src/Composer/Console/Output/ConsoleOutput.php | 104 ++++++++++++++++++ src/Composer/Downloader/FileDownloader.php | 8 +- 3 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 src/Composer/Console/Output/ConsoleOutput.php diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 0d24dfe7c..368af21a0 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -15,10 +15,10 @@ namespace Composer\Console; use Symfony\Component\Console\Application as BaseApplication; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -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\Output\ConsoleOutput; use Composer\Command; use Composer\Composer; use Composer\Installer; diff --git a/src/Composer/Console/Output/ConsoleOutput.php b/src/Composer/Console/Output/ConsoleOutput.php new file mode 100644 index 000000000..0a8a8bd03 --- /dev/null +++ b/src/Composer/Console/Output/ConsoleOutput.php @@ -0,0 +1,104 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Console\Output; + +use Symfony\Component\Console\Output\ConsoleOutput as BaseConsoleOutput; + +/** + * ConsoleOutput is the default class for all CLI output. + * + * @author François Pluchino + */ +class ConsoleOutput extends BaseConsoleOutput +{ + /** + * 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) + { + for ($place = $size; $place > 0; $place--) { + $this->write("\x08"); + } + + $this->write($messages, false, $type); + + for ($place = ($size - strlen($line)); $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(''); + } + } + + /** + * 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) + { + $this->write($messages, $size, true, $type); + } + + /** + * Interactively prompts for input without echoing to the terminal. + * Requires a bash shell or Windows and won't work with safe_mode + * settings (Uses `shell_exec`). + * + * @param string $title The title of prompt (only for windows) + * + * @return string The value + */ + public function promptSilent($title = '') + { + 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->writeln(''); + + return $value; + + } else { + $command = "/usr/bin/env bash -c 'echo OK'"; + + if (rtrim(shell_exec($command)) !== 'OK') { + trigger_error("Can't invoke bash"); + return; + } + + $command = "/usr/bin/env bash -c 'read -s mypassword && echo \$mypassword'"; + $value = rtrim(shell_exec($command)); + $this->writeln(''); + + return $value; + } + } +} diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 08769ff60..dc4769408 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -20,6 +20,7 @@ use Symfony\Component\Console\Input\InputInterface; * * @author Kirill chEbba Chebunin * @author Jordi Boggiano + * @author François Pluchino */ abstract class FileDownloader implements DownloaderInterface { @@ -65,7 +66,8 @@ abstract class FileDownloader implements DownloaderInterface $fileName = rtrim($path.'/'.md5(time().rand()).'.'.pathinfo($url, PATHINFO_EXTENSION), '.'); - echo 'Downloading '.$url.' to '.$fileName.PHP_EOL; + //echo 'Downloading '.$url.' to '.$fileName.PHP_EOL; + $this->output->writeln(" - Downloading " . $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 @@ -106,11 +108,11 @@ abstract class FileDownloader implements DownloaderInterface throw new \UnexpectedValueException('The checksum verification of the archive failed (downloaded from '.$url.')'); } - echo 'Unpacking archive'.PHP_EOL; + $this->output->writeln(' Unpacking archive'); $this->extract($fileName, $path); - echo 'Cleaning up'.PHP_EOL; + $this->output->writeln(' Cleaning up'); unlink($fileName); // If we have only a one dir inside it suppose to be a package itself From 395e2e040d112ae8be06e7fa814840829676a5ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Tue, 10 Jan 2012 21:22:52 +0100 Subject: [PATCH 04/16] Add callback download informations --- src/Composer/Console/Output/ConsoleOutput.php | 2 +- src/Composer/Downloader/FileDownloader.php | 76 +++++++++++++++++-- 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/src/Composer/Console/Output/ConsoleOutput.php b/src/Composer/Console/Output/ConsoleOutput.php index 0a8a8bd03..a301dd504 100644 --- a/src/Composer/Console/Output/ConsoleOutput.php +++ b/src/Composer/Console/Output/ConsoleOutput.php @@ -37,7 +37,7 @@ class ConsoleOutput extends BaseConsoleOutput $this->write($messages, false, $type); - for ($place = ($size - strlen($line)); $place > 0; $place--) { + for ($place = ($size - strlen($messages)); $place > 0; $place--) { $this->write(' '); } diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index dc4769408..d7f9575ad 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -26,6 +26,7 @@ abstract class FileDownloader implements DownloaderInterface { protected $intput; protected $output; + protected $bytesMax; /** * Constructor. @@ -52,6 +53,9 @@ abstract class FileDownloader implements DownloaderInterface */ public function download(PackageInterface $package, $path) { + // init the progress bar + $this->bytesMax = 0; + $url = $package->getDistUrl(); $checksum = $package->getDistSha1Checksum(); @@ -67,7 +71,7 @@ abstract class FileDownloader implements DownloaderInterface $fileName = rtrim($path.'/'.md5(time().rand()).'.'.pathinfo($url, PATHINFO_EXTENSION), '.'); //echo 'Downloading '.$url.' to '.$fileName.PHP_EOL; - $this->output->writeln(" - Downloading " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); + $this->output->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 @@ -79,6 +83,8 @@ abstract class FileDownloader implements DownloaderInterface } // Handle system proxy + $ctx = stream_context_create(); + if (isset($_SERVER['HTTP_PROXY'])) { // http(s):// is not supported in proxy $proxy = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $_SERVER['HTTP_PROXY']); @@ -93,12 +99,14 @@ abstract class FileDownloader implements DownloaderInterface 'request_fulluri' => true, ), )); - - copy($url, $filename, $ctx); - } else { - copy($url, $fileName); } + stream_context_set_params($ctx, array("notification" => array($this, 'callbackDownload'))); + + copy($url, $fileName, $ctx); + + $this->output->overwrite('', 80); + if (!file_exists($fileName)) { throw new \UnexpectedValueException($url.' could not be saved to '.$fileName.', make sure the' .' directory is writable and you have internet connectivity'); @@ -108,11 +116,11 @@ abstract class FileDownloader implements DownloaderInterface throw new \UnexpectedValueException('The checksum verification of the archive failed (downloaded from '.$url.')'); } - $this->output->writeln(' Unpacking archive'); + $this->output->overwrite(' Unpacking archive'); $this->extract($fileName, $path); - $this->output->writeln(' Cleaning up'); + $this->output->overwrite(' Cleaning up'); unlink($fileName); // If we have only a one dir inside it suppose to be a package itself @@ -126,6 +134,9 @@ abstract class FileDownloader implements DownloaderInterface } rmdir($contentDir); } + + $this->output->overwrite(''); + $this->output->writeln(''); } /** @@ -147,6 +158,57 @@ abstract class FileDownloader implements DownloaderInterface $fs->removeDirectory($path); } + /** + * Download notification action. + * + * @param integer $notificationCode The notification code + * @param integer $severity The severity level + * @param string $message The message + * @param integer $messageCode The message code + * @param integer $bytesTransferred The loaded size + * @param integer $bytesMax The total size + */ + protected function callbackDownload($notificationCode, $severity, $message, $messageCode, $bytesTransferred, $bytesMax) + { + switch ($notificationCode) { + case STREAM_NOTIFY_AUTH_REQUIRED: + break; + + case STREAM_NOTIFY_FILE_SIZE_IS: + if ($this->bytesMax < $bytesMax) { + $this->bytesMax = $bytesMax; + } + + break; + + case STREAM_NOTIFY_PROGRESS: + if ($this->bytesMax > 0) { + $progression = 0; + + if ($this->bytesMax > 0) { + $progression = ($bytesTransferred / $this->bytesMax * 100); + } + + $levels = array(0, 5, 10, 15, 20, 25, 30, 35, 40, 35, 50, 55, 60, + 65, 70, 75, 80, 85, 90, 95, 100); + + $progression = round($progression, 0); + + if (in_array($progression, $levels)) { + $this->output->overwrite(" Downloading: $progression%", 80); + } + } + + break; + + case STREAM_NOTIFY_AUTH_RESULT: + break; + + default: + break; + } + } + /** * Extract file to directory * From 9c7e7a2c4aa203121d7383aa9650e70e1241c98b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Tue, 10 Jan 2012 21:29:41 +0100 Subject: [PATCH 05/16] Remove comment --- src/Composer/Downloader/FileDownloader.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index d7f9575ad..a87478f3e 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -70,7 +70,6 @@ abstract class FileDownloader implements DownloaderInterface $fileName = rtrim($path.'/'.md5(time().rand()).'.'.pathinfo($url, PATHINFO_EXTENSION), '.'); - //echo 'Downloading '.$url.' to '.$fileName.PHP_EOL; $this->output->writeln(" - Package " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); if (!extension_loaded('openssl') && (0 === strpos($url, 'https:') || 0 === strpos($url, 'http://github.com'))) { From 9b0e50cdc2586476fce03d3f9edc12e3ebc6c4e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Wed, 11 Jan 2012 01:09:30 +0100 Subject: [PATCH 06/16] Restore the old regex --- src/Composer/Repository/Vcs/GitBitbucketDriver.php | 5 ++--- src/Composer/Repository/Vcs/GitHubDriver.php | 2 +- src/Composer/Repository/Vcs/HgBitbucketDriver.php | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitBitbucketDriver.php b/src/Composer/Repository/Vcs/GitBitbucketDriver.php index 75bf2d7b2..31e744b5e 100644 --- a/src/Composer/Repository/Vcs/GitBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/GitBitbucketDriver.php @@ -22,7 +22,6 @@ use Symfony\Component\Console\Input\InputInterface; */ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface { - //protected $url; protected $owner; protected $repository; protected $tags; @@ -32,7 +31,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function __construct($url, InputInterface $input, OutputInterface $output) { - preg_match('#^(?:https?|http)://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url, $match); + preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url, $match); $this->owner = $match[1]; $this->repository = $match[2]; @@ -162,6 +161,6 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface */ public static function supports($url, $deep = false) { - return preg_match('#^(?:https?|http)://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url, $match); + return preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url, $match); } } diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index e7c0efbb7..5a240d34c 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -21,7 +21,7 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface public function __construct($url, InputInterface $input, OutputInterface $output) { - preg_match('#^(?:https?|http|git)://github\.com/([^/]+)/(.+?)(?:\.git)?$#', $url, $match); + preg_match('#^(?:https?|git)://github\.com/([^/]+)/(.+?)(?:\.git)?$#', $url, $match); $this->owner = $match[1]; $this->repository = $match[2]; diff --git a/src/Composer/Repository/Vcs/HgBitbucketDriver.php b/src/Composer/Repository/Vcs/HgBitbucketDriver.php index 0c3b59ced..3edc9228d 100644 --- a/src/Composer/Repository/Vcs/HgBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/HgBitbucketDriver.php @@ -31,7 +31,7 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface public function __construct($url, InputInterface $input, OutputInterface $output) { - preg_match('#^(?:https?|http)://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url, $match); + preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url, $match); $this->owner = $match[1]; $this->repository = $match[2]; From 86070a8b6e5068fd28a5ff0f2e045bef067e817f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Wed, 11 Jan 2012 01:11:56 +0100 Subject: [PATCH 07/16] Replace getHttpSupport() to getScheme() --- src/Composer/Repository/Vcs/GitBitbucketDriver.php | 12 ++++++------ src/Composer/Repository/Vcs/GitHubDriver.php | 12 ++++++------ src/Composer/Repository/Vcs/HgBitbucketDriver.php | 12 ++++++------ src/Composer/Repository/Vcs/VcsDriver.php | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitBitbucketDriver.php b/src/Composer/Repository/Vcs/GitBitbucketDriver.php index 31e744b5e..19a418515 100644 --- a/src/Composer/Repository/Vcs/GitBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/GitBitbucketDriver.php @@ -51,7 +51,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getRootIdentifier() { if (null === $this->rootIdentifier) { - $repoData = json_decode(file_get_contents($this->getHttpSupport() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository), true); + $repoData = json_decode(file_get_contents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository), true); $this->rootIdentifier = !empty($repoData['main_branch']) ? $repoData['main_branch'] : 'master'; } @@ -82,7 +82,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getDist($identifier) { $label = array_search($identifier, $this->getTags()) ?: $identifier; - $url = $this->getHttpSupport() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/get/'.$label.'.zip'; + $url = $this->getScheme() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/get/'.$label.'.zip'; return array('type' => 'zip', 'url' => $url, 'reference' => $label, 'shasum' => ''); } @@ -93,7 +93,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getComposerInformation($identifier) { if (!isset($this->infoCache[$identifier])) { - $composer = @file_get_contents($this->getHttpSupport() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/raw/'.$identifier.'/composer.json'); + $composer = @file_get_contents($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()); } @@ -101,7 +101,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface $composer = JsonFile::parseJson($composer); if (!isset($composer['time'])) { - $changeset = json_decode(file_get_contents($this->getHttpSupport() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier), true); + $changeset = json_decode(file_get_contents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier), true); $composer['time'] = $changeset['timestamp']; } $this->infoCache[$identifier] = $composer; @@ -116,7 +116,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getTags() { if (null === $this->tags) { - $tagsData = json_decode(file_get_contents($this->getHttpSupport() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); + $tagsData = json_decode(file_get_contents($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']; @@ -132,7 +132,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getBranches() { if (null === $this->branches) { - $branchData = json_decode(file_get_contents($this->getHttpSupport() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches'), true); + $branchData = json_decode(file_get_contents($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/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 5a240d34c..841be35f6 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -41,7 +41,7 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface public function getRootIdentifier() { if (null === $this->rootIdentifier) { - $repoData = json_decode(file_get_contents($this->getHttpSupport() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository), true); + $repoData = json_decode(file_get_contents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository), true); $this->rootIdentifier = $repoData['master_branch'] ?: 'master'; } @@ -72,7 +72,7 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface public function getDist($identifier) { $label = array_search($identifier, $this->getTags()) ?: $identifier; - $url = $this->getHttpSupport() . '://github.com/'.$this->owner.'/'.$this->repository.'/zipball/'.$label; + $url = $this->getScheme() . '://github.com/'.$this->owner.'/'.$this->repository.'/zipball/'.$label; return array('type' => 'zip', 'url' => $url, 'reference' => $label, 'shasum' => ''); } @@ -83,7 +83,7 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface public function getComposerInformation($identifier) { if (!isset($this->infoCache[$identifier])) { - $composer = @file_get_contents($this->getHttpSupport() . '://raw.github.com/'.$this->owner.'/'.$this->repository.'/'.$identifier.'/composer.json'); + $composer = @file_get_contents($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()); } @@ -91,7 +91,7 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface $composer = JsonFile::parseJson($composer); if (!isset($composer['time'])) { - $commit = json_decode(file_get_contents($this->getHttpSupport() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/commits/'.$identifier), true); + $commit = json_decode(file_get_contents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/commits/'.$identifier), true); $composer['time'] = $commit['commit']['committer']['date']; } $this->infoCache[$identifier] = $composer; @@ -106,7 +106,7 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface public function getTags() { if (null === $this->tags) { - $tagsData = json_decode(file_get_contents($this->getHttpSupport() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/tags'), true); + $tagsData = json_decode(file_get_contents($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']; @@ -122,7 +122,7 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface public function getBranches() { if (null === $this->branches) { - $branchData = json_decode(file_get_contents($this->getHttpSupport() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/branches'), true); + $branchData = json_decode(file_get_contents($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 3edc9228d..15b2d4660 100644 --- a/src/Composer/Repository/Vcs/HgBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/HgBitbucketDriver.php @@ -51,7 +51,7 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getRootIdentifier() { if (null === $this->rootIdentifier) { - $repoData = json_decode(file_get_contents($this->getHttpSupport() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); + $repoData = json_decode(file_get_contents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); $this->rootIdentifier = $repoData['tip']['raw_node']; } @@ -82,7 +82,7 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getDist($identifier) { $label = array_search($identifier, $this->getTags()) ?: $identifier; - $url = $this->getHttpSupport() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/get/'.$label.'.zip'; + $url = $this->getScheme() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/get/'.$label.'.zip'; return array('type' => 'zip', 'url' => $url, 'reference' => $label, 'shasum' => ''); } @@ -93,7 +93,7 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getComposerInformation($identifier) { if (!isset($this->infoCache[$identifier])) { - $composer = @file_get_contents($this->getHttpSupport() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/raw/'.$identifier.'/composer.json'); + $composer = @file_get_contents($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()); } @@ -101,7 +101,7 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface $composer = JsonFile::parseJson($composer); if (!isset($composer['time'])) { - $changeset = json_decode(file_get_contents($this->getHttpSupport() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier), true); + $changeset = json_decode(file_get_contents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier), true); $composer['time'] = $changeset['timestamp']; } $this->infoCache[$identifier] = $composer; @@ -116,7 +116,7 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getTags() { if (null === $this->tags) { - $tagsData = json_decode(file_get_contents($this->getHttpSupport() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); + $tagsData = json_decode(file_get_contents($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']; @@ -132,7 +132,7 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getBranches() { if (null === $this->branches) { - $branchData = json_decode(file_get_contents($this->getHttpSupport() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches'), true); + $branchData = json_decode(file_get_contents($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/VcsDriver.php b/src/Composer/Repository/Vcs/VcsDriver.php index 703f7b16c..98650d4f3 100644 --- a/src/Composer/Repository/Vcs/VcsDriver.php +++ b/src/Composer/Repository/Vcs/VcsDriver.php @@ -46,7 +46,7 @@ abstract class VcsDriver * * @return string The correct type of protocol */ - protected function getHttpSupport() + protected function getScheme() { if (extension_loaded('openssl')) { return 'https'; From cad1497b951c6bcef555320c1e56da1ea885cd84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Wed, 11 Jan 2012 01:26:44 +0100 Subject: [PATCH 08/16] Keep history action on download --- src/Composer/Downloader/FileDownloader.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index a87478f3e..456241899 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -104,7 +104,8 @@ abstract class FileDownloader implements DownloaderInterface copy($url, $fileName, $ctx); - $this->output->overwrite('', 80); + $this->output->overwrite(" Downloading: OK", 80); + $this->writeln(''); if (!file_exists($fileName)) { throw new \UnexpectedValueException($url.' could not be saved to '.$fileName.', make sure the' @@ -115,11 +116,11 @@ abstract class FileDownloader implements DownloaderInterface throw new \UnexpectedValueException('The checksum verification of the archive failed (downloaded from '.$url.')'); } - $this->output->overwrite(' Unpacking archive'); + $this->output->writeln(' Unpacking archive'); $this->extract($fileName, $path); - $this->output->overwrite(' Cleaning up'); + $this->output->writeln(' Cleaning up'); unlink($fileName); // If we have only a one dir inside it suppose to be a package itself From 9af46ad800bc00d48cc517b1090281a2f16ba2b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Wed, 11 Jan 2012 13:55:05 +0100 Subject: [PATCH 09/16] Add Helper Wrapper --- src/Composer/Console/Application.php | 23 +-- src/Composer/Console/Helper/Wrapper.php | 160 ++++++++++++++++++ .../Console/Helper/WrapperInterface.php | 95 +++++++++++ src/Composer/Console/Output/ConsoleOutput.php | 104 ------------ src/Composer/Downloader/FileDownloader.php | 29 ++-- .../Repository/ComposerRepository.php | 10 +- src/Composer/Repository/PackageRepository.php | 13 +- src/Composer/Repository/PearRepository.php | 9 +- src/Composer/Repository/RepositoryManager.php | 13 +- .../Repository/Vcs/GitBitbucketDriver.php | 7 +- src/Composer/Repository/Vcs/GitDriver.php | 7 +- src/Composer/Repository/Vcs/GitHubDriver.php | 7 +- .../Repository/Vcs/HgBitbucketDriver.php | 7 +- src/Composer/Repository/Vcs/HgDriver.php | 7 +- src/Composer/Repository/Vcs/SvnDriver.php | 7 +- src/Composer/Repository/Vcs/VcsDriver.php | 20 +-- src/Composer/Repository/VcsRepository.php | 15 +- 17 files changed, 320 insertions(+), 213 deletions(-) create mode 100644 src/Composer/Console/Helper/Wrapper.php create mode 100644 src/Composer/Console/Helper/WrapperInterface.php delete mode 100644 src/Composer/Console/Output/ConsoleOutput.php diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 368af21a0..fb649c812 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -12,13 +12,16 @@ 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; +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\Output\ConsoleOutput; +use Composer\Console\Helper\Wrapper; use Composer\Command; use Composer\Composer; use Composer\Installer; @@ -37,8 +40,7 @@ use Composer\Json\JsonFile; class Application extends BaseApplication { protected $composer; - protected $input; - protected $output; + protected $wrapper; public function __construct() { @@ -67,8 +69,7 @@ class Application extends BaseApplication { $this->registerCommands(); - $this->input = $input; - $this->output = $output; + $this->wrapper = new Wrapper($input, $output); return parent::doRun($input, $output); } @@ -79,7 +80,7 @@ class Application extends BaseApplication public function getComposer() { if (null === $this->composer) { - $this->composer = self::bootstrapComposer(null, $this->input, $this->output); + $this->composer = self::bootstrapComposer(null, $this->wrapper); } return $this->composer; @@ -90,7 +91,7 @@ class Application extends BaseApplication * * @return Composer */ - public static function bootstrapComposer($composerFile = null, InputInterface $input = null, OutputInterface $output = null) + public static function bootstrapComposer($composerFile = null, WrapperInterface $wrapper) { // load Composer configuration if (null === $composerFile) { @@ -128,7 +129,7 @@ class Application extends BaseApplication $binDir = getenv('COMPOSER_BIN_DIR') ?: $packageConfig['config']['bin-dir']; // initialize repository manager - $rm = new Repository\RepositoryManager($input, $output); + $rm = new Repository\RepositoryManager($wrapper); $rm->setLocalRepository(new Repository\FilesystemRepository(new JsonFile($vendorDir.'/.composer/installed.json'))); $rm->setRepositoryClass('composer', 'Composer\Repository\ComposerRepository'); $rm->setRepositoryClass('vcs', 'Composer\Repository\VcsRepository'); @@ -140,8 +141,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($input, $output)); - $dm->setDownloader('zip', new Downloader\ZipDownloader($input, $output)); + $dm->setDownloader('pear', new Downloader\PearDownloader($wrapper)); + $dm->setDownloader('zip', new Downloader\ZipDownloader($wrapper)); // initialize installation manager $im = new Installer\InstallationManager($vendorDir); @@ -154,7 +155,7 @@ class Application extends BaseApplication // load default repository unless it's explicitly disabled if (!isset($packageConfig['repositories']['packagist']) || $packageConfig['repositories']['packagist'] !== false) { - $rm->addRepository(new Repository\ComposerRepository($input, $output, array('url' => 'http://packagist.org'))); + $rm->addRepository(new Repository\ComposerRepository(array('url' => 'http://packagist.org'))); } // init locker diff --git a/src/Composer/Console/Helper/Wrapper.php b/src/Composer/Console/Helper/Wrapper.php new file mode 100644 index 000000000..6600386e6 --- /dev/null +++ b/src/Composer/Console/Helper/Wrapper.php @@ -0,0 +1,160 @@ + + * 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 new file mode 100644 index 000000000..f0fe8b922 --- /dev/null +++ b/src/Composer/Console/Helper/WrapperInterface.php @@ -0,0 +1,95 @@ + + * 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/Console/Output/ConsoleOutput.php b/src/Composer/Console/Output/ConsoleOutput.php deleted file mode 100644 index a301dd504..000000000 --- a/src/Composer/Console/Output/ConsoleOutput.php +++ /dev/null @@ -1,104 +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\Output; - -use Symfony\Component\Console\Output\ConsoleOutput as BaseConsoleOutput; - -/** - * ConsoleOutput is the default class for all CLI output. - * - * @author François Pluchino - */ -class ConsoleOutput extends BaseConsoleOutput -{ - /** - * 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) - { - 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(''); - } - } - - /** - * 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) - { - $this->write($messages, $size, true, $type); - } - - /** - * Interactively prompts for input without echoing to the terminal. - * Requires a bash shell or Windows and won't work with safe_mode - * settings (Uses `shell_exec`). - * - * @param string $title The title of prompt (only for windows) - * - * @return string The value - */ - public function promptSilent($title = '') - { - 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->writeln(''); - - return $value; - - } else { - $command = "/usr/bin/env bash -c 'echo OK'"; - - if (rtrim(shell_exec($command)) !== 'OK') { - trigger_error("Can't invoke bash"); - return; - } - - $command = "/usr/bin/env bash -c 'read -s mypassword && echo \$mypassword'"; - $value = rtrim(shell_exec($command)); - $this->writeln(''); - - return $value; - } - } -} diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 456241899..504460644 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -11,9 +11,8 @@ namespace Composer\Downloader; +use Composer\Console\Helper\WrapperInterface; use Composer\Package\PackageInterface; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputInterface; /** * Base downloader for file packages @@ -24,20 +23,17 @@ use Symfony\Component\Console\Input\InputInterface; */ abstract class FileDownloader implements DownloaderInterface { - protected $intput; - protected $output; + protected $wrapper; protected $bytesMax; /** * Constructor. * - * @param InputInterface $input The Input instance - * @param OutputInterface $output The Output instance + * @param WrapperInterface $wrapper The Wrapper instance */ - public function __construct(InputInterface $input, OutputInterface $output) + public function __construct(WrapperInterface $wrapper) { - $this->intput = $input; - $this->output = $output; + $this->wrapper = $wrapper; } /** @@ -70,7 +66,7 @@ abstract class FileDownloader implements DownloaderInterface $fileName = rtrim($path.'/'.md5(time().rand()).'.'.pathinfo($url, PATHINFO_EXTENSION), '.'); - $this->output->writeln(" - Package " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); + $this->wrapper->getOutput()->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 @@ -104,8 +100,7 @@ abstract class FileDownloader implements DownloaderInterface copy($url, $fileName, $ctx); - $this->output->overwrite(" Downloading: OK", 80); - $this->writeln(''); + $this->wrapper->overwriteln(" Downloading: OK", 80); if (!file_exists($fileName)) { throw new \UnexpectedValueException($url.' could not be saved to '.$fileName.', make sure the' @@ -116,11 +111,11 @@ abstract class FileDownloader implements DownloaderInterface throw new \UnexpectedValueException('The checksum verification of the archive failed (downloaded from '.$url.')'); } - $this->output->writeln(' Unpacking archive'); + $this->wrapper->getOutput()->writeln(' Unpacking archive'); $this->extract($fileName, $path); - $this->output->writeln(' Cleaning up'); + $this->wrapper->getOutput()->writeln(' Cleaning up'); unlink($fileName); // If we have only a one dir inside it suppose to be a package itself @@ -135,8 +130,8 @@ abstract class FileDownloader implements DownloaderInterface rmdir($contentDir); } - $this->output->overwrite(''); - $this->output->writeln(''); + $this->wrapper->overwrite(''); + $this->wrapper->getOutput()->writeln(''); } /** @@ -195,7 +190,7 @@ abstract class FileDownloader implements DownloaderInterface $progression = round($progression, 0); if (in_array($progression, $levels)) { - $this->output->overwrite(" Downloading: $progression%", 80); + $this->wrapper->overwrite(" Downloading: $progression%", 80); } } diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 8a78af773..fadfd6a7c 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -15,25 +15,17 @@ namespace Composer\Repository; use Composer\Package\Loader\ArrayLoader; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Json\JsonFile; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputInterface; /** * @author Jordi Boggiano - * @author François Pluchino */ class ComposerRepository extends ArrayRepository { protected $url; protected $packages; - protected $input; - protected $output; - public function __construct(InputInterface $input, OutputInterface $output, array $config) + public function __construct(array $config) { - $this->input = $input; - $this->output = $output; - if (!preg_match('{^\w+://}', $config['url'])) { // assume http as the default protocol $config['url'] = 'http://'.$config['url']; diff --git a/src/Composer/Repository/PackageRepository.php b/src/Composer/Repository/PackageRepository.php index 113e8c0b7..01b1b24c2 100644 --- a/src/Composer/Repository/PackageRepository.php +++ b/src/Composer/Repository/PackageRepository.php @@ -16,33 +16,24 @@ use Composer\Json\JsonFile; use Composer\Package\PackageInterface; use Composer\Package\Loader\ArrayLoader; use Composer\Package\Dumper\ArrayDumper; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputInterface; /** * Package repository. * * @author Jordi Boggiano - * @author François Pluchino */ class PackageRepository extends ArrayRepository { private $config; - private $input; - private $output; /** * Initializes filesystem repository. * - * @param InputInterface $input The Input instance - * @param OutputInterface $output The Output instance - * @param array $config package definition + * @param array $config package definition */ - public function __construct(InputInterface $input, OutputInterface $output, array $config) + public function __construct(array $config) { $this->config = $config; - $this->input = $input; - $this->output = $output; } /** diff --git a/src/Composer/Repository/PearRepository.php b/src/Composer/Repository/PearRepository.php index ae26e5579..b19620b3a 100644 --- a/src/Composer/Repository/PearRepository.php +++ b/src/Composer/Repository/PearRepository.php @@ -13,21 +13,16 @@ namespace Composer\Repository; use Composer\Package\Loader\ArrayLoader; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputInterface; /** * @author Benjamin Eberlei * @author Jordi Boggiano - * @author François Pluchino */ class PearRepository extends ArrayRepository { protected $url; - private $input; - private $output; - public function __construct(InputInterface $input, OutputInterface $output, array $config) + public function __construct(array $config) { if (!preg_match('{^https?://}', $config['url'])) { $config['url'] = 'http://'.$config['url']; @@ -37,8 +32,6 @@ class PearRepository extends ArrayRepository } $this->url = $config['url']; - $this->input = $input; - $this->output = $output; } protected function initialize() diff --git a/src/Composer/Repository/RepositoryManager.php b/src/Composer/Repository/RepositoryManager.php index 1157ae493..b6c902b6a 100644 --- a/src/Composer/Repository/RepositoryManager.php +++ b/src/Composer/Repository/RepositoryManager.php @@ -12,8 +12,7 @@ namespace Composer\Repository; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputInterface; +use Composer\Console\Helper\WrapperInterface; /** * Repositories manager. @@ -27,13 +26,11 @@ class RepositoryManager private $localRepository; private $repositories = array(); private $repositoryClasses = array(); - private $input; - private $output; + private $wrapper; - public function __construct(InputInterface $input, OutputInterface $output) + public function __construct(WrapperInterface $wrapper) { - $this->input = $input; - $this->output = $output; + $this->wrapper = $wrapper; } /** @@ -78,7 +75,7 @@ class RepositoryManager } $class = $this->repositoryClasses[$type]; - return new $class($this->input, $this->output, $config); + return new $class($config, $this->wrapper); } /** diff --git a/src/Composer/Repository/Vcs/GitBitbucketDriver.php b/src/Composer/Repository/Vcs/GitBitbucketDriver.php index 19a418515..01e90bc89 100644 --- a/src/Composer/Repository/Vcs/GitBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/GitBitbucketDriver.php @@ -13,8 +13,7 @@ namespace Composer\Repository\Vcs; use Composer\Json\JsonFile; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputInterface; +use Composer\Console\Helper\WrapperInterface; /** * @author Per Bernhardt @@ -29,13 +28,13 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface protected $rootIdentifier; protected $infoCache = array(); - public function __construct($url, InputInterface $input, OutputInterface $output) + public function __construct($url, WrapperInterface $wrapper) { preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url, $match); $this->owner = $match[1]; $this->repository = $match[2]; - parent::__construct($url, $input, $output); + parent::__construct($url, $wrapper); } /** diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index 09afc0c8e..971b13f73 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -3,8 +3,7 @@ namespace Composer\Repository\Vcs; use Composer\Json\JsonFile; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputInterface; +use Composer\Console\Helper\WrapperInterface; /** * @author Jordi Boggiano @@ -17,11 +16,11 @@ class GitDriver extends VcsDriver implements VcsDriverInterface protected $rootIdentifier; protected $infoCache = array(); - public function __construct($url, InputInterface $input, OutputInterface $output) + public function __construct($url, WrapperInterface $wrapper) { $this->tmpDir = sys_get_temp_dir() . '/composer-' . preg_replace('{[^a-z0-9]}i', '-', $url) . '/'; - parent::__construct($url, $input, $output); + parent::__construct($url, $wrapper); } /** diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 841be35f6..35f1f2737 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -3,8 +3,7 @@ namespace Composer\Repository\Vcs; use Composer\Json\JsonFile; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputInterface; +use Composer\Console\Helper\WrapperInterface; /** * @author Jordi Boggiano @@ -19,13 +18,13 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface protected $rootIdentifier; protected $infoCache = array(); - public function __construct($url, InputInterface $input, OutputInterface $output) + public function __construct($url, WrapperInterface $wrapper) { preg_match('#^(?:https?|git)://github\.com/([^/]+)/(.+?)(?:\.git)?$#', $url, $match); $this->owner = $match[1]; $this->repository = $match[2]; - parent::__construct($url, $input, $output); + parent::__construct($url, $wrapper); } /** diff --git a/src/Composer/Repository/Vcs/HgBitbucketDriver.php b/src/Composer/Repository/Vcs/HgBitbucketDriver.php index 15b2d4660..2b7908c37 100644 --- a/src/Composer/Repository/Vcs/HgBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/HgBitbucketDriver.php @@ -13,8 +13,7 @@ namespace Composer\Repository\Vcs; use Composer\Json\JsonFile; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputInterface; +use Composer\Console\Helper\WrapperInterface; /** * @author Per Bernhardt @@ -29,13 +28,13 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface protected $rootIdentifier; protected $infoCache = array(); - public function __construct($url, InputInterface $input, OutputInterface $output) + public function __construct($url, WrapperInterface $wrapper) { preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url, $match); $this->owner = $match[1]; $this->repository = $match[2]; - parent::__construct($url, $input, $output); + parent::__construct($url, $wrapper); } /** diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index 366cd7b88..928723a46 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -13,8 +13,7 @@ namespace Composer\Repository\Vcs; use Composer\Json\JsonFile; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputInterface; +use Composer\Console\Helper\WrapperInterface; /** * @author Per Bernhardt @@ -27,11 +26,11 @@ class HgDriver extends VcsDriver implements VcsDriverInterface protected $rootIdentifier; protected $infoCache = array(); - public function __construct($url, InputInterface $input, OutputInterface $output) + public function __construct($url, WrapperInterface $wrapper) { $this->tmpDir = sys_get_temp_dir() . '/composer-' . preg_replace('{[^a-z0-9]}i', '-', $url) . '/'; - parent::__construct($url, $input, $output); + parent::__construct($url, $wrapper); } /** diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index f6a27feac..9d2941d0e 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -3,8 +3,7 @@ namespace Composer\Repository\Vcs; use Composer\Json\JsonFile; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputInterface; +use Composer\Console\Helper\WrapperInterface; /** * @author Jordi Boggiano @@ -17,9 +16,9 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface protected $branches; protected $infoCache = array(); - public function __construct($url, InputInterface $input, OutputInterface $output) + public function __construct($url, WrapperInterface $wrapper) { - parent::__construct($this->baseUrl = rtrim($url, '/'), $input, $output); + parent::__construct($this->baseUrl = rtrim($url, '/'), $wrapper); 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 98650d4f3..3c0682f6e 100644 --- a/src/Composer/Repository/Vcs/VcsDriver.php +++ b/src/Composer/Repository/Vcs/VcsDriver.php @@ -13,32 +13,28 @@ namespace Composer\Repository\Vcs; /** - * A driver implementation + * A driver implementation for driver with authentification interaction. * * @author François Pluchino */ -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputInterface; +use Composer\Console\Helper\WrapperInterface; abstract class VcsDriver { protected $url; - protected $input; - protected $output; + protected $wrapper; /** - * Constructor + * Constructor. * - * @param string $url The URL - * @param InputInterface $input The Input instance - * @param OutputInterface $output The output instance + * @param string $url The URL + * @param WrapperInterface $wrapper The Wrapper instance */ - public function __construct($url, InputInterface $input, OutputInterface $output) + public function __construct($url, WrapperInterface $wrapper) { $this->url = $url; - $this->input = $input; - $this->output = $output; + $this->wrapper = $wrapper; } /** diff --git a/src/Composer/Repository/VcsRepository.php b/src/Composer/Repository/VcsRepository.php index 52802c5a5..b1f43a28c 100644 --- a/src/Composer/Repository/VcsRepository.php +++ b/src/Composer/Repository/VcsRepository.php @@ -5,8 +5,7 @@ namespace Composer\Repository; use Composer\Repository\Vcs\VcsDriverInterface; use Composer\Package\Version\VersionParser; use Composer\Package\Loader\ArrayLoader; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputInterface; +use Composer\Console\Helper\WrapperInterface; /** * @author Jordi Boggiano @@ -17,10 +16,9 @@ class VcsRepository extends ArrayRepository protected $url; protected $packageName; protected $debug; - protected $input; - protected $output; + protected $wrapper; - public function __construct(InputInterface $input, OutputInterface $output, array $config, array $drivers = null) + public function __construct(WrapperInterface $wrapper, array $config, array $drivers = null) { if (!filter_var($config['url'], FILTER_VALIDATE_URL)) { throw new \UnexpectedValueException('Invalid url given for PEAR repository: '.$config['url']); @@ -36,8 +34,7 @@ class VcsRepository extends ArrayRepository ); $this->url = $config['url']; - $this->input = $input; - $this->output = $output; + $this->wrapper = $wrapper; } public function setDebug($debug) @@ -49,7 +46,7 @@ class VcsRepository extends ArrayRepository { foreach ($this->drivers as $driver) { if ($driver::supports($this->url)) { - $driver = new $driver($this->url, $this->input, $this->output); + $driver = new $driver($this->url, $this->wrapper); $driver->initialize(); return $driver; } @@ -57,7 +54,7 @@ class VcsRepository extends ArrayRepository foreach ($this->drivers as $driver) { if ($driver::supports($this->url, true)) { - $driver = new $driver($this->url); + $driver = new $driver($this->url, $this->wrapper); $driver->initialize(); return $driver; } From a10f92aafe84d0318db4990ea3275af8c660cb32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Mon, 16 Jan 2012 14:14:15 +0100 Subject: [PATCH 10/16] Convert Wrapper class to ConsoleIO class --- src/Composer/Console/Application.php | 20 +- src/Composer/Console/Helper/Wrapper.php | 160 -------- .../Console/Helper/WrapperInterface.php | 95 ----- src/Composer/Downloader/FileDownloader.php | 26 +- src/Composer/IO/ConsoleIO.php | 374 ++++++++++++++++++ src/Composer/IO/IOInterface.php | 143 +++++++ src/Composer/Repository/RepositoryManager.php | 10 +- .../Repository/Vcs/GitBitbucketDriver.php | 17 +- src/Composer/Repository/Vcs/GitDriver.php | 7 +- src/Composer/Repository/Vcs/GitHubDriver.php | 17 +- .../Repository/Vcs/HgBitbucketDriver.php | 17 +- src/Composer/Repository/Vcs/HgDriver.php | 7 +- src/Composer/Repository/Vcs/SvnDriver.php | 7 +- src/Composer/Repository/Vcs/VcsDriver.php | 89 ++++- .../Repository/Vcs/VcsDriverInterface.php | 2 + src/Composer/Repository/VcsRepository.php | 13 +- 16 files changed, 666 insertions(+), 338 deletions(-) delete mode 100644 src/Composer/Console/Helper/Wrapper.php delete mode 100644 src/Composer/Console/Helper/WrapperInterface.php create mode 100644 src/Composer/IO/ConsoleIO.php create mode 100644 src/Composer/IO/IOInterface.php 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; } From 7888ec5313514c755b5d3bf3dbc2eeccebaa620d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Mon, 16 Jan 2012 20:09:32 +0100 Subject: [PATCH 11/16] Add Authentification in FileDownloader --- src/Composer/Command/InstallCommand.php | 2 +- src/Composer/Console/Application.php | 6 +- src/Composer/Downloader/FileDownloader.php | 142 ++++++++++++--------- src/Composer/Repository/Vcs/VcsDriver.php | 9 +- src/Composer/Repository/VcsRepository.php | 22 ++-- 5 files changed, 110 insertions(+), 71 deletions(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 392b35d28..bf8bca726 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -111,7 +111,7 @@ EOT $request->install($package->getName(), $constraint); } } else { - $output->writeln('Installing dependencies.'); + $output->writeln('Installing dependencies'); $links = $this->collectLinks($input, $composer->getPackage()); diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index e60dc63ab..60c8db7c3 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -99,11 +99,11 @@ class Application extends BaseApplication $file = new JsonFile($composerFile); if (!$file->exists()) { if ($composerFile === 'composer.json') { - echo 'Composer could not find a composer.json file in '.getcwd().PHP_EOL; + $this->io->writeln('Composer could not find a composer.json file in '.getcwd()); } else { - echo 'Composer could not find the config file: '.$composerFile.PHP_EOL; + $this->io->writeln('Composer could not find the config file: '.$composerFile); } - echo 'To initialize a project, please create a composer.json file as described on the http://packagist.org/ "Getting Started" section'.PHP_EOL; + $this->io->writeln('To initialize a project, please create a composer.json file as described on the http://packagist.org/ "Getting Started" section'); exit(1); } diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 102b627e7..df7556b2c 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -77,29 +77,19 @@ abstract class FileDownloader implements DownloaderInterface } } - // Handle system proxy - $ctx = stream_context_create(); + $auth = $this->io->getAuthentification($package->getSourceUrl()); - if (isset($_SERVER['HTTP_PROXY'])) { - // http(s):// is not supported in proxy - $proxy = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $_SERVER['HTTP_PROXY']); - - if (0 === strpos($proxy, 'ssl:') && !extension_loaded('openssl')) { - throw new \RuntimeException('You must enable the openssl extension to use a proxy over https'); - } - - $ctx = stream_context_create(array( - 'http' => array( - 'proxy' => $proxy, - 'request_fulluri' => true, - ), - )); + if (isset($_SERVER['HTTP_PROXY'])) { + // http(s):// is not supported in proxy + $proxy = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $_SERVER['HTTP_PROXY']); + + if (0 === strpos($proxy, 'ssl:') && !extension_loaded('openssl')) { + throw new \RuntimeException('You must enable the openssl extension to use a proxy over https'); + } } - stream_context_set_params($ctx, array("notification" => array($this, 'callbackDownload'))); - $this->io->overwrite(" Downloading: connection...", 80); - copy($url, $fileName, $ctx); + $this->copy($url, $fileName, $auth['username'], $auth['password']); $this->io->overwriteln(" Downloading", 80); if (!file_exists($fileName)) { @@ -156,54 +146,90 @@ abstract class FileDownloader implements DownloaderInterface /** * Download notification action. * - * @param integer $notificationCode The notification code - * @param integer $severity The severity level - * @param string $message The message - * @param integer $messageCode The message code - * @param integer $bytesTransferred The loaded size - * @param integer $bytesMax The total size + * @param integer $sizeTotal The total size + * @param integer $sizeLoaded The loaded size */ - protected function callbackDownload($notificationCode, $severity, $message, $messageCode, $bytesTransferred, $bytesMax) + protected function callbackDownload($sizeTotal, $sizeLoaded) { - switch ($notificationCode) { - case STREAM_NOTIFY_AUTH_REQUIRED: - break; - - case STREAM_NOTIFY_FILE_SIZE_IS: - if ($this->bytesMax < $bytesMax) { - $this->bytesMax = $bytesMax; - } - - break; - - case STREAM_NOTIFY_PROGRESS: - if ($this->bytesMax > 0) { - $progression = 0; + if ($sizeTotal > 1024) { + $progression = 0; - if ($this->bytesMax > 0) { - $progression = ($bytesTransferred / $this->bytesMax * 100); - } + if ($sizeTotal > 0) { + $progression = ($sizeLoaded / $sizeTotal * 100); + } - $levels = array(0, 5, 10, 15, 20, 25, 30, 35, 40, 35, 50, 55, 60, - 65, 70, 75, 80, 85, 90, 95, 100); + $levels = array(0, 5, 10, 15, 20, 25, 30, 35, 40, 35, 50, 55, 60, + 65, 70, 75, 80, 85, 90, 95, 100); - $progression = round($progression, 0); + $progression = round($progression, 0); - if (in_array($progression, $levels)) { - $this->io->overwrite(" Downloading: $progression%", 80); - } - } - - break; - - case STREAM_NOTIFY_AUTH_RESULT: - break; - - default: - break; + if (in_array($progression, $levels)) { + $this->io->overwrite(" Downloading: $progression%", 80); + } } } + /** + * Copy the content in file directory. + * + * @param string $url The file URL + * @param string $filename The local path + * @param string $username The username + * @param string $password The password + */ + protected function copy($url, $filename, $username = null, $password = null) + { + // create directory + if (!file_exists(dirname($filename))) { + mkdir(dirname($filename), 0777, true); + } + + $fh = fopen($filename, 'c+'); + + // curl options + $defaults = array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_BINARYTRANSFER => true, + CURLOPT_BUFFERSIZE => 128000, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_NOPROGRESS => false, + CURLOPT_PROGRESSFUNCTION => array($this, 'callbackDownload'), + CURLOPT_URL => $url, + CURLOPT_HTTPGET => true, + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_FILE => $fh, + ); + + // add authorization to curl options + if (null !== $username && null !== $password) { + $defaults[CURLOPT_USERPWD] = $username . ':' . $password; + } + + // init curl + $ch = curl_init(); + + // curl options + 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); + fclose($fh); + + if (200 !== $code) { + return false; + } + + return true; + } + /** * Extract file to directory * diff --git a/src/Composer/Repository/Vcs/VcsDriver.php b/src/Composer/Repository/Vcs/VcsDriver.php index 9ad139aea..93d757a02 100644 --- a/src/Composer/Repository/Vcs/VcsDriver.php +++ b/src/Composer/Repository/Vcs/VcsDriver.php @@ -23,6 +23,7 @@ abstract class VcsDriver { protected $url; protected $io; + private $firstCall; /** * Constructor. @@ -34,6 +35,7 @@ abstract class VcsDriver { $this->url = $url; $this->io = $io; + $this->firstCall = true; } /** @@ -96,8 +98,13 @@ abstract class VcsDriver curl_close($ch); // for private repository returning 404 error when the authentification is incorrect - $ps = 404 === $code && null === $this->io->getLastUsername() && null === $auth['username']; + $ps = $this->firstCall && 404 === $code && null === $this->io->getLastUsername() && null === $auth['username']; + if ($this->firstCall) { + $this->firstCall = false; + } + + // auth required if (401 === $code || $ps) { if (!$this->io->isInteractive()) { $mess = "The '$url' URL not found"; diff --git a/src/Composer/Repository/VcsRepository.php b/src/Composer/Repository/VcsRepository.php index e33c9804c..d8839e505 100644 --- a/src/Composer/Repository/VcsRepository.php +++ b/src/Composer/Repository/VcsRepository.php @@ -80,6 +80,7 @@ class VcsRepository extends ArrayRepository } foreach ($driver->getTags() as $tag => $identifier) { + $this->io->overwrite('Get composer of ' . $this->packageName . ' (' . $tag . ')'); $parsedTag = $this->validateTag($versionParser, $tag); if ($parsedTag && $driver->hasComposerFile($identifier)) { try { @@ -87,7 +88,7 @@ class VcsRepository extends ArrayRepository } catch (\Exception $e) { if (strpos($e->getMessage(), 'JSON Parse Error') !== false) { if ($debug) { - echo 'Skipped tag '.$tag.', '.$e->getMessage().PHP_EOL; + $this->io->writeln('Skipped tag '.$tag.', '.$e->getMessage()); } continue; } else { @@ -111,22 +112,25 @@ class VcsRepository extends ArrayRepository // broken package, version doesn't match tag if ($data['version_normalized'] !== $parsedTag) { if ($debug) { - echo 'Skipped tag '.$tag.', tag ('.$parsedTag.') does not match version ('.$data['version_normalized'].') in composer.json'.PHP_EOL; + $this->io->writeln('Skipped tag '.$tag.', tag ('.$parsedTag.') does not match version ('.$data['version_normalized'].') in composer.json'); } continue; } if ($debug) { - echo 'Importing tag '.$tag.' ('.$data['version_normalized'].')'.PHP_EOL; + $this->io->writeln('Importing tag '.$tag.' ('.$data['version_normalized'].')'); } $this->addPackage($loader->load($this->preProcess($driver, $data, $identifier))); } elseif ($debug) { - echo 'Skipped tag '.$tag.', '.($parsedTag ? 'no composer file was found' : 'invalid name').PHP_EOL; + $this->io->writeln('Skipped tag '.$tag.', '.($parsedTag ? 'no composer file was found' : 'invalid name')); } } + $this->io->overwrite(''); + foreach ($driver->getBranches() as $branch => $identifier) { + $this->io->overwrite('Get composer of ' . $this->packageName . ' (' . $branch . ')'); $parsedBranch = $this->validateBranch($versionParser, $branch); if ($driver->hasComposerFile($identifier)) { $data = $driver->getComposerInformation($identifier); @@ -140,7 +144,7 @@ class VcsRepository extends ArrayRepository $data['version_normalized'] = $parsedBranch; } else { if ($debug) { - echo 'Skipped branch '.$branch.', invalid name and no composer file was found'.PHP_EOL; + $this->io->writeln('Skipped branch '.$branch.', invalid name and no composer file was found'); } continue; } @@ -154,7 +158,7 @@ class VcsRepository extends ArrayRepository foreach ($this->getPackages() as $package) { if ($normalizedStableVersion === $package->getVersion()) { if ($debug) { - echo 'Skipped branch '.$branch.', already tagged'.PHP_EOL; + $this->io->writeln('Skipped branch '.$branch.', already tagged'); } continue 2; @@ -162,14 +166,16 @@ class VcsRepository extends ArrayRepository } if ($debug) { - echo 'Importing branch '.$branch.' ('.$data['version_normalized'].')'.PHP_EOL; + $this->io->writeln('Importing branch '.$branch.' ('.$data['version_normalized'].')'); } $this->addPackage($loader->load($this->preProcess($driver, $data, $identifier))); } elseif ($debug) { - echo 'Skipped branch '.$branch.', no composer file was found'.PHP_EOL; + $this->io->writeln('Skipped branch '.$branch.', no composer file was found'); } } + + $this->io->overwrite(''); } private function preProcess(VcsDriverInterface $driver, array $data, $identifier) From 0f9dcc961834ffd6f1d1fe8f140b6b8d440f1cb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Mon, 16 Jan 2012 20:44:06 +0100 Subject: [PATCH 12/16] Remplace all echo with writeln method of IO --- src/Composer/Command/InstallCommand.php | 2 +- src/Composer/Console/Application.php | 12 +++++++++-- src/Composer/DependencyResolver/Solver.php | 20 +++++++++++-------- src/Composer/Installer/InstallerInstaller.php | 6 ++++-- src/Composer/Installer/LibraryInstaller.php | 8 ++++++-- 5 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index bf8bca726..086108698 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -123,7 +123,7 @@ EOT // prepare solver $installationManager = $composer->getInstallationManager(); $policy = new DependencyResolver\DefaultPolicy(); - $solver = new DependencyResolver\Solver($policy, $pool, $installedRepo); + $solver = new DependencyResolver\Solver($policy, $pool, $installedRepo, $this->getApplication()->getIO()); // solve dependencies $operations = $solver->solve($request); diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 60c8db7c3..2ba59d27b 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -84,6 +84,14 @@ class Application extends BaseApplication return $this->composer; } + /** + * @return IOInterface + */ + public function getIO() + { + return $this->io; + } + /** * Bootstraps a Composer instance * @@ -144,8 +152,8 @@ class Application extends BaseApplication // initialize installation manager $im = new Installer\InstallationManager($vendorDir); - $im->addInstaller(new Installer\LibraryInstaller($vendorDir, $binDir, $dm, $rm->getLocalRepository(), null)); - $im->addInstaller(new Installer\InstallerInstaller($vendorDir, $binDir, $dm, $rm->getLocalRepository(), $im)); + $im->addInstaller(new Installer\LibraryInstaller($vendorDir, $binDir, $dm, $rm->getLocalRepository(), $io, null)); + $im->addInstaller(new Installer\InstallerInstaller($vendorDir, $binDir, $dm, $rm->getLocalRepository(), $io, $im)); // load package $loader = new Package\Loader\RootPackageLoader($rm); diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 2b6498965..9afb231fa 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -12,6 +12,7 @@ namespace Composer\DependencyResolver; +use Composer\IO\IOInterface; use Composer\Repository\RepositoryInterface; use Composer\Package\PackageInterface; use Composer\DependencyResolver\Operation; @@ -55,12 +56,15 @@ class Solver protected $packageToUpdateRule = array(); protected $packageToFeatureRule = array(); - public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed) + protected $io; + + public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed, IOInterface $io) { $this->policy = $policy; $this->pool = $pool; $this->installed = $installed; $this->rules = new RuleSet; + $this->io = $io; } /** @@ -2046,26 +2050,26 @@ class Solver public function printDecisionMap() { - echo "\nDecisionMap: \n"; + $this->io->writeln("\nDecisionMap: "); foreach ($this->decisionMap as $packageId => $level) { if ($packageId === 0) { continue; } if ($level > 0) { - echo ' +' . $this->pool->packageById($packageId) . "\n"; + $this->io->writeln(' +' . $this->pool->packageById($packageId)); } else { - echo ' -' . $this->pool->packageById($packageId) . "\n"; + $this->io->writeln(' -' . $this->pool->packageById($packageId)); } } - echo "\n"; + $this->io->writeln(''); } public function printDecisionQueue() { - echo "DecisionQueue: \n"; + $this->io->writeln("DecisionQueue: "); foreach ($this->decisionQueue as $i => $literal) { - echo ' ' . $literal . ' ' . $this->decisionQueueWhy[$i] . "\n"; + $this->io->writeln(' ' . $literal . ' ' . $this->decisionQueueWhy[$i]); } - echo "\n"; + $this->io->writeln(''); } } diff --git a/src/Composer/Installer/InstallerInstaller.php b/src/Composer/Installer/InstallerInstaller.php index 7c688ee34..520bde273 100644 --- a/src/Composer/Installer/InstallerInstaller.php +++ b/src/Composer/Installer/InstallerInstaller.php @@ -12,6 +12,7 @@ namespace Composer\Installer; +use Composer\IO\IOInterface; use Composer\Autoload\AutoloadGenerator; use Composer\Downloader\DownloadManager; use Composer\Repository\WritableRepositoryInterface; @@ -33,10 +34,11 @@ class InstallerInstaller extends LibraryInstaller * @param string $binDir relative path for binaries * @param DownloadManager $dm download manager * @param WritableRepositoryInterface $repository repository controller + * @param IOInterface $io io instance */ - public function __construct($vendorDir, $binDir, DownloadManager $dm, WritableRepositoryInterface $repository, InstallationManager $im) + public function __construct($vendorDir, $binDir, DownloadManager $dm, WritableRepositoryInterface $repository, IOInterface $io, InstallationManager $im) { - parent::__construct($vendorDir, $binDir, $dm, $repository, 'composer-installer'); + parent::__construct($vendorDir, $binDir, $dm, $repository, $io, 'composer-installer'); $this->installationManager = $im; foreach ($repository->getPackages() as $package) { diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index d9d7ddd76..25f77e73c 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -12,6 +12,7 @@ namespace Composer\Installer; +use Composer\IO\IOInterface; use Composer\Downloader\DownloadManager; use Composer\Repository\WritableRepositoryInterface; use Composer\DependencyResolver\Operation\OperationInterface; @@ -30,6 +31,7 @@ class LibraryInstaller implements InstallerInterface protected $binDir; protected $downloadManager; protected $repository; + protected $io; private $type; private $filesystem; @@ -40,12 +42,14 @@ class LibraryInstaller implements InstallerInterface * @param string $binDir relative path for binaries * @param DownloadManager $dm download manager * @param WritableRepositoryInterface $repository repository controller + * @param IOInterface $io io instance * @param string $type package type that this installer handles */ - public function __construct($vendorDir, $binDir, DownloadManager $dm, WritableRepositoryInterface $repository, $type = 'library') + public function __construct($vendorDir, $binDir, DownloadManager $dm, WritableRepositoryInterface $repository, IOInterface $io, $type = 'library') { $this->downloadManager = $dm; $this->repository = $repository; + $this->io = $io; $this->type = $type; $this->filesystem = new Filesystem(); @@ -136,7 +140,7 @@ class LibraryInstaller implements InstallerInterface foreach ($package->getBinaries() as $bin) { $link = $this->binDir.'/'.basename($bin); if (file_exists($link)) { - echo 'Skipped installation of '.$bin.' for package '.$package->getName().', name conflicts with an existing file'.PHP_EOL; + $this->io->writeln('Skipped installation of '.$bin.' for package '.$package->getName().', name conflicts with an existing file'); continue; } $bin = $this->getInstallPath($package).'/'.$bin; From a5fb4abb36ec848e1a2ff93a5f7b482fa12cd272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Tue, 17 Jan 2012 10:29:54 +0100 Subject: [PATCH 13/16] Fix some improvements --- src/Composer/Console/Application.php | 4 +- src/Composer/IO/ConsoleIO.php | 100 +----------------- src/Composer/IO/IOInterface.php | 38 ++++--- src/Composer/Repository/Vcs/VcsDriver.php | 3 +- .../Repository/Vcs/VcsDriverInterface.php | 2 - 5 files changed, 32 insertions(+), 115 deletions(-) diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 2ba59d27b..60df563bf 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -78,7 +78,7 @@ class Application extends BaseApplication public function getComposer() { if (null === $this->composer) { - $this->composer = self::bootstrapComposer(null, $this->io); + $this->composer = self::bootstrapComposer($this->io); } return $this->composer; @@ -97,7 +97,7 @@ class Application extends BaseApplication * * @return Composer */ - public static function bootstrapComposer($composerFile = null, IOInterface $io) + public static function bootstrapComposer(IOInterface $io, $composerFile = null) { // load Composer configuration if (null === $composerFile) { diff --git a/src/Composer/IO/ConsoleIO.php b/src/Composer/IO/ConsoleIO.php index cf2b2165f..edc970fe8 100644 --- a/src/Composer/IO/ConsoleIO.php +++ b/src/Composer/IO/ConsoleIO.php @@ -12,10 +12,8 @@ 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; @@ -37,89 +35,17 @@ class ConsoleIO implements IOInterface /** * Constructor. * - * @param InputInterface $input The input instance - * @param ConsoleOutputInterface $output The output instance - * @param HelperSet $helperSet The helperSet instance + * @param InputInterface $input The input instance + * @param OutputInterface $output The output instance + * @param HelperSet $helperSet The helperSet instance */ - public function __construct(InputInterface $input, ConsoleOutputInterface $output, HelperSet $helperSet) + public function __construct(InputInterface $input, OutputInterface $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} */ @@ -128,22 +54,6 @@ class ConsoleIO implements IOInterface return $this->input->isInteractive(); } - /** - * {@inheritDoc} - */ - public function getErrorOutput() - { - return $this->output->getErrorOutput(); - } - - /** - * {@inheritDoc} - */ - public function setErrorOutput(OutputInterface $error) - { - $this->output->setErrorOutput($error); - } - /** * {@inheritDoc} */ @@ -290,7 +200,7 @@ class ConsoleIO implements IOInterface } // for other OS with shell_exec (hide the answer) - else if (rtrim(shell_exec($command)) === 'OK') { + if (rtrim(shell_exec($command)) === 'OK') { $command = "/usr/bin/env bash -c 'echo OK'"; $this->write($question); diff --git a/src/Composer/IO/IOInterface.php b/src/Composer/IO/IOInterface.php index b05d9db68..8a3b77f43 100644 --- a/src/Composer/IO/IOInterface.php +++ b/src/Composer/IO/IOInterface.php @@ -12,8 +12,7 @@ namespace Composer\IO; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Helper\HelperSet; /** @@ -21,8 +20,17 @@ use Symfony\Component\Console\Helper\HelperSet; * * @author François Pluchino */ -interface IOInterface extends InputInterface, ConsoleOutputInterface +interface IOInterface extends OutputInterface { + + + /** + * Is this input means interactive? + * + * @return Boolean + */ + function isInteractive(); + /** * Overwrites a previous message to the output. * @@ -31,7 +39,7 @@ interface IOInterface extends InputInterface, ConsoleOutputInterface * @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); + function overwrite($messages, $size = 80, $newline = false, $type = 0); /** * Overwrites a previous message to the output and adds a newline at the end. @@ -40,7 +48,7 @@ interface IOInterface extends InputInterface, ConsoleOutputInterface * @param integer $size The size of line * @param integer $type The type of output */ - public function overwriteln($messages, $size = 80, $type = 0); + function overwriteln($messages, $size = 80, $type = 0); /** * Asks a question to the user. @@ -52,7 +60,7 @@ interface IOInterface extends InputInterface, ConsoleOutputInterface * * @throws \RuntimeException If there is no data to read in the input stream */ - public function ask($question, $default = null); + function ask($question, $default = null); /** * Asks a confirmation to the user. @@ -64,7 +72,7 @@ interface IOInterface extends InputInterface, ConsoleOutputInterface * * @return Boolean true if the user has confirmed, false otherwise */ - public function askConfirmation($question, $default = true); + function askConfirmation($question, $default = true); /** * Asks for a value and validates the response. @@ -82,7 +90,7 @@ interface IOInterface extends InputInterface, ConsoleOutputInterface * * @throws \Exception When any of the validators return an error */ - public function askAndValidate($question, $validator, $attempts = false, $default = null); + function askAndValidate($question, $validator, $attempts = false, $default = null); /** * Asks a question to the user and hide the answer. @@ -91,28 +99,28 @@ interface IOInterface extends InputInterface, ConsoleOutputInterface * * @return string The answer */ - public function askAndHideAnswer($question); + function askAndHideAnswer($question); /** * Get the last username entered. * * @return string The username */ - public function getLastUsername(); + function getLastUsername(); /** * Get the last password entered. * * @return string The password */ - public function getLastPassword(); + function getLastPassword(); /** * Get all authentification informations entered. * * @return array The map of authentification */ - public function getAuthentifications(); + function getAuthentifications(); /** * Verify if the repository has a authentification informations. @@ -121,7 +129,7 @@ interface IOInterface extends InputInterface, ConsoleOutputInterface * * @return boolean */ - public function hasAuthentification($repositoryName); + function hasAuthentification($repositoryName); /** * Get the username and password of repository. @@ -130,7 +138,7 @@ interface IOInterface extends InputInterface, ConsoleOutputInterface * * @return array The 'username' and 'password' */ - public function getAuthentification($repositoryName); + function getAuthentification($repositoryName); /** * Set the authentification informations for the repository. @@ -139,5 +147,5 @@ interface IOInterface extends InputInterface, ConsoleOutputInterface * @param string $username The username * @param string $password The password */ - public function setAuthentification($repositoryName, $username, $password = null); + function setAuthentification($repositoryName, $username, $password = null); } diff --git a/src/Composer/Repository/Vcs/VcsDriver.php b/src/Composer/Repository/Vcs/VcsDriver.php index 93d757a02..27eafb3cd 100644 --- a/src/Composer/Repository/Vcs/VcsDriver.php +++ b/src/Composer/Repository/Vcs/VcsDriver.php @@ -122,8 +122,9 @@ abstract class VcsDriver $this->io->setAuthentification($this->url, $username, $password); return $this->getContents($url); + } - } else if (404 === $code) { + if (404 === $code) { throw new \LogicException("The '$url' URL not found"); } diff --git a/src/Composer/Repository/Vcs/VcsDriverInterface.php b/src/Composer/Repository/Vcs/VcsDriverInterface.php index 9e391b2c3..124530905 100644 --- a/src/Composer/Repository/Vcs/VcsDriverInterface.php +++ b/src/Composer/Repository/Vcs/VcsDriverInterface.php @@ -2,8 +2,6 @@ namespace Composer\Repository\Vcs; -use Composer\Console\Helper\Wrapper; - /** * @author Jordi Boggiano */ From 541285022d133330cdb200704c58f79bb32cff68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Tue, 17 Jan 2012 12:52:14 +0100 Subject: [PATCH 14/16] Replace CURL with copy() and file_get_contents() --- src/Composer/Downloader/FileDownloader.php | 148 +++++++++------------ src/Composer/Repository/Vcs/VcsDriver.php | 117 +++++++++------- 2 files changed, 129 insertions(+), 136 deletions(-) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index df7556b2c..70d8243a0 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -77,19 +77,34 @@ abstract class FileDownloader implements DownloaderInterface } } - $auth = $this->io->getAuthentification($package->getSourceUrl()); + // Handle system proxy + $params = array('http' => array()); if (isset($_SERVER['HTTP_PROXY'])) { // http(s):// is not supported in proxy $proxy = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $_SERVER['HTTP_PROXY']); - + if (0 === strpos($proxy, 'ssl:') && !extension_loaded('openssl')) { throw new \RuntimeException('You must enable the openssl extension to use a proxy over https'); - } + } + + $params['http'] = array( + 'proxy' => $proxy, + 'request_fulluri' => true, + ); } + if ($this->io->hasAuthentification($package->getSourceUrl())) { + $auth = $this->io->getAuthentification($package->getSourceUrl()); + $authStr = base64_encode($auth['username'] . ':' . $auth['password']); + $params['http'] = array_merge($params['http'], array('header' => "Authorization: Basic $authStr\r\n")); + } + + $ctx = stream_context_create($params); + stream_context_set_params($ctx, array("notification" => array($this, 'callbackGet'))); + $this->io->overwrite(" Downloading: connection...", 80); - $this->copy($url, $fileName, $auth['username'], $auth['password']); + copy($url, $fileName, $ctx); $this->io->overwriteln(" Downloading", 80); if (!file_exists($fileName)) { @@ -120,7 +135,6 @@ abstract class FileDownloader implements DownloaderInterface rmdir($contentDir); } - $this->io->overwrite(''); $this->io->writeln(''); } @@ -144,92 +158,56 @@ abstract class FileDownloader implements DownloaderInterface } /** - * Download notification action. + * Get notification action. * - * @param integer $sizeTotal The total size - * @param integer $sizeLoaded The loaded size + * @param integer $notificationCode The notification code + * @param integer $severity The severity level + * @param string $message The message + * @param integer $messageCode The message code + * @param integer $bytesTransferred The loaded size + * @param integer $bytesMax The total size */ - protected function callbackDownload($sizeTotal, $sizeLoaded) + protected function callbackGet($notificationCode, $severity, $message, $messageCode, $bytesTransferred, $bytesMax) { - if ($sizeTotal > 1024) { - $progression = 0; - - if ($sizeTotal > 0) { - $progression = ($sizeLoaded / $sizeTotal * 100); - } - - $levels = array(0, 5, 10, 15, 20, 25, 30, 35, 40, 35, 50, 55, 60, - 65, 70, 75, 80, 85, 90, 95, 100); - - $progression = round($progression, 0); - - if (in_array($progression, $levels)) { - $this->io->overwrite(" Downloading: $progression%", 80); - } + switch ($notificationCode) { + case STREAM_NOTIFY_AUTH_REQUIRED: + throw new \LogicException("Authentification is required"); + break; + + case STREAM_NOTIFY_FAILURE: + throw new \LogicException("File not found"); + break; + + case STREAM_NOTIFY_FILE_SIZE_IS: + if ($this->bytesMax < $bytesMax) { + $this->bytesMax = $bytesMax; + } + break; + + case STREAM_NOTIFY_PROGRESS: + if ($this->bytesMax > 0) { + $progression = 0; + + if ($this->bytesMax > 0) { + $progression = ($bytesTransferred / $this->bytesMax * 100); + } + + $levels = array(0, 5, 10, 15, 20, 25, 30, 35, 40, 35, 50, + 55, 60, 65, 70, 75, 80, 85, 90, 95, 100); + + $progression = round($progression, 0); + + if (in_array($progression, $levels)) { + $this->io->overwrite(" Downloading: $progression%", 80); + } + } + break; + + default: + break; } } - /** - * Copy the content in file directory. - * - * @param string $url The file URL - * @param string $filename The local path - * @param string $username The username - * @param string $password The password - */ - protected function copy($url, $filename, $username = null, $password = null) - { - // create directory - if (!file_exists(dirname($filename))) { - mkdir(dirname($filename), 0777, true); - } - - $fh = fopen($filename, 'c+'); - - // curl options - $defaults = array( - CURLOPT_RETURNTRANSFER => true, - CURLOPT_BINARYTRANSFER => true, - CURLOPT_BUFFERSIZE => 128000, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_NOPROGRESS => false, - CURLOPT_PROGRESSFUNCTION => array($this, 'callbackDownload'), - CURLOPT_URL => $url, - CURLOPT_HTTPGET => true, - CURLOPT_SSL_VERIFYPEER => false, - CURLOPT_FILE => $fh, - ); - - // add authorization to curl options - if (null !== $username && null !== $password) { - $defaults[CURLOPT_USERPWD] = $username . ':' . $password; - } - - // init curl - $ch = curl_init(); - - // curl options - 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); - fclose($fh); - - if (200 !== $code) { - return false; - } - - return true; - } - /** * Extract file to directory * diff --git a/src/Composer/Repository/Vcs/VcsDriver.php b/src/Composer/Repository/Vcs/VcsDriver.php index 27eafb3cd..857899cd5 100644 --- a/src/Composer/Repository/Vcs/VcsDriver.php +++ b/src/Composer/Repository/Vcs/VcsDriver.php @@ -24,6 +24,8 @@ abstract class VcsDriver protected $url; protected $io; private $firstCall; + private $contentUrl; + private $content; /** * Constructor. @@ -60,74 +62,87 @@ abstract class VcsDriver */ protected function getContents($url) { + $this->contentUrl = $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 - ); + $params = array(); // add authorization to curl options if ($this->io->hasAuthentification($this->url)) { - $defaults[CURLOPT_USERPWD] = $auth['username'] . ':' . $auth['password']; + $authStr = base64_encode($auth['username'] . ':' . $auth['password']); + $params['http'] = array('header' => "Authorization: Basic $authStr\r\n"); } else if (null !== $this->io->getLastUsername()) { - $defaults[CURLOPT_USERPWD] = $this->io->getLastUsername() . ':' . $this->io->getLastPassword(); + $authStr = base64_encode($this->io->getLastUsername() . ':' . $this->io->getLastPassword()); + $params['http'] = array('header' => "Authorization: Basic $authStr\r\n"); } - // init curl - $ch = curl_init(); - curl_setopt_array($ch, $defaults); + $ctx = stream_context_create($params); + stream_context_set_params($ctx, array("notification" => array($this, 'callbackGet'))); - // 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; + $content = @file_get_contents($url, false, $ctx); - //close streams - curl_close($ch); - - // for private repository returning 404 error when the authentification is incorrect - $ps = $this->firstCall && 404 === $code && null === $this->io->getLastUsername() && null === $auth['username']; - - if ($this->firstCall) { - $this->firstCall = false; + // content get after authentification + if (false === $content) { + $content = $this->content; + $this->content = null; + $this->contentUrl = null; } - // auth required - if (401 === $code || $ps) { - if (!$this->io->isInteractive()) { - $mess = "The '$url' URL not found"; + return $content; + } - if (401 === $code || $ps) { - $mess = "The '$url' URL required the authentification.\nYou must be used the interactive console"; + /** + * Get notification action. + * + * @param integer $notificationCode The notification code + * @param integer $severity The severity level + * @param string $message The message + * @param integer $messageCode The message code + * @param integer $bytesTransferred The loaded size + * @param integer $bytesMax The total size + */ + protected function callbackGet($notificationCode, $severity, $message, $messageCode, $bytesTransferred, $bytesMax) + { + switch ($notificationCode) { + case STREAM_NOTIFY_AUTH_REQUIRED: + case STREAM_NOTIFY_FAILURE: + // for private repository returning 404 error when the authentification is incorrect + $auth = $this->io->getAuthentification($this->url); + $ps = $this->firstCall && 404 === $messageCode + && null === $this->io->getLastUsername() + && null === $auth['username']; + + if (404 === $messageCode && !$this->firstCall) { + throw new \LogicException("The '" . $this->contentUrl . "' URL not found"); + } + + if ($this->firstCall) { + $this->firstCall = false; } - throw new \LogicException($mess); - } + // get authentification informations + if (401 === $messageCode || $ps) { + if (!$this->io->isInteractive()) { + $mess = "The '" . $this->contentUrl . "' URL not found"; + + if (401 === $code || $ps) { + $mess = "The '" . $this->contentUrl . "' 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); + $this->io->writeln("Authorization for " . $this->contentUrl . ":"); + $username = $this->io->ask(' Username: '); + $password = $this->io->askAndHideAnswer(' Password: '); + $this->io->setAuthentification($this->url, $username, $password); - return $this->getContents($url); + $this->content = $this->getContents($this->contentUrl); + } + break; + + default: + break; } - - if (404 === $code) { - throw new \LogicException("The '$url' URL not found"); - } - - return $curl_result; } } From 9963bde3672cf9898f61a4d562a0c68fcc0c513d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Tue, 17 Jan 2012 13:24:18 +0100 Subject: [PATCH 15/16] Fix some improvements --- src/Composer/Downloader/FileDownloader.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 70d8243a0..3f19020c4 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -189,15 +189,10 @@ abstract class FileDownloader implements DownloaderInterface $progression = 0; if ($this->bytesMax > 0) { - $progression = ($bytesTransferred / $this->bytesMax * 100); + $progression = round($bytesTransferred / $this->bytesMax * 100); } - $levels = array(0, 5, 10, 15, 20, 25, 30, 35, 40, 35, 50, - 55, 60, 65, 70, 75, 80, 85, 90, 95, 100); - - $progression = round($progression, 0); - - if (in_array($progression, $levels)) { + if (0 === $progression % 5) { $this->io->overwrite(" Downloading: $progression%", 80); } } From 03f5eee3fa2d3d87ca7372d871eb032d4db1a68d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Tue, 17 Jan 2012 14:53:50 +0100 Subject: [PATCH 16/16] Fix some improvements --- src/Composer/Downloader/FileDownloader.php | 6 ++--- src/Composer/IO/ConsoleIO.php | 26 +++++++++------------- src/Composer/IO/IOInterface.php | 20 ++++++++--------- src/Composer/Repository/Vcs/VcsDriver.php | 22 +++++++++--------- 4 files changed, 34 insertions(+), 40 deletions(-) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 3f19020c4..0a25fddb8 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -94,8 +94,8 @@ abstract class FileDownloader implements DownloaderInterface ); } - if ($this->io->hasAuthentification($package->getSourceUrl())) { - $auth = $this->io->getAuthentification($package->getSourceUrl()); + if ($this->io->hasAuthorization($package->getSourceUrl())) { + $auth = $this->io->getAuthorization($package->getSourceUrl()); $authStr = base64_encode($auth['username'] . ':' . $auth['password']); $params['http'] = array_merge($params['http'], array('header' => "Authorization: Basic $authStr\r\n")); } @@ -171,7 +171,7 @@ abstract class FileDownloader implements DownloaderInterface { switch ($notificationCode) { case STREAM_NOTIFY_AUTH_REQUIRED: - throw new \LogicException("Authentification is required"); + throw new \LogicException("Authorization is required"); break; case STREAM_NOTIFY_FAILURE: diff --git a/src/Composer/IO/ConsoleIO.php b/src/Composer/IO/ConsoleIO.php index edc970fe8..cf11f6a57 100644 --- a/src/Composer/IO/ConsoleIO.php +++ b/src/Composer/IO/ConsoleIO.php @@ -28,7 +28,7 @@ class ConsoleIO implements IOInterface protected $input; protected $output; protected $helperSet; - protected $authentifications; + protected $authorizations = array(); protected $lastUsername; protected $lastPassword; @@ -242,42 +242,38 @@ class ConsoleIO implements IOInterface /** * {@inheritDoc} */ - public function getAuthentifications() + public function getAuthorizations() { - if (null === $this->authentifications) { - $this->authentifications = array(); - } - - return $this->authentifications; + return $this->authorizations; } /** * {@inheritDoc} */ - public function hasAuthentification($repositoryName) + public function hasAuthorization($repositoryName) { - $auths = $this->getAuthentifications(); - return isset($auths[$repositoryName]) ? true : false; + $auths = $this->getAuthorizations(); + return isset($auths[$repositoryName]); } /** * {@inheritDoc} */ - public function getAuthentification($repositoryName) + public function getAuthorization($repositoryName) { - $auths = $this->getAuthentifications(); + $auths = $this->getAuthorizations(); return isset($auths[$repositoryName]) ? $auths[$repositoryName] : array('username' => null, 'password' => null); } /** * {@inheritDoc} */ - public function setAuthentification($repositoryName, $username, $password = null) + public function setAuthorization($repositoryName, $username, $password = null) { - $auths = $this->getAuthentifications(); + $auths = $this->getAuthorizations(); $auths[$repositoryName] = array('username' => $username, 'password' => $password); - $this->authentifications = $auths; + $this->authorizations = $auths; $this->lastUsername = $username; $this->lastPassword = $password; } diff --git a/src/Composer/IO/IOInterface.php b/src/Composer/IO/IOInterface.php index 8a3b77f43..e5f81facf 100644 --- a/src/Composer/IO/IOInterface.php +++ b/src/Composer/IO/IOInterface.php @@ -21,9 +21,7 @@ use Symfony\Component\Console\Helper\HelperSet; * @author François Pluchino */ interface IOInterface extends OutputInterface -{ - - +{ /** * Is this input means interactive? * @@ -116,20 +114,20 @@ interface IOInterface extends OutputInterface function getLastPassword(); /** - * Get all authentification informations entered. + * Get all authorization informations entered. * - * @return array The map of authentification + * @return array The map of authorization */ - function getAuthentifications(); + function getAuthorizations(); /** - * Verify if the repository has a authentification informations. + * Verify if the repository has a authorization informations. * * @param string $repositoryName The unique name of repository * * @return boolean */ - function hasAuthentification($repositoryName); + function hasAuthorization($repositoryName); /** * Get the username and password of repository. @@ -138,14 +136,14 @@ interface IOInterface extends OutputInterface * * @return array The 'username' and 'password' */ - function getAuthentification($repositoryName); + function getAuthorization($repositoryName); /** - * Set the authentification informations for the repository. + * Set the authorization informations for the repository. * * @param string $repositoryName The unique name of repository * @param string $username The username * @param string $password The password */ - function setAuthentification($repositoryName, $username, $password = null); + function setAuthorization($repositoryName, $username, $password = null); } diff --git a/src/Composer/Repository/Vcs/VcsDriver.php b/src/Composer/Repository/Vcs/VcsDriver.php index 857899cd5..bd7565603 100644 --- a/src/Composer/Repository/Vcs/VcsDriver.php +++ b/src/Composer/Repository/Vcs/VcsDriver.php @@ -15,7 +15,7 @@ namespace Composer\Repository\Vcs; use Composer\IO\IOInterface; /** - * A driver implementation for driver with authentification interaction. + * A driver implementation for driver with authorization interaction. * * @author François Pluchino */ @@ -63,11 +63,11 @@ abstract class VcsDriver protected function getContents($url) { $this->contentUrl = $url; - $auth = $this->io->getAuthentification($this->url); + $auth = $this->io->getAuthorization($this->url); $params = array(); // add authorization to curl options - if ($this->io->hasAuthentification($this->url)) { + if ($this->io->hasAuthorization($this->url)) { $authStr = base64_encode($auth['username'] . ':' . $auth['password']); $params['http'] = array('header' => "Authorization: Basic $authStr\r\n"); @@ -81,7 +81,7 @@ abstract class VcsDriver $content = @file_get_contents($url, false, $ctx); - // content get after authentification + // content get after authorization if (false === $content) { $content = $this->content; $this->content = null; @@ -106,36 +106,36 @@ abstract class VcsDriver switch ($notificationCode) { case STREAM_NOTIFY_AUTH_REQUIRED: case STREAM_NOTIFY_FAILURE: - // for private repository returning 404 error when the authentification is incorrect - $auth = $this->io->getAuthentification($this->url); + // for private repository returning 404 error when the authorization is incorrect + $auth = $this->io->getAuthorization($this->url); $ps = $this->firstCall && 404 === $messageCode && null === $this->io->getLastUsername() && null === $auth['username']; if (404 === $messageCode && !$this->firstCall) { - throw new \LogicException("The '" . $this->contentUrl . "' URL not found"); + throw new \RuntimeException("The '" . $this->contentUrl . "' URL not found"); } if ($this->firstCall) { $this->firstCall = false; } - // get authentification informations + // get authorization informations if (401 === $messageCode || $ps) { if (!$this->io->isInteractive()) { $mess = "The '" . $this->contentUrl . "' URL not found"; if (401 === $code || $ps) { - $mess = "The '" . $this->contentUrl . "' URL required the authentification.\nYou must be used the interactive console"; + $mess = "The '" . $this->contentUrl . "' URL required the authorization.\nYou must be used the interactive console"; } - throw new \LogicException($mess); + throw new \RuntimeException($mess); } $this->io->writeln("Authorization for " . $this->contentUrl . ":"); $username = $this->io->ask(' Username: '); $password = $this->io->askAndHideAnswer(' Password: '); - $this->io->setAuthentification($this->url, $username, $password); + $this->io->setAuthorization($this->url, $username, $password); $this->content = $this->getContents($this->contentUrl); }