Skip to content

Instantly share code, notes, and snippets.

@rskuipers
Created March 17, 2016 15:00
Show Gist options
  • Save rskuipers/df208a4cf41040a35d5d to your computer and use it in GitHub Desktop.
Save rskuipers/df208a4cf41040a35d5d to your computer and use it in GitHub Desktop.
<?php
class ValidationFlow
{
/**
* @var ValidationStep[]
*/
private $steps = [];
/**
* @var ValidationExit[]
*/
private $exits = [];
/**
* @var string
*/
private $initialStep;
/**
* @param string $stepName
*/
public function setInitialStep($stepName)
{
$this->initialStep = $stepName;
}
/**
* @param array $steps
* @return $this
*/
public function registerSteps(array $steps)
{
array_walk($steps, [$this, 'registerStep']);
return $this;
}
/**
* @param ValidationStep $step
* @return $this
*/
public function registerStep(ValidationStep $step)
{
$stepName = get_class($step);
if (count($this->steps) === 0 && $this->initialStep === null) {
$this->setInitialStep($stepName);
}
$step->setFlow($this);
$this->steps[$stepName] = $step;
return $this;
}
/**
* @param array $exits
* @return $this
*/
public function registerExits(array $exits)
{
array_walk($exits, [$this, 'registerExit']);
return $this;
}
/**
* @param ValidationExit $exit
* @return $this
*/
public function registerExit(ValidationExit $exit)
{
$this->exits[get_class($exit)] = $exit;
return $this;
}
/**
* @param string $name
* @param mixed $payload
* @return bool
*/
public function continueTo($name, $payload)
{
return $this->steps[$name]->execute($payload);
}
/**
* @param string $name
* @param mixed $payload
* @return mixed
*/
public function exitWith($name, $payload)
{
return $this->exits[$name]->execute($payload);
}
/**
* @param mixed $payload
* @return bool
*/
public function validate($payload)
{
return $this->steps[$this->initialStep]->execute($payload);
}
}
interface ValidationExit
{
/**
* @param mixed $payload
* @return bool
*/
public function execute($payload);
}
interface ValidationStep
{
/**
* @param ValidationFlow $flow
*/
public function setFlow(ValidationFlow $flow);
/**
* @param mixed $payload
* @return bool
*/
public function execute($payload);
}
abstract class AbstractValidationStep implements ValidationStep
{
/**
* @var ValidationFlow
*/
private $flow;
/**
* @param ValidationFlow $flow
*/
public function setFlow(ValidationFlow $flow)
{
$this->flow = $flow;
}
/**
* @param string $name
* @param mixed $payload
* @return bool
*/
protected function continueTo($name, $payload)
{
return $this->flow->continueTo($name, $payload);
}
/**
* @param string $name
* @param mixed $payload
* @return bool
*/
protected function exitWith($name, $payload)
{
return $this->flow->exitWith($name, $payload);
}
}
class ExitA implements ValidationExit
{
public function execute($payload)
{
// Do some stuff
return false; // fail!
}
}
class ValidationStepA extends AbstractValidationStep
{
public function execute($contract)
{
if ($contract->a !== true) {
return $this->exitWith(ExitA::class, $contract);
}
if (!true) {
return $this->exitWith(ExitA::class, $contract);
}
return $this->continueTo(ValidationStepB::class, $contract);
}
}
class ValidationStepB extends AbstractValidationStep
{
public function execute($contract)
{
return true; // done!
}
}
$flow = new ValidationFlow();
$flow
->registerSteps([
new ValidationStepA(),
new ValidationStepB(),
])
->registerExits([
new ExitA(),
])
;
$contract = new stdClass();
$contract->a = true;
if ($flow->validate($contract)) {
echo 'Success!';
} else {
echo 'Fail!';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment