1
0
mirror of https://github.com/flarum/core.git synced 2025-10-25 13:46:29 +02:00

Split up the installer logic

This is probably the most complicated way I could find to fix #1587.

Jokes aside, this was done with a few goals in mind:
- Reduce coupling between the installer and the rest of Flarum's
  "Application", which we are building during installation.
- Move the installer logic to several smaller classes, which can then
  be used by the web frontend and the console task, instead of the
  former hacking its way into the latter to be "DRY".
- Separate installer infrastructure (the "pipeline", with the ability
  to revert steps upon failure) from the actual steps being taken.

The problem was conceptual, and would certainly re-occur in a similar
fashion if we wouldn't tackle it at its roots.

It is fixed now, because we no longer use the ExtensionManager for
enabling extensions, but instead duplicate some of its logic. That is
fine because we don't want to do everything it does, e.g. omit
extenders' lifecycle hooks (which depend on the Application instance
being complete).

> for each desired change, make the change easy (warning: this may be
> hard), then make the easy change

- Kent Beck, https://twitter.com/kentbeck/status/250733358307500032

Fixes #1587.
This commit is contained in:
Franz Liedke
2018-10-24 22:33:45 +02:00
parent abf224bb0a
commit 790d5beee5
22 changed files with 1077 additions and 368 deletions

108
src/Install/Pipeline.php Normal file
View File

@@ -0,0 +1,108 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Install;
use Exception;
use SplStack;
class Pipeline
{
/**
* @var callable[]
*/
private $steps;
/**
* @var callable[]
*/
private $callbacks;
/**
* @var SplStack
*/
private $successfulSteps;
public function __construct(array $steps = [])
{
$this->steps = $steps;
}
public function pipe(callable $factory)
{
$this->steps[] = $factory;
return $this;
}
public function on($event, callable $callback)
{
$this->callbacks[$event] = $callback;
return $this;
}
public function run()
{
$this->successfulSteps = new SplStack;
try {
foreach ($this->steps as $factory) {
$this->runStep($factory);
}
} catch (StepFailed $failure) {
$this->revertReversibleSteps();
throw $failure;
}
}
/**
* @param callable $factory
* @throws StepFailed
*/
private function runStep(callable $factory)
{
/** @var Step $step */
$step = $factory();
$this->fireCallbacks('start', $step);
try {
$step->run();
$this->successfulSteps->push($step);
$this->fireCallbacks('end', $step);
} catch (Exception $e) {
$this->fireCallbacks('fail', $step);
throw new StepFailed('Step failed', 0, $e);
}
}
private function revertReversibleSteps()
{
foreach ($this->successfulSteps as $step) {
if ($step instanceof ReversibleStep) {
$this->fireCallbacks('rollback', $step);
$step->revert();
}
}
}
private function fireCallbacks($event, Step $step)
{
if (isset($this->callbacks[$event])) {
($this->callbacks[$event])($step);
}
}
}