Bläddra i källkod

Overhaul VcsDrivers, introduce TransportException for remote filesystem errors

Jordi Boggiano 13 år sedan
förälder
incheckning
3e22084ea4

+ 20 - 0
src/Composer/Downloader/TransportException.php

@@ -0,0 +1,20 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Downloader;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class TransportException extends \Exception
+{
+}

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

+ 2 - 16
src/Composer/Repository/Vcs/GitDriver.php

@@ -9,7 +9,7 @@ use Composer\IO\IOInterface;
 /**
  * @author Jordi Boggiano <j.boggiano@seld.be>
  */
-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}
      */

+ 6 - 20
src/Composer/Repository/Vcs/GitHubDriver.php

@@ -8,7 +8,7 @@ use Composer\IO\IOInterface;
 /**
  * @author Jordi Boggiano <j.boggiano@seld.be>
  */
-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}
      */

+ 7 - 21
src/Composer/Repository/Vcs/HgBitbucketDriver.php

@@ -18,7 +18,7 @@ use Composer\IO\IOInterface;
 /**
  * @author Per Bernhardt <plb@webfactory.de>
  */
-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);
     }
 }

+ 2 - 16
src/Composer/Repository/Vcs/HgDriver.php

@@ -19,7 +19,7 @@ use Composer\IO\IOInterface;
 /**
  * @author Per Bernhardt <plb@webfactory.de>
  */
-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}
      */

+ 2 - 16
src/Composer/Repository/Vcs/SvnDriver.php

@@ -9,7 +9,7 @@ use Composer\IO\IOInterface;
 /**
  * @author Jordi Boggiano <j.boggiano@seld.be>
  */
-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}
      */

+ 16 - 1
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 <francois.pluchino@opendisplay.com>
  */
-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.
      *

+ 5 - 1
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

+ 6 - 5
src/Composer/Util/RemoteFilesystem.php

@@ -13,6 +13,7 @@
 namespace Composer\Util;
 
 use Composer\IO\IOInterface;
+use Composer\Downloader\TransportException;
 
 /**
  * @author François Pluchino <francois.pluchino@opendisplay.com>
@@ -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 (<info>'.parse_url($this->fileUrl, PHP_URL_HOST).'</info>):');

+ 2 - 2
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);
         }