| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- <?php
- /*
- * This file is part of Composer.
- *
- * (c) Nils Adermann <naderman@naderman.de>
- * Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- namespace Composer\DependencyResolver;
- use Composer\DependencyResolver\Operation\OperationInterface;
- use Composer\Package\AliasPackage;
- use Composer\Package\RootAliasPackage;
- use Composer\Package\RootPackageInterface;
- use Composer\Repository\ArrayRepository;
- use Composer\Repository\RepositoryInterface;
- use Composer\Test\Repository\ArrayRepositoryTest;
- /**
- * @author Nils Adermann <naderman@naderman.de>
- */
- class LockTransaction
- {
- protected $policy;
- /** @var Pool */
- protected $pool;
- /**
- * packages in current lock file, platform repo or otherwise present
- * @var array
- */
- protected $presentMap;
- /**
- * Packages which cannot be mapped, platform repo, root package, other fixed repos
- * @var array
- */
- protected $unlockableMap;
- protected $decisions;
- protected $resultPackages;
- /**
- * @var array
- */
- protected $operations;
- public function __construct($policy, $pool, $presentMap, $unlockableMap, $decisions)
- {
- $this->policy = $policy;
- $this->pool = $pool;
- $this->presentMap = $presentMap;
- $this->unlockableMap = $unlockableMap;
- $this->decisions = $decisions;
- $this->operations = $this->calculateOperations();
- }
- /**
- * @return OperationInterface[]
- */
- public function getOperations()
- {
- return $this->operations;
- }
- protected function calculateOperations()
- {
- $operations = array();
- $ignoreRemove = array();
- $lockMeansUpdateMap = $this->findPotentialUpdates();
- foreach ($this->decisions as $i => $decision) {
- $literal = $decision[Decisions::DECISION_LITERAL];
- $reason = $decision[Decisions::DECISION_REASON];
- $package = $this->pool->literalToPackage($literal);
- // wanted & !present
- if ($literal > 0 && !isset($this->presentMap[spl_object_hash($package)])) {
- if (isset($lockMeansUpdateMap[spl_object_hash($package)]) && !$package instanceof AliasPackage) {
- // TODO we end up here sometimes because we prefer the remote package now to get up to date metadata
- // TODO define some level of identity here for what constitutes an update and what can be ignored? new kind of metadata only update?
- $target = $lockMeansUpdateMap[spl_object_hash($package)];
- if ($package->getName() !== $target->getName() || $package->getVersion() !== $target->getVersion()) {
- $operations[] = new Operation\UpdateOperation($target, $package, $reason);
- }
- // avoid updates to one package from multiple origins
- $ignoreRemove[spl_object_hash($lockMeansUpdateMap[spl_object_hash($package)])] = true;
- unset($lockMeansUpdateMap[spl_object_hash($package)]);
- } else {
- if ($package instanceof AliasPackage) {
- $operations[] = new Operation\MarkAliasInstalledOperation($package, $reason);
- } else {
- $operations[] = new Operation\InstallOperation($package, $reason);
- }
- }
- }
- }
- foreach ($this->decisions as $i => $decision) {
- $literal = $decision[Decisions::DECISION_LITERAL];
- $reason = $decision[Decisions::DECISION_REASON];
- $package = $this->pool->literalToPackage($literal);
- if ($literal <= 0 && isset($this->presentMap[spl_object_hash($package)]) && !isset($ignoreRemove[spl_object_hash($package)])) {
- if ($package instanceof AliasPackage) {
- $operations[] = new Operation\MarkAliasUninstalledOperation($package, $reason);
- } else {
- $operations[] = new Operation\UninstallOperation($package, $reason);
- }
- }
- }
- foreach ($this->presentMap as $package) {
- if ($package->id === -1 && !isset($ignoreRemove[spl_object_hash($package)])) {
- // TODO pass reason parameter to these two operations?
- if ($package instanceof AliasPackage) {
- $operations[] = new Operation\MarkAliasUninstalledOperation($package);
- } else {
- $operations[] = new Operation\UninstallOperation($package);
- }
- }
- }
- $this->setResultPackages();
- return $operations;
- }
- // TODO make this a bit prettier instead of the two text indexes?
- public function setResultPackages()
- {
- $this->resultPackages = array('non-dev' => array(), 'dev' => array());
- foreach ($this->decisions as $i => $decision) {
- $literal = $decision[Decisions::DECISION_LITERAL];
- if ($literal > 0) {
- $package = $this->pool->literalToPackage($literal);
- if (!isset($this->unlockableMap[$package->id])) {
- $this->resultPackages['non-dev'][] = $package;
- }
- }
- }
- }
- public function setNonDevPackages(LockTransaction $extractionResult)
- {
- $packages = $extractionResult->getNewLockPackages(false);
- $this->resultPackages['dev'] = $this->resultPackages['non-dev'];
- $this->resultPackages['non-dev'] = array();
- foreach ($packages as $package) {
- foreach ($this->resultPackages['dev'] as $i => $resultPackage) {
- // TODO this comparison is probably insufficient, aliases, what about modified versions? I guess they aren't possible?
- if ($package->getName() == $resultPackage->getName()) {
- $this->resultPackages['non-dev'][] = $resultPackage;
- unset($this->resultPackages['dev'][$i]);
- }
- }
- }
- }
- // TODO additionalFixedRepository needs to be looked at here as well?
- public function getNewLockPackages($devMode, $updateMirrors = false)
- {
- $packages = array();
- foreach ($this->resultPackages[$devMode ? 'dev' : 'non-dev'] as $package) {
- if (!($package instanceof AliasPackage) && !($package instanceof RootAliasPackage)) {
- // if we're just updating mirrors we need to reset references to the same as currently "present" packages' references to keep the lock file as-is
- // we do not reset references if the currently present package didn't have any, or if the type of VCS has changed
- if ($updateMirrors && !isset($this->presentMap[spl_object_hash($package)])) {
- foreach ($this->presentMap as $presentPackage) {
- if ($package->getName() == $presentPackage->getName() &&
- $package->getVersion() == $presentPackage->getVersion() &&
- $presentPackage->getSourceReference() &&
- $presentPackage->getSourceType() === $package->getSourceType()
- ) {
- $package->setSourceDistReferences($presentPackage->getSourceReference());
- }
- }
- }
- $packages[] = $package;
- }
- }
- return $packages;
- }
- protected function findPotentialUpdates()
- {
- $lockMeansUpdateMap = array();
- $packages = array();
- foreach ($this->decisions as $i => $decision) {
- $literal = $decision[Decisions::DECISION_LITERAL];
- $package = $this->pool->literalToPackage($literal);
- if ($literal <= 0 && isset($this->presentMap[spl_object_hash($package)])) {
- $packages[spl_object_hash($package)] = $package;
- }
- }
- // some locked packages are not in the pool and thus, were not decided at all
- foreach ($this->presentMap as $package) {
- if ($package->id === -1) {
- $packages[spl_object_hash($package)] = $package;
- }
- }
- foreach ($packages as $package) {
- if ($package instanceof AliasPackage) {
- continue;
- }
- // TODO can't we just look at existing rules?
- $updates = $this->policy->findUpdatePackages($this->pool, $package);
- $updatesAndPackage = array_merge(array($package), $updates);
- foreach ($updatesAndPackage as $update) {
- if (!isset($lockMeansUpdateMap[spl_object_hash($update)])) {
- $lockMeansUpdateMap[spl_object_hash($update)] = $package;
- }
- }
- }
- return $lockMeansUpdateMap;
- }
- }
|