View source
<?php
namespace Drupal\automatic_updates;
use Drupal\automatic_updates\Event\PreCommitEvent;
use Drupal\automatic_updates\Event\PreStartEvent;
use Drupal\automatic_updates\Event\UpdateEvent;
use Drupal\automatic_updates\Exception\UpdateException;
use Drupal\Core\State\StateInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\package_manager\ComposerUtility;
use Drupal\system\SystemManager;
use PhpTuf\ComposerStager\Domain\BeginnerInterface;
use PhpTuf\ComposerStager\Domain\CleanerInterface;
use PhpTuf\ComposerStager\Domain\CommitterInterface;
use PhpTuf\ComposerStager\Domain\StagerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class Updater {
use StringTranslationTrait;
public const STATE_KEY = 'AUTOMATIC_UPDATES_CURRENT';
protected $beginner;
protected $stager;
protected $cleaner;
protected $committer;
protected $state;
protected $eventDispatcher;
protected $pathLocator;
public function __construct(StateInterface $state, TranslationInterface $translation, BeginnerInterface $beginner, StagerInterface $stager, CleanerInterface $cleaner, CommitterInterface $committer, EventDispatcherInterface $event_dispatcher, PathLocator $path_locator) {
$this->state = $state;
$this->beginner = $beginner;
$this->stager = $stager;
$this->cleaner = $cleaner;
$this->committer = $committer;
$this
->setStringTranslation($translation);
$this->eventDispatcher = $event_dispatcher;
$this->pathLocator = $path_locator;
}
public function hasActiveUpdate() : bool {
$staged_dir = $this->pathLocator
->getStageDirectory();
if (is_dir($staged_dir) || $this->state
->get(static::STATE_KEY)) {
return TRUE;
}
return FALSE;
}
public function begin(array $project_versions) : string {
if (count($project_versions) !== 1 || !array_key_exists('drupal', $project_versions)) {
throw new \InvalidArgumentException("Currently only updates to Drupal core are supported.");
}
$composer = ComposerUtility::createForDirectory($this->pathLocator
->getActiveDirectory());
$packages = [];
foreach ($composer
->getCorePackageNames() as $package) {
$packages[$package] = $project_versions['drupal'];
}
$stage_key = $this
->createActiveStage($packages);
$event = $this
->dispatchUpdateEvent(new PreStartEvent($composer, $packages), AutomaticUpdatesEvents::PRE_START);
$this->beginner
->begin($this->pathLocator
->getActiveDirectory(), $this->pathLocator
->getStageDirectory(), $this
->getExclusions($event));
return $stage_key;
}
private function getExclusions(UpdateEvent $event) : array {
$make_relative = function (string $path) : string {
return str_replace($this->pathLocator
->getActiveDirectory() . '/', '', $path);
};
return array_map($make_relative, $event
->getExcludedPaths());
}
public function stage() : void {
$current = $this->state
->get(static::STATE_KEY);
$this
->stagePackages($current['package_versions']);
}
protected function stagePackages(array $packages) : void {
$command = array_merge([
'require',
], $packages);
$command[] = '--update-with-all-dependencies';
$this
->stageCommand($command);
}
public function commit() : void {
$active_dir = $this->pathLocator
->getActiveDirectory();
$active_composer = ComposerUtility::createForDirectory($active_dir);
$stage_dir = $this->pathLocator
->getStageDirectory();
$stage_composer = ComposerUtility::createForDirectory($stage_dir);
$event = $this
->dispatchUpdateEvent(new PreCommitEvent($active_composer, $stage_composer), AutomaticUpdatesEvents::PRE_COMMIT);
$this->committer
->commit($stage_dir, $active_dir, $this
->getExclusions($event));
$this
->dispatchUpdateEvent(new UpdateEvent($active_composer), AutomaticUpdatesEvents::POST_COMMIT);
}
public function clean() : void {
$stage_dir = $this->pathLocator
->getStageDirectory();
if (is_dir($stage_dir)) {
$this->cleaner
->clean($stage_dir);
}
$this->state
->delete(static::STATE_KEY);
}
protected function stageCommand(array $command) : void {
$this->stager
->stage($command, $this->pathLocator
->getStageDirectory());
}
private function createActiveStage(array $package_versions) : string {
$requirements = [];
foreach ($package_versions as $package_name => $version) {
$requirements[] = "{$package_name}:{$version}";
}
$value = static::STATE_KEY . microtime();
$this->state
->set(static::STATE_KEY, [
'id' => $value,
'package_versions' => $requirements,
]);
return $value;
}
public function dispatchUpdateEvent(UpdateEvent $event, string $event_name) : UpdateEvent {
$this->eventDispatcher
->dispatch($event, $event_name);
if ($checker_results = $event
->getResults(SystemManager::REQUIREMENT_ERROR)) {
throw new UpdateException($checker_results, "Unable to complete the update because of errors.");
}
return $event;
}
}