ZipDownloader.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  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\Cache;
  14. use Composer\Util\ProcessExecutor;
  15. use Composer\IO\IOInterface;
  16. use ZipArchive;
  17. /**
  18. * @author Jordi Boggiano <j.boggiano@seld.be>
  19. */
  20. class ZipDownloader extends ArchiveDownloader
  21. {
  22. protected $process;
  23. public function __construct(IOInterface $io, Config $config, Cache $cache = null, ProcessExecutor $process = null)
  24. {
  25. $this->process = $process ?: new ProcessExecutor;
  26. parent::__construct($io, $config, $cache);
  27. }
  28. protected function extract($file, $path)
  29. {
  30. $processError = null;
  31. // try to use unzip on *nix
  32. if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
  33. $command = 'unzip '.escapeshellarg($file).' -d '.escapeshellarg($path);
  34. if (0 === $this->process->execute($command, $ignoredOutput)) {
  35. return;
  36. }
  37. $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput();
  38. }
  39. if (!class_exists('ZipArchive')) {
  40. // php.ini path is added to the error message to help users find the correct file
  41. $iniPath = php_ini_loaded_file();
  42. if ($iniPath) {
  43. $iniMessage = 'The php.ini used by your command-line PHP is: ' . $iniPath;
  44. } else {
  45. $iniMessage = 'A php.ini file does not exist. You will have to create one.';
  46. }
  47. $error = "Could not decompress the archive, enable the PHP zip extension or install unzip.\n".
  48. $iniMessage . "\n" . $processError;
  49. if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
  50. $error = "You need the zip extension enabled to use the ZipDownloader.\n".
  51. $iniMessage;
  52. }
  53. throw new \RuntimeException($error);
  54. }
  55. $zipArchive = new ZipArchive();
  56. if (true !== ($retval = $zipArchive->open($file))) {
  57. throw new \UnexpectedValueException($this->getErrorMessage($retval, $file));
  58. }
  59. if (true !== $zipArchive->extractTo($path)) {
  60. throw new \RuntimeException("There was an error extracting the ZIP file. Corrupt file?");
  61. }
  62. $zipArchive->close();
  63. }
  64. /**
  65. * Give a meaningful error message to the user.
  66. *
  67. * @param int $retval
  68. * @param string $file
  69. * @return string
  70. */
  71. protected function getErrorMessage($retval, $file)
  72. {
  73. switch ($retval) {
  74. case ZipArchive::ER_EXISTS:
  75. return sprintf("File '%s' already exists.", $file);
  76. case ZipArchive::ER_INCONS:
  77. return sprintf("Zip archive '%s' is inconsistent.", $file);
  78. case ZipArchive::ER_INVAL:
  79. return sprintf("Invalid argument (%s)", $file);
  80. case ZipArchive::ER_MEMORY:
  81. return sprintf("Malloc failure (%s)", $file);
  82. case ZipArchive::ER_NOENT:
  83. return sprintf("No such zip file: '%s'", $file);
  84. case ZipArchive::ER_NOZIP:
  85. return sprintf("'%s' is not a zip archive.", $file);
  86. case ZipArchive::ER_OPEN:
  87. return sprintf("Can't open zip file: %s", $file);
  88. case ZipArchive::ER_READ:
  89. return sprintf("Zip read error (%s)", $file);
  90. case ZipArchive::ER_SEEK:
  91. return sprintf("Zip seek error (%s)", $file);
  92. default:
  93. return sprintf("'%s' is not a valid zip archive, got error code: %s", $file, $retval);
  94. }
  95. }
  96. }