Dumper.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <?php
  2. /*
  3. * This file is part of Packagist.
  4. *
  5. * (c) Jordi Boggiano <j.boggiano@seld.be>
  6. * Nils Adermann <naderman@naderman.de>
  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 Packagist\WebBundle\Package;
  12. use Symfony\Component\Filesystem\Filesystem;
  13. use Symfony\Bridge\Doctrine\RegistryInterface;
  14. use Symfony\Component\Routing\RouterInterface;
  15. use Packagist\WebBundle\Entity\Version;
  16. /**
  17. * @author Jordi Boggiano <j.boggiano@seld.be>
  18. */
  19. class Dumper
  20. {
  21. /**
  22. * Doctrine
  23. * @var RegistryInterface
  24. */
  25. protected $doctrine;
  26. /**
  27. * @var Filesystem
  28. */
  29. protected $fs;
  30. /**
  31. * @var string
  32. */
  33. protected $webDir;
  34. /**
  35. * @var string
  36. */
  37. protected $buildDir;
  38. /**
  39. * @var RouterInterface
  40. */
  41. protected $router;
  42. /**
  43. * Data cache
  44. * @var array
  45. */
  46. private $files = array();
  47. /**
  48. * Constructor
  49. *
  50. * @param RegistryInterface $doctrine
  51. * @param string $webDir web root
  52. * @param string $cacheDir cache dir
  53. */
  54. public function __construct(RegistryInterface $doctrine, Filesystem $filesystem, RouterInterface $router, $webDir, $cacheDir)
  55. {
  56. $this->doctrine = $doctrine;
  57. $this->fs = $filesystem;
  58. $this->router = $router;
  59. $this->webDir = realpath($webDir);
  60. $this->buildDir = $cacheDir . '/composer-packages-build';
  61. }
  62. /**
  63. * Dump a set of packages to the web root
  64. *
  65. * @param array $packages
  66. * @param Boolean $force
  67. */
  68. public function dump(array $packages, $force = false)
  69. {
  70. // prepare build dir
  71. $webDir = $this->webDir;
  72. $buildDir = $this->buildDir;
  73. $this->fs->remove($buildDir);
  74. $this->fs->mkdir($buildDir);
  75. if (!$force) {
  76. foreach (glob($webDir.'/packages*.json') as $file) {
  77. copy($file, $buildDir.'/'.basename($file));
  78. }
  79. }
  80. $modifiedFiles = array();
  81. // prepare packages in memory
  82. foreach ($packages as $package) {
  83. // clean up all versions of that package
  84. foreach (glob($buildDir.'/packages*.json') as $file) {
  85. $key = basename($file);
  86. $this->loadFile($file);
  87. if (isset($this->files[$key]['packages'][$package->getName()])) {
  88. unset($this->files[$key]['packages'][$package->getName()]);
  89. $modifiedFiles[$key] = true;
  90. }
  91. }
  92. // (re)write versions
  93. foreach ($package->getVersions() as $version) {
  94. $file = $buildDir.'/'.$this->getTargetFile($version);
  95. $modifiedFiles[basename($file)] = true;
  96. $this->dumpVersion($version, $file);
  97. }
  98. $package->setDumpedAt(new \DateTime);
  99. }
  100. // prepare root file
  101. $rootFile = $buildDir.'/packages.json';
  102. $this->loadFile($rootFile);
  103. if (!isset($this->files['packages.json']['packages'])) {
  104. $this->files['packages.json']['packages'] = array();
  105. }
  106. $url = $this->router->generate('track_download', array('name' => 'VND/PKG'));
  107. $this->files['packages.json']['notify'] = str_replace('VND/PKG', '%package%', $url);
  108. // dump files to build dir
  109. foreach ($modifiedFiles as $file => $dummy) {
  110. $this->dumpFile($buildDir.'/'.$file);
  111. $this->files['packages.json']['includes'][$file] = array('sha1' => sha1_file($buildDir.'/'.$file));
  112. }
  113. $this->dumpFile($rootFile);
  114. // put the new files in production
  115. foreach ($modifiedFiles as $file => $dummy) {
  116. rename($buildDir.'/'.$file, $webDir.'/'.$file);
  117. }
  118. rename($rootFile, $webDir.'/'.basename($rootFile));
  119. if ($force) {
  120. // clear files that were not created in this build
  121. foreach (glob($webDir.'/packages-*.json') as $file) {
  122. if (!isset($modifiedFiles[basename($file)])) {
  123. unlink($file);
  124. }
  125. }
  126. }
  127. // update dump dates
  128. $this->doctrine->getEntityManager()->flush();
  129. }
  130. private function loadFile($file)
  131. {
  132. $key = basename($file);
  133. if (isset($this->files[$key])) {
  134. return;
  135. }
  136. if (file_exists($file)) {
  137. $this->files[$key] = json_decode(file_get_contents($file), true);
  138. } else {
  139. $this->files[$key] = array();
  140. }
  141. }
  142. private function dumpFile($file)
  143. {
  144. $key = basename($file);
  145. // sort all versions and packages to make sha1 consistent
  146. ksort($this->files[$key]['packages']);
  147. foreach ($this->files[$key]['packages'] as $package => $versions) {
  148. ksort($this->files[$key]['packages'][$package]);
  149. }
  150. file_put_contents($file, json_encode($this->files[$key]));
  151. }
  152. private function dumpVersion(Version $version, $file)
  153. {
  154. $this->loadFile($file);
  155. $this->files[basename($file)]['packages'][$version->getName()][$version->getVersion()] = $version->toArray();
  156. }
  157. private function getTargetFile(Version $version)
  158. {
  159. if ($version->isDevelopment()) {
  160. $distribution = 16;
  161. return 'packages-dev-' . chr(abs(crc32($version->getName())) % $distribution + 97) . '.json';
  162. }
  163. $date = $version->getReleasedAt();
  164. return 'packages-' . ($date->format('Y') === date('Y') ? $date->format('Y-m') : $date->format('Y')) . '.json';
  165. }
  166. }