Updater.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  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 Composer\Package\AliasPackage;
  13. use Composer\Package\PackageInterface;
  14. use Composer\Repository\RepositoryInterface;
  15. use Composer\Util\ErrorHandler;
  16. use Packagist\WebBundle\Entity\Author;
  17. use Packagist\WebBundle\Entity\Package;
  18. use Packagist\WebBundle\Entity\Tag;
  19. use Packagist\WebBundle\Entity\Version;
  20. use Packagist\WebBundle\Entity\SuggestLink;
  21. use Symfony\Bridge\Doctrine\RegistryInterface;
  22. /**
  23. * @author Jordi Boggiano <j.boggiano@seld.be>
  24. */
  25. class Updater
  26. {
  27. const UPDATE_TAGS = 1;
  28. const DELETE_BEFORE = 2;
  29. /**
  30. * Doctrine
  31. * @var RegistryInterface
  32. */
  33. protected $doctrine;
  34. /**
  35. * Supported link types
  36. * @var array
  37. */
  38. protected $supportedLinkTypes = array(
  39. 'require' => array(
  40. 'method' => 'getRequires',
  41. 'entity' => 'RequireLink',
  42. ),
  43. 'conflict' => array(
  44. 'method' => 'getConflicts',
  45. 'entity' => 'ConflictLink',
  46. ),
  47. 'provide' => array(
  48. 'method' => 'getProvides',
  49. 'entity' => 'ProvideLink',
  50. ),
  51. 'replace' => array(
  52. 'method' => 'getReplaces',
  53. 'entity' => 'ReplaceLink',
  54. ),
  55. 'devRequire' => array(
  56. 'method' => 'getDevRequires',
  57. 'entity' => 'DevRequireLink',
  58. ),
  59. );
  60. /**
  61. * Constructor
  62. *
  63. * @param RegistryInterface $doctrine
  64. */
  65. public function __construct(RegistryInterface $doctrine)
  66. {
  67. $this->doctrine = $doctrine;
  68. ErrorHandler::register();
  69. }
  70. /**
  71. * Update a project
  72. *
  73. * @param \Packagist\WebBundle\Entity\Package $package
  74. * @param RepositoryInterface $repository the repository instance used to update from
  75. * @param int $flags a few of the constants of this class
  76. * @param \DateTime $start
  77. */
  78. public function update(Package $package, RepositoryInterface $repository, $flags = 0, \DateTime $start = null)
  79. {
  80. $blacklist = '{^symfony/symfony (2.0.[456]|dev-charset|dev-console)}i';
  81. if (null === $start) {
  82. $start = new \DateTime();
  83. }
  84. $pruneDate = clone $start;
  85. $pruneDate->modify('-8days');
  86. $versions = $repository->getPackages();
  87. $em = $this->doctrine->getEntityManager();
  88. usort($versions, function ($a, $b) {
  89. $aVersion = $a->getVersion();
  90. $bVersion = $b->getVersion();
  91. if ($aVersion === '9999999-dev' || 'dev-' === substr($aVersion, 0, 4)) {
  92. $aVersion = 'dev';
  93. }
  94. if ($bVersion === '9999999-dev' || 'dev-' === substr($bVersion, 0, 4)) {
  95. $bVersion = 'dev';
  96. }
  97. if ($aVersion === $bVersion) {
  98. return $a->getReleaseDate() > $b->getReleaseDate() ? 1 : -1;
  99. }
  100. return version_compare($a->getVersion(), $b->getVersion());
  101. });
  102. $versionRepository = $this->doctrine->getRepository('PackagistWebBundle:Version');
  103. if ($flags & self::DELETE_BEFORE) {
  104. foreach ($package->getVersions() as $version) {
  105. $versionRepository->remove($version);
  106. }
  107. $em->flush();
  108. $em->refresh($package);
  109. }
  110. foreach ($versions as $version) {
  111. if ($version instanceof AliasPackage) {
  112. continue;
  113. }
  114. if (preg_match($blacklist, $version->getName().' '.$version->getPrettyVersion())) {
  115. continue;
  116. }
  117. $this->updateInformation($package, $version, $flags);
  118. $em->flush();
  119. }
  120. // remove outdated versions
  121. foreach ($package->getVersions() as $version) {
  122. if ($version->getUpdatedAt() < $pruneDate) {
  123. $versionRepository->remove($version);
  124. }
  125. }
  126. $package->setUpdatedAt(new \DateTime);
  127. $package->setCrawledAt(new \DateTime);
  128. $em->flush();
  129. }
  130. private function updateInformation(Package $package, PackageInterface $data, $flags)
  131. {
  132. $em = $this->doctrine->getEntityManager();
  133. $version = new Version();
  134. $version->setNormalizedVersion($data->getVersion());
  135. // check if we have that version yet
  136. foreach ($package->getVersions() as $existingVersion) {
  137. if ($existingVersion->getNormalizedVersion() === $version->getNormalizedVersion()) {
  138. if ($existingVersion->getDevelopment() || ($flags & self::UPDATE_TAGS)) {
  139. $version = $existingVersion;
  140. break;
  141. }
  142. // mark it updated to avoid it being pruned
  143. $existingVersion->setUpdatedAt(new \DateTime);
  144. return;
  145. }
  146. }
  147. $version->setName($package->getName());
  148. $version->setVersion($data->getPrettyVersion());
  149. $version->setDevelopment($data->isDev());
  150. $em->persist($version);
  151. $version->setDescription($data->getDescription());
  152. $package->setDescription($data->getDescription());
  153. $version->setHomepage($data->getHomepage());
  154. $version->setLicense($data->getLicense() ?: array());
  155. $version->setPackage($package);
  156. $version->setUpdatedAt(new \DateTime);
  157. $version->setReleasedAt($data->getReleaseDate());
  158. if ($data->getSourceType()) {
  159. $source['type'] = $data->getSourceType();
  160. $source['url'] = $data->getSourceUrl();
  161. $source['reference'] = $data->getSourceReference();
  162. $version->setSource($source);
  163. }
  164. if ($data->getDistType()) {
  165. $dist['type'] = $data->getDistType();
  166. $dist['url'] = $data->getDistUrl();
  167. $dist['reference'] = $data->getDistReference();
  168. $dist['shasum'] = $data->getDistSha1Checksum();
  169. $version->setDist($dist);
  170. }
  171. if ($data->getType()) {
  172. $version->setType($data->getType());
  173. if ($data->getType() && $data->getType() !== $package->getType()) {
  174. $package->setType($data->getType());
  175. }
  176. }
  177. $version->setTargetDir($data->getTargetDir());
  178. $version->setAutoload($data->getAutoload());
  179. $version->setExtra($data->getExtra());
  180. $version->setBinaries($data->getBinaries());
  181. $version->setIncludePaths($data->getIncludePaths());
  182. $version->setSupport($data->getSupport());
  183. $version->getTags()->clear();
  184. if ($data->getKeywords()) {
  185. foreach ($data->getKeywords() as $keyword) {
  186. $tag = Tag::getByName($em, $keyword, true);
  187. if (!$version->getTags()->contains($tag)) {
  188. $version->addTag($tag);
  189. }
  190. }
  191. }
  192. $authorRepository = $this->doctrine->getRepository('PackagistWebBundle:Author');
  193. $version->getAuthors()->clear();
  194. if ($data->getAuthors()) {
  195. foreach ($data->getAuthors() as $authorData) {
  196. $author = null;
  197. // skip authors with no information
  198. if (empty($authorData['email']) && empty($authorData['name'])) {
  199. continue;
  200. }
  201. if (!empty($authorData['email'])) {
  202. $author = $authorRepository->findOneByEmail($authorData['email']);
  203. }
  204. if (!$author && !empty($authorData['homepage'])) {
  205. $author = $authorRepository->findOneBy(array(
  206. 'name' => $authorData['name'],
  207. 'homepage' => $authorData['homepage']
  208. ));
  209. }
  210. if (!$author && !empty($authorData['name'])) {
  211. $author = $authorRepository->findOneByNameAndPackage($authorData['name'], $package);
  212. }
  213. if (!$author) {
  214. $author = new Author();
  215. $em->persist($author);
  216. }
  217. foreach (array('email', 'name', 'homepage', 'role') as $field) {
  218. if (isset($authorData[$field])) {
  219. $author->{'set'.$field}($authorData[$field]);
  220. }
  221. }
  222. $author->setUpdatedAt(new \DateTime);
  223. if (!$version->getAuthors()->contains($author)) {
  224. $version->addAuthor($author);
  225. }
  226. if (!$author->getVersions()->contains($version)) {
  227. $author->addVersion($version);
  228. }
  229. }
  230. }
  231. // handle links
  232. foreach ($this->supportedLinkTypes as $linkType => $opts) {
  233. $links = array();
  234. foreach ($data->{$opts['method']}() as $link) {
  235. $links[$link->getTarget()] = $link->getPrettyConstraint();
  236. }
  237. foreach ($version->{'get'.$linkType}() as $link) {
  238. // clear links that have changed/disappeared (for updates)
  239. if (!isset($links[$link->getPackageName()]) || $links[$link->getPackageName()] !== $link->getPackageVersion()) {
  240. $version->{'get'.$linkType}()->removeElement($link);
  241. $em->remove($link);
  242. } else {
  243. // clear those that are already set
  244. unset($links[$link->getPackageName()]);
  245. }
  246. }
  247. foreach ($links as $linkPackageName => $linkPackageVersion) {
  248. $class = 'Packagist\WebBundle\Entity\\'.$opts['entity'];
  249. $link = new $class;
  250. $link->setPackageName($linkPackageName);
  251. $link->setPackageVersion($linkPackageVersion);
  252. $version->{'add'.$linkType.'Link'}($link);
  253. $link->setVersion($version);
  254. $em->persist($link);
  255. }
  256. }
  257. // handle suggests
  258. if ($suggests = $data->getSuggests()) {
  259. foreach ($version->getSuggest() as $link) {
  260. // clear links that have changed/disappeared (for updates)
  261. if (!isset($suggests[$link->getPackageName()]) || $suggests[$link->getPackageName()] !== $link->getPackageVersion()) {
  262. $version->getSuggest()->removeElement($link);
  263. $em->remove($link);
  264. } else {
  265. // clear those that are already set
  266. unset($suggests[$link->getPackageName()]);
  267. }
  268. }
  269. foreach ($suggests as $linkPackageName => $linkPackageVersion) {
  270. $link = new SuggestLink;
  271. $link->setPackageName($linkPackageName);
  272. $link->setPackageVersion($linkPackageVersion);
  273. $version->addSuggestLink($link);
  274. $link->setVersion($version);
  275. $em->persist($link);
  276. }
  277. }
  278. if (!$package->getVersions()->contains($version)) {
  279. $package->addVersions($version);
  280. }
  281. }
  282. }