Created
February 22, 2016 15:24
-
-
Save Isinlor/5d901ff80cfc992ec30f to your computer and use it in GitHub Desktop.
Interface and calsses that allows dynamical nesting of loops
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Interface DynamicLoops | |
* | |
* Allows to dynamically nest loops eg.: | |
* | |
* $loops = [ | |
* function (DynamicLoops $nextLoop, array $values = []) { | |
* foreach (range(0, 1) as $value) { | |
* $values[] = $value; | |
* $nextLoop($values); | |
* } | |
* }, | |
* function (DynamicLoops $nextLoop, array $values = []) { | |
* foreach (range(2, 3) as $value) { | |
* $values[] = $value; | |
* $nextLoop($values); | |
* } | |
* } | |
* ]; | |
* | |
* (new BasicDynamicLoops($loops))(); // $values: [[0, 2], [0, 3], [1, 2], [1, 3]] | |
* (new BasicDynamicLoops(array_reverse($loops)))(); // $values: [[2, 0], [2, 1], [3, 0], [3, 1]] | |
* | |
* | |
*/ | |
interface DynamicLoops | |
{ | |
/** | |
* Invokes next loop | |
* | |
* @param array ...$args Arguments that will be passed to the loop callable (see example above) | |
* | |
* @return mixed | |
*/ | |
public function __invoke(...$args); | |
} | |
class BasicDynamicLoops implements DynamicLoops | |
{ | |
protected $loops = []; | |
protected $currentLoop = 0; | |
public function __construct(array $loops) | |
{ | |
$this->loops = array_values($loops); | |
} | |
public function __invoke(...$args) | |
{ | |
/* @var $loop callable */ | |
$loop = $this->loops[$this->currentLoop]; | |
$this->currentLoop++; | |
$loop($this, ...$args); | |
$this->currentLoop--; | |
} | |
} | |
class ContinueInLoop extends Exception | |
{ | |
protected $loop; | |
public function __construct(string $loop) | |
{ | |
$this->loop = $loop; | |
} | |
public function getLoop() | |
{ | |
return $this->loop; | |
} | |
} | |
class DynamicLoopsWithContinue extends BasicDynamicLoops | |
{ | |
protected $loopsKeys = []; | |
public function __construct(array $loops) | |
{ | |
$this->loopsKeys = array_flip(array_keys($loops)); | |
parent::__construct($loops); | |
} | |
public function __invoke(...$args) | |
{ | |
try { | |
/* @var $loop callable */ | |
$loop = $this->loops[$this->currentLoop]; | |
$this->currentLoop++; | |
$continueInLoop = $loop($this, ...$args); | |
} catch (ContinueInLoop $skipper) { | |
// -1 because we haven't yet decremented counter, but we exited a loop already with exception | |
// -1 because we want to stop inside the requested loop | |
if ($this->currentLoop - 2 != $skipper->getLoop()) { | |
throw $skipper; | |
} | |
} finally { | |
$this->currentLoop--; | |
// check if continuing in some loop was requested | |
// throw an exception to break execution of loops in between the requested loop and the current loop | |
if (isset($continueInLoop)) { | |
throw new ContinueInLoop($this->loopsKeys[$continueInLoop]); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment