From 3e22084ea4c44f7c8c8c8d13ecdd819f9baf914f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 8 Mar 2012 21:59:02 +0100 Subject: [PATCH] Overhaul VcsDrivers, introduce TransportException for remote filesystem errors --- .../Downloader/TransportException.php | 20 +++++++++++++ .../Repository/Vcs/GitBitbucketDriver.php | 24 ++++------------ src/Composer/Repository/Vcs/GitDriver.php | 18 ++---------- src/Composer/Repository/Vcs/GitHubDriver.php | 26 ++++------------- .../Repository/Vcs/HgBitbucketDriver.php | 28 +++++-------------- src/Composer/Repository/Vcs/HgDriver.php | 18 ++---------- src/Composer/Repository/Vcs/SvnDriver.php | 18 ++---------- src/Composer/Repository/Vcs/VcsDriver.php | 17 ++++++++++- src/Composer/Repository/VcsRepository.php | 6 +++- src/Composer/Util/RemoteFilesystem.php | 11 ++++---- .../Test/Util/RemoteFilesystemTest.php | 4 +-- 11 files changed, 73 insertions(+), 117 deletions(-) create mode 100644 src/Composer/Downloader/TransportException.php diff --git a/src/Composer/Downloader/TransportException.php b/src/Composer/Downloader/TransportException.php new file mode 100644 index 000000000..61bd67d11 --- /dev/null +++ b/src/Composer/Downloader/TransportException.php @@ -0,0 +1,20 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Downloader; + +/** + * @author Jordi Boggiano + */ +class TransportException extends \Exception +{ +} diff --git a/src/Composer/Repository/Vcs/GitBitbucketDriver.php b/src/Composer/Repository/Vcs/GitBitbucketDriver.php index 20e060e68..8021a2cb8 100644 --- a/src/Composer/Repository/Vcs/GitBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/GitBitbucketDriver.php @@ -49,7 +49,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getRootIdentifier() { if (null === $this->rootIdentifier) { - $repoData = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository), true); + $repoData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository)); $this->rootIdentifier = !empty($repoData['main_branch']) ? $repoData['main_branch'] : 'master'; } @@ -93,13 +93,13 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface if (!isset($this->infoCache[$identifier])) { $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()); + return; } $composer = JsonFile::parseJson($composer); if (!isset($composer['time'])) { - $changeset = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier), true); + $changeset = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier)); $composer['time'] = $changeset['timestamp']; } $this->infoCache[$identifier] = $composer; @@ -114,7 +114,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getTags() { if (null === $this->tags) { - $tagsData = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); + $tagsData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags')); $this->tags = array(); foreach ($tagsData as $tag => $data) { $this->tags[$tag] = $data['raw_node']; @@ -130,7 +130,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getBranches() { if (null === $this->branches) { - $branchData = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches'), true); + $branchData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches')); $this->branches = array(); foreach ($branchData as $branch => $data) { $this->branches[$branch] = $data['raw_node']; @@ -140,20 +140,6 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface return $this->branches; } - /** - * {@inheritDoc} - */ - public function hasComposerFile($identifier) - { - try { - $this->getComposerInformation($identifier); - return true; - } catch (\Exception $e) { - } - - return false; - } - /** * {@inheritDoc} */ diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index d6c49daf2..90f5ff926 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -9,7 +9,7 @@ use Composer\IO\IOInterface; /** * @author Jordi Boggiano */ -class GitDriver extends VcsDriver implements VcsDriverInterface +class GitDriver extends VcsDriver { protected $tags; protected $branches; @@ -117,7 +117,7 @@ class GitDriver extends VcsDriver implements VcsDriverInterface $this->process->execute(sprintf('cd %s && git show %s:composer.json', escapeshellarg($this->repoDir), escapeshellarg($identifier)), $composer); if (!trim($composer)) { - throw new \UnexpectedValueException('Failed to retrieve composer information for identifier '.$identifier.' in '.$this->getUrl()); + return; } $composer = JsonFile::parseJson($composer); @@ -173,20 +173,6 @@ class GitDriver extends VcsDriver implements VcsDriverInterface return $this->branches; } - /** - * {@inheritDoc} - */ - public function hasComposerFile($identifier) - { - try { - $this->getComposerInformation($identifier); - return true; - } catch (\Exception $e) { - } - - return false; - } - /** * {@inheritDoc} */ diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 13071a594..01371c1a2 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -8,7 +8,7 @@ use Composer\IO\IOInterface; /** * @author Jordi Boggiano */ -class GitHubDriver extends VcsDriver implements VcsDriverInterface +class GitHubDriver extends VcsDriver { protected $owner; protected $repository; @@ -39,7 +39,7 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface public function getRootIdentifier() { if (null === $this->rootIdentifier) { - $repoData = json_decode($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository), true); + $repoData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository)); $this->rootIdentifier = $repoData['master_branch'] ?: 'master'; } @@ -83,13 +83,13 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface if (!isset($this->infoCache[$identifier])) { $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()); + return; } $composer = JsonFile::parseJson($composer); if (!isset($composer['time'])) { - $commit = json_decode($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/commits/'.$identifier), true); + $commit = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/commits/'.$identifier)); $composer['time'] = $commit['commit']['committer']['date']; } $this->infoCache[$identifier] = $composer; @@ -104,7 +104,7 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface public function getTags() { if (null === $this->tags) { - $tagsData = json_decode($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/tags'), true); + $tagsData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/tags')); $this->tags = array(); foreach ($tagsData as $tag) { $this->tags[$tag['name']] = $tag['commit']['sha']; @@ -120,7 +120,7 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface public function getBranches() { if (null === $this->branches) { - $branchData = json_decode($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/branches'), true); + $branchData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/branches')); $this->branches = array(); foreach ($branchData as $branch) { $this->branches[$branch['name']] = $branch['commit']['sha']; @@ -130,20 +130,6 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface return $this->branches; } - /** - * {@inheritDoc} - */ - public function hasComposerFile($identifier) - { - try { - $this->getComposerInformation($identifier); - return true; - } catch (\Exception $e) { - } - - return false; - } - /** * {@inheritDoc} */ diff --git a/src/Composer/Repository/Vcs/HgBitbucketDriver.php b/src/Composer/Repository/Vcs/HgBitbucketDriver.php index 6d2e8b066..54dfd5d30 100644 --- a/src/Composer/Repository/Vcs/HgBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/HgBitbucketDriver.php @@ -18,7 +18,7 @@ use Composer\IO\IOInterface; /** * @author Per Bernhardt */ -class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface +class HgBitbucketDriver extends VcsDriver { protected $owner; protected $repository; @@ -49,7 +49,7 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getRootIdentifier() { if (null === $this->rootIdentifier) { - $repoData = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); + $repoData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags')); $this->rootIdentifier = $repoData['tip']['raw_node']; } @@ -93,13 +93,13 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface if (!isset($this->infoCache[$identifier])) { $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()); + return; } $composer = JsonFile::parseJson($composer); if (!isset($composer['time'])) { - $changeset = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier), true); + $changeset = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier)); $composer['time'] = $changeset['timestamp']; } $this->infoCache[$identifier] = $composer; @@ -114,7 +114,7 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getTags() { if (null === $this->tags) { - $tagsData = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); + $tagsData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags')); $this->tags = array(); foreach ($tagsData as $tag => $data) { $this->tags[$tag] = $data['raw_node']; @@ -130,7 +130,7 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getBranches() { if (null === $this->branches) { - $branchData = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches'), true); + $branchData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches')); $this->branches = array(); foreach ($branchData as $branch => $data) { $this->branches[$branch] = $data['raw_node']; @@ -140,25 +140,11 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface return $this->branches; } - /** - * {@inheritDoc} - */ - public function hasComposerFile($identifier) - { - try { - $this->getComposerInformation($identifier); - return true; - } catch (\Exception $e) { - } - - return false; - } - /** * {@inheritDoc} */ public static function supports($url, $deep = false) { - return preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url); + return extension_loaded('openssl') && preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url); } } diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index 1ca21948d..f7390fb47 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -19,7 +19,7 @@ use Composer\IO\IOInterface; /** * @author Per Bernhardt */ -class HgDriver extends VcsDriver implements VcsDriverInterface +class HgDriver extends VcsDriver { protected $tags; protected $branches; @@ -100,7 +100,7 @@ class HgDriver extends VcsDriver implements VcsDriverInterface $this->process->execute(sprintf('cd %s && hg cat -r %s composer.json', escapeshellarg($this->tmpDir), escapeshellarg($identifier)), $composer); if (!trim($composer)) { - throw new \UnexpectedValueException('Failed to retrieve composer information for identifier ' . $identifier . ' in ' . $this->getUrl()); + return; } $composer = JsonFile::parseJson($composer); @@ -159,20 +159,6 @@ class HgDriver extends VcsDriver implements VcsDriverInterface return $this->branches; } - /** - * {@inheritDoc} - */ - public function hasComposerFile($identifier) - { - try { - $this->getComposerInformation($identifier); - return true; - } catch (\Exception $e) { - } - - return false; - } - /** * {@inheritDoc} */ diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 2d428f6d4..f9b986c91 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -9,7 +9,7 @@ use Composer\IO\IOInterface; /** * @author Jordi Boggiano */ -class SvnDriver extends VcsDriver implements VcsDriverInterface +class SvnDriver extends VcsDriver { protected $baseUrl; protected $tags; @@ -108,7 +108,7 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface ); if (!trim($composer)) { - throw new \UnexpectedValueException('Failed to retrieve composer information for identifier '.$identifier.' in '.$this->getUrl()); + return; } $composer = JsonFile::parseJson($composer); @@ -226,20 +226,6 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface ); } - /** - * {@inheritDoc} - */ - public function hasComposerFile($identifier) - { - try { - $this->getComposerInformation($identifier); - return true; - } catch (\Exception $e) { - } - - return false; - } - /** * {@inheritDoc} */ diff --git a/src/Composer/Repository/Vcs/VcsDriver.php b/src/Composer/Repository/Vcs/VcsDriver.php index 75b631a42..a20e959fd 100644 --- a/src/Composer/Repository/Vcs/VcsDriver.php +++ b/src/Composer/Repository/Vcs/VcsDriver.php @@ -12,6 +12,7 @@ namespace Composer\Repository\Vcs; +use Composer\Downloader\TransportException; use Composer\IO\IOInterface; use Composer\Util\ProcessExecutor; use Composer\Util\RemoteFilesystem; @@ -21,7 +22,7 @@ use Composer\Util\RemoteFilesystem; * * @author François Pluchino */ -abstract class VcsDriver +abstract class VcsDriver implements VcsDriverInterface { protected $url; protected $io; @@ -41,6 +42,20 @@ abstract class VcsDriver $this->process = $process ?: new ProcessExecutor; } + /** + * {@inheritDoc} + */ + public function hasComposerFile($identifier) + { + try { + return (Boolean) $this->getComposerInformation($identifier); + } catch (TransportException $e) { + } + + return false; + } + + /** * Get the https or http protocol depending on SSL support. * diff --git a/src/Composer/Repository/VcsRepository.php b/src/Composer/Repository/VcsRepository.php index afdc12bcb..fa37a62aa 100644 --- a/src/Composer/Repository/VcsRepository.php +++ b/src/Composer/Repository/VcsRepository.php @@ -2,6 +2,7 @@ namespace Composer\Repository; +use Composer\Downloader\TransportException; use Composer\Repository\Vcs\VcsDriverInterface; use Composer\Package\Version\VersionParser; use Composer\Package\PackageInterface; @@ -90,11 +91,14 @@ class VcsRepository extends ArrayRepository if ($parsedTag && $driver->hasComposerFile($identifier)) { try { $data = $driver->getComposerInformation($identifier); - } catch (\Exception $e) { + } catch (TransportException $e) { if ($debug) { $this->io->write('Skipped tag '.$tag.', '.$e->getMessage()); } continue; + } catch (\Exception $e) { + $this->io->write('Skipped tag '.$tag.', '.$e->getMessage()); + continue; } // manually versioned package diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 7bd0f3bfe..bc50b06fb 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -13,6 +13,7 @@ namespace Composer\Util; use Composer\IO\IOInterface; +use Composer\Downloader\TransportException; /** * @author François Pluchino @@ -81,7 +82,7 @@ class RemoteFilesystem * @param boolean $progress Display the progression * @param boolean $firstCall Whether this is the first attempt at fetching this resource * - * @throws \RuntimeException When the file could not be downloaded + * @throws TransportException When the file could not be downloaded */ protected function get($originUrl, $fileUrl, $fileName = null, $progress = true, $firstCall = true) { @@ -117,7 +118,7 @@ class RemoteFilesystem } if (false === $this->result) { - throw new \RuntimeException("The '$fileUrl' file could not be downloaded"); + throw new TransportException("The '$fileUrl' file could not be downloaded"); } } @@ -137,7 +138,7 @@ class RemoteFilesystem case STREAM_NOTIFY_AUTH_REQUIRED: case STREAM_NOTIFY_FAILURE: if (404 === $messageCode && !$this->firstCall) { - throw new \RuntimeException("The '" . $this->fileUrl . "' URL not found"); + throw new TransportException("The '" . $this->fileUrl . "' URL not found", 404); } // for private repository returning 404 error when the authorization is incorrect @@ -149,9 +150,9 @@ class RemoteFilesystem // get authorization informations if (401 === $messageCode || $attemptAuthentication) { if (!$this->io->isInteractive()) { - $mess = "The '" . $this->fileUrl . "' URL required authentication.\nYou must be using the interactive console"; + $message = "The '" . $this->fileUrl . "' URL required authentication.\nYou must be using the interactive console"; - throw new \RuntimeException($mess); + throw new TransportException($message, 401); } $this->io->overwrite(' Authentication required ('.parse_url($this->fileUrl, PHP_URL_HOST).'):'); diff --git a/tests/Composer/Test/Util/RemoteFilesystemTest.php b/tests/Composer/Test/Util/RemoteFilesystemTest.php index c7e5c076e..bee389941 100644 --- a/tests/Composer/Test/Util/RemoteFilesystemTest.php +++ b/tests/Composer/Test/Util/RemoteFilesystemTest.php @@ -111,7 +111,7 @@ class RemoteFilesystemTest extends \PHPUnit_Framework_TestCase $this->callCallbackGet($fs, STREAM_NOTIFY_FAILURE, 0, '', 404, 0, 0); $this->fail(); } catch (\Exception $e) { - $this->assertInstanceOf('RuntimeException', $e); + $this->assertInstanceOf('Composer\Downloader\TransportException', $e); $this->assertContains('URL not found', $e->getMessage()); } } @@ -137,7 +137,7 @@ class RemoteFilesystemTest extends \PHPUnit_Framework_TestCase $this->callCallbackGet($fs, STREAM_NOTIFY_FAILURE, 0, '', 404, 0, 0); $this->fail(); } catch (\Exception $e) { - $this->assertInstanceOf('RuntimeException', $e); + $this->assertInstanceOf('Composer\Downloader\TransportException', $e); $this->assertContains('URL required authentication', $e->getMessage()); $this->assertAttributeEquals(false, 'firstCall', $fs); }