ZipDownloader.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  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 = "Could not decompress the archive, enable the PHP zip extension.\n" . $iniMessage;
  51. }
  52. throw new \RuntimeException($error);
  53. }
  54. $zipArchive = new ZipArchive();
  55. if (true !== ($retval = $zipArchive->open($file))) {
  56. throw new \UnexpectedValueException($this->getErrorMessage($retval, $file));
  57. }
  58. if (true !== $zipArchive->extractTo($path)) {
  59. throw new \RuntimeException("There was an error extracting the ZIP file. Corrupt file?");
  60. }
  61. $zipArchive->close();
  62. }
  63. /**
  64. * Give a meaningful error message to the user.
  65. *
  66. * @param int $retval
  67. * @param string $file
  68. * @return string
  69. */
  70. protected function getErrorMessage($retval, $file)
  71. {
  72. switch ($retval) {
  73. case ZipArchive::ER_EXISTS:
  74. return sprintf("File '%s' already exists.", $file);
  75. case ZipArchive::ER_INCONS:
  76. return sprintf("Zip archive '%s' is inconsistent.", $file);
  77. case ZipArchive::ER_INVAL:
  78. return sprintf("Invalid argument (%s)", $file);
  79. case ZipArchive::ER_MEMORY:
  80. return sprintf("Malloc failure (%s)", $file);
  81. case ZipArchive::ER_NOENT:
  82. return sprintf("No such zip file: '%s'", $file);
  83. case ZipArchive::ER_NOZIP:
  84. return sprintf("'%s' is not a zip archive.", $file);
  85. case ZipArchive::ER_OPEN:
  86. return sprintf("Can't open zip file: %s", $file);
  87. case ZipArchive::ER_READ:
  88. return sprintf("Zip read error (%s)", $file);
  89. case ZipArchive::ER_SEEK:
  90. return sprintf("Zip seek error (%s)", $file);
  91. default:
  92. return sprintf("'%s' is not a valid zip archive, got error code: %s", $file, $retval);
  93. }
  94. }
  95. }