ZipDownloader.php 3.7 KB

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