Package.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  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\Entity;
  12. use Doctrine\ORM\Mapping as ORM;
  13. use Symfony\Component\Validator\Constraints as Assert;
  14. use Symfony\Component\Validator\ExecutionContext;
  15. use Doctrine\Common\Collections\ArrayCollection;
  16. use Composer\IO\NullIO;
  17. use Composer\Factory;
  18. use Composer\Repository\VcsRepository;
  19. use Composer\Repository\RepositoryManager;
  20. /**
  21. * @ORM\Entity(repositoryClass="Packagist\WebBundle\Entity\PackageRepository")
  22. * @ORM\Table(
  23. * name="package",
  24. * uniqueConstraints={@ORM\UniqueConstraint(name="name_idx", columns={"name"})},
  25. * indexes={
  26. * @ORM\Index(name="indexed_idx",columns={"indexedAt"}),
  27. * @ORM\Index(name="crawled_idx",columns={"crawledAt"}),
  28. * @ORM\Index(name="dumped_idx",columns={"dumpedAt"})
  29. * }
  30. * )
  31. * @Assert\Callback(methods={"isPackageUnique","isRepositoryValid"})
  32. * @author Jordi Boggiano <j.boggiano@seld.be>
  33. */
  34. class Package
  35. {
  36. /**
  37. * @ORM\Id
  38. * @ORM\Column(type="integer")
  39. * @ORM\GeneratedValue(strategy="AUTO")
  40. */
  41. private $id;
  42. /**
  43. * Unique package name
  44. *
  45. * @ORM\Column()
  46. */
  47. private $name;
  48. /**
  49. * @ORM\Column(nullable=true)
  50. */
  51. private $type;
  52. /**
  53. * @ORM\Column(type="text", nullable=true)
  54. */
  55. private $description;
  56. /**
  57. * @ORM\OneToMany(targetEntity="Packagist\WebBundle\Entity\Version", mappedBy="package")
  58. */
  59. private $versions;
  60. /**
  61. * @ORM\ManyToMany(targetEntity="User", inversedBy="packages")
  62. * @ORM\JoinTable(name="maintainers_packages")
  63. */
  64. private $maintainers;
  65. /**
  66. * @ORM\Column()
  67. * @Assert\NotBlank()
  68. */
  69. private $repository;
  70. // dist-tags / rel or runtime?
  71. /**
  72. * @ORM\Column(type="datetime")
  73. */
  74. private $createdAt;
  75. /**
  76. * @ORM\Column(type="datetime", nullable=true)
  77. */
  78. private $updatedAt;
  79. /**
  80. * @ORM\Column(type="datetime", nullable=true)
  81. */
  82. private $crawledAt;
  83. /**
  84. * @ORM\Column(type="datetime", nullable=true)
  85. */
  86. private $indexedAt;
  87. /**
  88. * @ORM\Column(type="datetime", nullable=true)
  89. */
  90. private $dumpedAt;
  91. /**
  92. * @ORM\Column(type="boolean")
  93. */
  94. private $autoUpdated = false;
  95. private $entityRepository;
  96. private $repositoryClass;
  97. public function __construct()
  98. {
  99. $this->versions = new ArrayCollection();
  100. $this->createdAt = new \DateTime;
  101. }
  102. public function toArray()
  103. {
  104. $versions = array();
  105. foreach ($this->getVersions() as $version) {
  106. $versions[$version->getVersion()] = $version->toArray();
  107. }
  108. $maintainers = array();
  109. foreach ($this->getMaintainers() as $maintainer) {
  110. $maintainers[] = $maintainer->toArray();
  111. }
  112. $data = array(
  113. 'name' => $this->getName(),
  114. 'description' => $this->getDescription(),
  115. 'maintainers' => $maintainers,
  116. 'versions' => $versions,
  117. 'type' => $this->getType(),
  118. 'repository' => $this->getRepository()
  119. );
  120. return $data;
  121. }
  122. public function isRepositoryValid(ExecutionContext $context)
  123. {
  124. $property = 'repository';
  125. $repo = $this->repositoryClass;
  126. if (!$repo) {
  127. if (preg_match('{//.+@}', $this->repository)) {
  128. $context->addViolationAtSubPath($property, 'URLs with user@host are not supported, use a read-only public URL', array(), null);
  129. } else {
  130. $context->addViolationAtSubPath($property, 'No valid/supported repository was found at the given URL', array(), null);
  131. }
  132. return;
  133. }
  134. try {
  135. $information = $repo->getComposerInformation($repo->getRootIdentifier());
  136. if (!isset($information['name']) || !$information['name']) {
  137. $context->addViolationAtSubPath($property, 'The package name was not found in the composer.json, make sure there is a name present.', array(), null);
  138. return;
  139. }
  140. if (!preg_match('{^[a-z0-9_.-]+/[a-z0-9_.-]+$}i', $information['name'])) {
  141. $context->addViolationAtSubPath($property, 'The package name '.$information['name'].' is invalid, it should have a vendor name, a forward slash, and a package name, matching <em>[a-z0-9_.-]+/[a-z0-9_.-]+</em>.', array(), null);
  142. return;
  143. }
  144. } catch (\Exception $e) {
  145. $context->addViolationAtSubPath($property, 'We had problems parsing your composer.json file, the parser reports: '.$e->getMessage(), array(), null);
  146. }
  147. }
  148. public function setEntityRepository($repository)
  149. {
  150. $this->entityRepository = $repository;
  151. }
  152. public function isPackageUnique(ExecutionContext $context)
  153. {
  154. try {
  155. if ($this->entityRepository->findOneByName($this->name)) {
  156. $context->addViolationAtSubPath('repository', 'A package with the name '.$this->name.' already exists.', array(), null);
  157. }
  158. } catch (\Doctrine\ORM\NoResultException $e) {}
  159. }
  160. /**
  161. * Get id
  162. *
  163. * @return string
  164. */
  165. public function getId()
  166. {
  167. return $this->id;
  168. }
  169. /**
  170. * Set name
  171. *
  172. * @param string $name
  173. */
  174. public function setName($name)
  175. {
  176. $this->name = $name;
  177. }
  178. /**
  179. * Get name
  180. *
  181. * @return string
  182. */
  183. public function getName()
  184. {
  185. return $this->name;
  186. }
  187. /**
  188. * Get vendor prefix
  189. *
  190. * @return string
  191. */
  192. public function getVendor()
  193. {
  194. return preg_replace('{/.*$}', '', $this->name);
  195. }
  196. /**
  197. * Get package name without vendor
  198. *
  199. * @return string
  200. */
  201. public function getPackageName()
  202. {
  203. return preg_replace('{^[^/]*/}', '', $this->name);
  204. }
  205. /**
  206. * Set description
  207. *
  208. * @param string $description
  209. */
  210. public function setDescription($description)
  211. {
  212. $this->description = $description;
  213. }
  214. /**
  215. * Get description
  216. *
  217. * @return string
  218. */
  219. public function getDescription()
  220. {
  221. return $this->description;
  222. }
  223. /**
  224. * Set createdAt
  225. *
  226. * @param \DateTime $createdAt
  227. */
  228. public function setCreatedAt($createdAt)
  229. {
  230. $this->createdAt = $createdAt;
  231. }
  232. /**
  233. * Get createdAt
  234. *
  235. * @return \DateTime
  236. */
  237. public function getCreatedAt()
  238. {
  239. return $this->createdAt;
  240. }
  241. /**
  242. * Set repository
  243. *
  244. * @param string $repository
  245. */
  246. public function setRepository($repository)
  247. {
  248. $this->repository = $repository;
  249. // avoid user@host URLs
  250. if (preg_match('{//.+@}', $repository)) {
  251. return;
  252. }
  253. try {
  254. $config = Factory::createConfig();
  255. $repository = new VcsRepository(array('url' => $repository), new NullIO(), $config);
  256. $repo = $this->repositoryClass = $repository->getDriver();
  257. if (!$repo) {
  258. return;
  259. }
  260. $information = $repo->getComposerInformation($repo->getRootIdentifier());
  261. $this->setName($information['name']);
  262. } catch (\Exception $e) {
  263. }
  264. }
  265. /**
  266. * Get repository
  267. *
  268. * @return string $repository
  269. */
  270. public function getRepository()
  271. {
  272. return $this->repository;
  273. }
  274. /**
  275. * Add versions
  276. *
  277. * @param \Packagist\WebBundle\Entity\Version $versions
  278. */
  279. public function addVersions(Version $versions)
  280. {
  281. $this->versions[] = $versions;
  282. }
  283. /**
  284. * Get versions
  285. *
  286. * @return \Doctrine\Common\Collections\Collection
  287. */
  288. public function getVersions()
  289. {
  290. return $this->versions;
  291. }
  292. /**
  293. * Set updatedAt
  294. *
  295. * @param \DateTime $updatedAt
  296. */
  297. public function setUpdatedAt($updatedAt)
  298. {
  299. $this->updatedAt = $updatedAt;
  300. }
  301. /**
  302. * Get updatedAt
  303. *
  304. * @return \DateTime
  305. */
  306. public function getUpdatedAt()
  307. {
  308. return $this->updatedAt;
  309. }
  310. /**
  311. * Set crawledAt
  312. *
  313. * @param \DateTime $crawledAt
  314. */
  315. public function setCrawledAt($crawledAt)
  316. {
  317. $this->crawledAt = $crawledAt;
  318. }
  319. /**
  320. * Get crawledAt
  321. *
  322. * @return \DateTime
  323. */
  324. public function getCrawledAt()
  325. {
  326. return $this->crawledAt;
  327. }
  328. /**
  329. * Set indexedAt
  330. *
  331. * @param \DateTime $indexedAt
  332. */
  333. public function setIndexedAt($indexedAt)
  334. {
  335. $this->indexedAt = $indexedAt;
  336. }
  337. /**
  338. * Get indexedAt
  339. *
  340. * @return \DateTime
  341. */
  342. public function getIndexedAt()
  343. {
  344. return $this->indexedAt;
  345. }
  346. /**
  347. * Set dumpedAt
  348. *
  349. * @param \DateTime $dumpedAt
  350. */
  351. public function setDumpedAt($dumpedAt)
  352. {
  353. $this->dumpedAt = $dumpedAt;
  354. }
  355. /**
  356. * Get dumpedAt
  357. *
  358. * @return \DateTime
  359. */
  360. public function getDumpedAt()
  361. {
  362. return $this->dumpedAt;
  363. }
  364. /**
  365. * Add maintainers
  366. *
  367. * @param \Packagist\WebBundle\Entity\User $maintainer
  368. */
  369. public function addMaintainer(User $maintainer)
  370. {
  371. $this->maintainers[] = $maintainer;
  372. }
  373. /**
  374. * Get maintainers
  375. *
  376. * @return \Doctrine\Common\Collections\Collection
  377. */
  378. public function getMaintainers()
  379. {
  380. return $this->maintainers;
  381. }
  382. /**
  383. * Set type
  384. *
  385. * @param string $type
  386. */
  387. public function setType($type)
  388. {
  389. $this->type = $type;
  390. }
  391. /**
  392. * Get type
  393. *
  394. * @return string
  395. */
  396. public function getType()
  397. {
  398. return $this->type;
  399. }
  400. /**
  401. * Set autoUpdated
  402. *
  403. * @param Boolean $autoUpdated
  404. */
  405. public function setAutoUpdated($autoUpdated)
  406. {
  407. $this->autoUpdated = $autoUpdated;
  408. }
  409. /**
  410. * Get autoUpdated
  411. *
  412. * @return Boolean
  413. */
  414. public function isAutoUpdated()
  415. {
  416. return $this->autoUpdated;
  417. }
  418. }