FileDownloader.php 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. <?php
  2. /*
  3. * This file is part of Composer.
  4. *
  5. * (c) Nils Adermann <naderman@naderman.de>
  6. * Jordi Boggiano <j.boggiano@seld.be>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Composer\Downloader;
  12. use Composer\Config;
  13. use Composer\IO\IOInterface;
  14. use Composer\Package\PackageInterface;
  15. use Composer\Package\Version\VersionParser;
  16. use Composer\Util\Filesystem;
  17. use Composer\Util\GitHub;
  18. use Composer\Util\RemoteFilesystem;
  19. /**
  20. * Base downloader for files
  21. *
  22. * @author Kirill chEbba Chebunin <iam@chebba.org>
  23. * @author Jordi Boggiano <j.boggiano@seld.be>
  24. * @author François Pluchino <francois.pluchino@opendisplay.com>
  25. */
  26. class FileDownloader implements DownloaderInterface
  27. {
  28. protected $io;
  29. protected $config;
  30. protected $rfs;
  31. protected $filesystem;
  32. /**
  33. * Constructor.
  34. *
  35. * @param IOInterface $io The IO instance
  36. * @param Config $config The config
  37. * @param RemoteFilesystem $rfs The remote filesystem
  38. * @param Filesystem $filesystem The filesystem
  39. */
  40. public function __construct(IOInterface $io, Config $config, RemoteFilesystem $rfs = null, Filesystem $filesystem = null)
  41. {
  42. $this->io = $io;
  43. $this->config = $config;
  44. $this->rfs = $rfs ?: new RemoteFilesystem($io);
  45. $this->filesystem = $filesystem ?: new Filesystem();
  46. }
  47. /**
  48. * {@inheritDoc}
  49. */
  50. public function getInstallationSource()
  51. {
  52. return 'dist';
  53. }
  54. /**
  55. * {@inheritDoc}
  56. */
  57. public function download(PackageInterface $package, $path)
  58. {
  59. $url = $package->getDistUrl();
  60. if (!$url) {
  61. throw new \InvalidArgumentException('The given package is missing url information');
  62. }
  63. $this->filesystem->ensureDirectoryExists($path);
  64. $fileName = $this->getFileName($package, $path);
  65. $this->io->write(" - Installing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
  66. $processUrl = $this->processUrl($package, $url);
  67. try {
  68. try {
  69. $this->rfs->copy(parse_url($processUrl, PHP_URL_HOST), $processUrl, $fileName);
  70. } catch (TransportException $e) {
  71. if (404 === $e->getCode() && 'github.com' === parse_url($processUrl, PHP_URL_HOST)) {
  72. $message = "\n".'Could not fetch '.$processUrl.', enter your GitHub credentials to access private repos';
  73. $gitHubUtil = new GitHub($this->io, $this->config, null, $this->rfs);
  74. if (!$gitHubUtil->authorizeOAuth('github.com')
  75. && (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively('github.com', $message))
  76. ) {
  77. throw $e;
  78. }
  79. $this->rfs->copy(parse_url($processUrl, PHP_URL_HOST), $processUrl, $fileName);
  80. } else {
  81. throw $e;
  82. }
  83. }
  84. if (!file_exists($fileName)) {
  85. throw new \UnexpectedValueException($url.' could not be saved to '.$fileName.', make sure the'
  86. .' directory is writable and you have internet connectivity');
  87. }
  88. $checksum = $package->getDistSha1Checksum();
  89. if ($checksum && hash_file('sha1', $fileName) !== $checksum) {
  90. throw new \UnexpectedValueException('The checksum verification of the file failed (downloaded from '.$url.')');
  91. }
  92. } catch (\Exception $e) {
  93. // clean up
  94. $this->filesystem->removeDirectory($path);
  95. throw $e;
  96. }
  97. }
  98. /**
  99. * {@inheritDoc}
  100. */
  101. public function update(PackageInterface $initial, PackageInterface $target, $path)
  102. {
  103. $this->remove($initial, $path);
  104. $this->download($target, $path);
  105. }
  106. /**
  107. * {@inheritDoc}
  108. */
  109. public function remove(PackageInterface $package, $path)
  110. {
  111. $this->io->write(" - Removing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
  112. if (!$this->filesystem->removeDirectory($path)) {
  113. throw new \RuntimeException('Could not completely delete '.$path.', aborting.');
  114. }
  115. }
  116. /**
  117. * Gets file name for specific package
  118. *
  119. * @param PackageInterface $package package instance
  120. * @param string $path download path
  121. * @return string file name
  122. */
  123. protected function getFileName(PackageInterface $package, $path)
  124. {
  125. return $path.'/'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_BASENAME);
  126. }
  127. /**
  128. * Process the download url
  129. *
  130. * @param PackageInterface $package package the url is coming from
  131. * @param string $url download url
  132. * @return string url
  133. *
  134. * @throws \RuntimeException If any problem with the url
  135. */
  136. protected function processUrl(PackageInterface $package, $url)
  137. {
  138. if (!extension_loaded('openssl') && 0 === strpos($url, 'https:')) {
  139. throw new \RuntimeException('You must enable the openssl extension to download files via https');
  140. }
  141. return $url;
  142. }
  143. }