ArtifactRepository.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  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\Repository;
  12. use Composer\IO\IOInterface;
  13. use Composer\Json\JsonFile;
  14. use Composer\Package\Loader\ArrayLoader;
  15. use Composer\Package\Loader\LoaderInterface;
  16. /**
  17. * @author Serge Smertin <serg.smertin@gmail.com>
  18. */
  19. class ArtifactRepository extends ArrayRepository implements ConfigurableRepositoryInterface
  20. {
  21. /** @var LoaderInterface */
  22. protected $loader;
  23. protected $lookup;
  24. protected $repoConfig;
  25. public function __construct(array $repoConfig, IOInterface $io)
  26. {
  27. if (!extension_loaded('zip')) {
  28. throw new \RuntimeException('The artifact repository requires PHP\'s zip extension');
  29. }
  30. $this->loader = new ArrayLoader();
  31. $this->lookup = $repoConfig['url'];
  32. $this->io = $io;
  33. $this->repoConfig = $repoConfig;
  34. }
  35. public function getRepoConfig()
  36. {
  37. return $this->repoConfig;
  38. }
  39. protected function initialize()
  40. {
  41. parent::initialize();
  42. $this->scanDirectory($this->lookup);
  43. }
  44. private function scanDirectory($path)
  45. {
  46. $io = $this->io;
  47. $directory = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS);
  48. $iterator = new \RecursiveIteratorIterator($directory);
  49. $regex = new \RegexIterator($iterator, '/^.+\.(zip|phar)$/i');
  50. foreach ($regex as $file) {
  51. /* @var $file \SplFileInfo */
  52. if (!$file->isFile()) {
  53. continue;
  54. }
  55. $package = $this->getComposerInformation($file);
  56. if (!$package) {
  57. if ($io->isVerbose()) {
  58. $io->writeError("File <comment>{$file->getBasename()}</comment> doesn't seem to hold a package");
  59. }
  60. continue;
  61. }
  62. if ($io->isVerbose()) {
  63. $template = 'Found package <info>%s</info> (<comment>%s</comment>) in file <info>%s</info>';
  64. $io->writeError(sprintf($template, $package->getName(), $package->getPrettyVersion(), $file->getBasename()));
  65. }
  66. $this->addPackage($package);
  67. }
  68. }
  69. /**
  70. * Find a file by name, returning the one that has the shortest path.
  71. *
  72. * @param \ZipArchive $zip
  73. * @param $filename
  74. * @return bool|int
  75. */
  76. private function locateFile(\ZipArchive $zip, $filename)
  77. {
  78. $indexOfShortestMatch = false;
  79. $lengthOfShortestMatch = -1;
  80. for ($i = 0; $i < $zip->numFiles; $i++) {
  81. $stat = $zip->statIndex($i);
  82. if (strcmp(basename($stat['name']), $filename) === 0) {
  83. $directoryName = dirname($stat['name']);
  84. if ($directoryName == '.') {
  85. //if composer.json is in root directory
  86. //it has to be the one to use.
  87. return $i;
  88. }
  89. if (strpos($directoryName, '\\') !== false ||
  90. strpos($directoryName, '/') !== false) {
  91. //composer.json files below first directory are rejected
  92. continue;
  93. }
  94. $length = strlen($stat['name']);
  95. if ($indexOfShortestMatch == false || $length < $lengthOfShortestMatch) {
  96. //Check it's not a directory.
  97. $contents = $zip->getFromIndex($i);
  98. if ($contents !== false) {
  99. $indexOfShortestMatch = $i;
  100. $lengthOfShortestMatch = $length;
  101. }
  102. }
  103. }
  104. }
  105. return $indexOfShortestMatch;
  106. }
  107. private function getComposerInformation(\SplFileInfo $file)
  108. {
  109. $zip = new \ZipArchive();
  110. $zip->open($file->getPathname());
  111. if (0 == $zip->numFiles) {
  112. return false;
  113. }
  114. $foundFileIndex = $this->locateFile($zip, 'composer.json');
  115. if (false === $foundFileIndex) {
  116. return false;
  117. }
  118. $configurationFileName = $zip->getNameIndex($foundFileIndex);
  119. $composerFile = "zip://{$file->getPathname()}#$configurationFileName";
  120. $json = file_get_contents($composerFile);
  121. $package = JsonFile::parseJson($json, $composerFile);
  122. $package['dist'] = array(
  123. 'type' => 'zip',
  124. 'url' => $file->getPathname(),
  125. 'shasum' => sha1_file($file->getRealPath()),
  126. );
  127. $package = $this->loader->load($package);
  128. return $package;
  129. }
  130. }