FilesystemRepository.php 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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\Json\JsonFile;
  13. use Composer\Package\Loader\ArrayLoader;
  14. use Composer\Package\RootPackageInterface;
  15. use Composer\Package\AliasPackage;
  16. use Composer\Package\Dumper\ArrayDumper;
  17. use Composer\Installer\InstallationManager;
  18. use Composer\Util\Filesystem;
  19. /**
  20. * Filesystem repository.
  21. *
  22. * @author Konstantin Kudryashov <ever.zet@gmail.com>
  23. * @author Jordi Boggiano <j.boggiano@seld.be>
  24. */
  25. class FilesystemRepository extends WritableArrayRepository
  26. {
  27. private $file;
  28. private $dumpVersions;
  29. private $rootPackage;
  30. /**
  31. * Initializes filesystem repository.
  32. *
  33. * @param JsonFile $repositoryFile repository json file
  34. * @param bool $dumpVersions
  35. * @param ?RootPackageInterface $rootPackage Must be provided if $dumpVersions is true
  36. */
  37. public function __construct(JsonFile $repositoryFile, $dumpVersions = false, RootPackageInterface $rootPackage = null)
  38. {
  39. parent::__construct();
  40. $this->file = $repositoryFile;
  41. $this->dumpVersions = $dumpVersions;
  42. $this->rootPackage = $rootPackage;
  43. if ($dumpVersions && !$rootPackage) {
  44. throw new \InvalidArgumentException('Expected a root package instance if $dumpVersions is true');
  45. }
  46. }
  47. /**
  48. * Initializes repository (reads file, or remote address).
  49. */
  50. protected function initialize()
  51. {
  52. parent::initialize();
  53. if (!$this->file->exists()) {
  54. return;
  55. }
  56. try {
  57. $data = $this->file->read();
  58. if (isset($data['packages'])) {
  59. $packages = $data['packages'];
  60. } else {
  61. $packages = $data;
  62. }
  63. // forward compatibility for composer v2 installed.json
  64. if (isset($packages['packages'])) {
  65. $packages = $packages['packages'];
  66. }
  67. if (!is_array($packages)) {
  68. throw new \UnexpectedValueException('Could not parse package list from the repository');
  69. }
  70. } catch (\Exception $e) {
  71. throw new InvalidRepositoryException('Invalid repository data in '.$this->file->getPath().', packages could not be loaded: ['.get_class($e).'] '.$e->getMessage());
  72. }
  73. $loader = new ArrayLoader(null, true);
  74. foreach ($packages as $packageData) {
  75. $package = $loader->load($packageData);
  76. $this->addPackage($package);
  77. }
  78. }
  79. public function reload()
  80. {
  81. $this->packages = null;
  82. $this->initialize();
  83. }
  84. /**
  85. * Writes writable repository.
  86. */
  87. public function write($devMode, InstallationManager $installationManager)
  88. {
  89. $data = array('packages' => array(), 'dev' => $devMode);
  90. $dumper = new ArrayDumper();
  91. $fs = new Filesystem();
  92. $repoDir = dirname($fs->normalizePath($this->file->getPath()));
  93. foreach ($this->getCanonicalPackages() as $package) {
  94. $pkgArray = $dumper->dump($package);
  95. $path = $installationManager->getInstallPath($package);
  96. $pkgArray['install-path'] = ('' !== $path && null !== $path) ? $fs->findShortestPath($repoDir, $fs->isAbsolutePath($path) ? $path : getcwd() . '/' . $path, true) : null;
  97. $data['packages'][] = $pkgArray;
  98. }
  99. usort($data['packages'], function ($a, $b) {
  100. return strcmp($a['name'], $b['name']);
  101. });
  102. $this->file->write($data);
  103. if ($this->dumpVersions) {
  104. $versions = array('versions' => array());
  105. $packages = $this->getPackages();
  106. $packages[] = $rootPackage = $this->rootPackage;
  107. while ($rootPackage instanceof AliasPackage) {
  108. $rootPackage = $rootPackage->getAliasOf();
  109. $packages[] = $rootPackage;
  110. }
  111. // add real installed packages
  112. foreach ($packages as $package) {
  113. if ($package instanceof AliasPackage) {
  114. continue;
  115. }
  116. $reference = null;
  117. if ($package->getInstallationSource()) {
  118. $reference = $package->getInstallationSource() === 'source' ? $package->getSourceReference() : $package->getDistReference();
  119. }
  120. if (null === $reference) {
  121. $reference = ($package->getSourceReference() ?: $package->getDistReference()) ?: null;
  122. }
  123. $versions['versions'][$package->getName()] = array(
  124. 'pretty_version' => $package->getPrettyVersion(),
  125. 'version' => $package->getVersion(),
  126. 'aliases' => array(),
  127. 'reference' => $reference,
  128. );
  129. if ($package instanceof RootPackageInterface) {
  130. $versions['root'] = $versions['versions'][$package->getName()];
  131. $versions['root']['name'] = $package->getName();
  132. }
  133. }
  134. // add provided/replaced packages
  135. foreach ($packages as $package) {
  136. foreach ($package->getReplaces() as $replace) {
  137. $replaced = $replace->getPrettyConstraint();
  138. if ($replaced === 'self.version') {
  139. $replaced = $package->getPrettyVersion();
  140. }
  141. if (!isset($versions['versions'][$replace->getTarget()]['replaced']) || !in_array($replaced, $versions['versions'][$replace->getTarget()]['replaced'], true)) {
  142. $versions['versions'][$replace->getTarget()]['replaced'][] = $replaced;
  143. }
  144. }
  145. foreach ($package->getProvides() as $provide) {
  146. $provided = $provide->getPrettyConstraint();
  147. if ($provided === 'self.version') {
  148. $provided = $package->getPrettyVersion();
  149. }
  150. if (!isset($versions['versions'][$provide->getTarget()]['provided']) || !in_array($provided, $versions['versions'][$provide->getTarget()]['provided'], true)) {
  151. $versions['versions'][$provide->getTarget()]['provided'][] = $provided;
  152. }
  153. }
  154. }
  155. // add aliases
  156. foreach ($packages as $package) {
  157. if (!$package instanceof AliasPackage) {
  158. continue;
  159. }
  160. $versions['versions'][$package->getName()]['aliases'][] = $package->getPrettyVersion();
  161. if ($package instanceof RootPackageInterface) {
  162. $versions['root']['aliases'][] = $package->getPrettyVersion();
  163. }
  164. }
  165. ksort($versions['versions']);
  166. ksort($versions);
  167. $fs->filePutContentsIfModified($repoDir.'/installed.php', '<?php return '.var_export($versions, true).';'."\n");
  168. $installedVersionsClass = file_get_contents(__DIR__.'/../InstalledVersions.php');
  169. $installedVersionsClass = str_replace('private static $installed;', 'private static $installed = '.var_export($versions, true).';', $installedVersionsClass);
  170. $fs->filePutContentsIfModified($repoDir.'/InstalledVersions.php', $installedVersionsClass);
  171. }
  172. }
  173. }