Dumper.php 5.2 KB

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