phabel/src/Plugin/ReGenerator/ReGenerator.php

207 lines
4.3 KiB
PHP
Raw Normal View History

2020-08-23 22:16:23 +02:00
<?php
2020-08-24 12:25:35 +02:00
namespace Phabel\Plugin\ReGenerator;
2020-08-23 22:16:23 +02:00
/**
* Regenerator class.
*/
class ReGenerator implements \Iterator
{
/**
* Variable list for context suspension.
*
* @var array
*/
2020-08-30 16:58:59 +02:00
public $variables = [];
2020-08-23 22:16:23 +02:00
/**
* Return value.
*
* @var mixed
*/
2020-08-30 16:58:59 +02:00
public $returnValue;
2020-08-23 22:16:23 +02:00
/**
* Yield key.
*
* @var mixed
*/
2020-08-30 16:58:59 +02:00
public $yieldKey;
2020-08-23 22:16:23 +02:00
/**
* Yield value.
*
* @var mixed
*/
2020-08-30 16:58:59 +02:00
public $yieldValue;
2020-08-23 22:16:23 +02:00
/**
* Value sent from the outside.
*
* @var mixed
*/
2020-08-30 16:58:59 +02:00
public $sentValue;
2020-08-23 22:16:23 +02:00
/**
* Exception sent from the outside.
*/
2020-08-30 16:58:59 +02:00
public ?\Throwable $sentException = null;
2020-08-23 22:16:23 +02:00
/**
* Current state of state machine.
*/
2020-08-30 16:58:59 +02:00
public int $state = 0;
2020-08-23 22:16:23 +02:00
/**
* Whether the generator has returned.
*/
2020-08-30 16:58:59 +02:00
public bool $returned = false;
2020-08-23 22:16:23 +02:00
/**
* Whether the generator was started.
*/
2020-08-30 16:58:59 +02:00
public bool $started = false;
2020-08-23 22:16:23 +02:00
/**
* Actual generator function.
*/
2020-08-30 16:58:59 +02:00
public \Closure $generator;
2020-08-23 22:16:23 +02:00
/**
* Construct regenerator.
*
* @param \Closure $function
*/
public function __construct(\Closure $function)
{
$this->generator = $function;
}
/**
* Get return value.
*
2020-08-24 12:25:35 +02:00
* @return mixed
2020-08-23 22:16:23 +02:00
*/
public function getReturn()
{
return $this->returnValue;
}
/**
* Start generator.
*
* @return void
*/
private function start(): void
{
if (!$this->started) {
2020-08-30 16:58:59 +02:00
($this->generator)($this->state, $this->variables, $this->yieldKey, $this->yieldValue, $this->sentValue, $this->sentException, $this->returnValue, $this->returned);
2020-08-23 22:16:23 +02:00
$this->started = true;
}
}
2020-08-30 16:58:59 +02:00
/**
* Send value into generator
*
* @param mixed $value Value
*
* @return mixed
*/
2020-08-23 22:16:23 +02:00
public function send($value)
{
$this->start();
$value = $this->yieldValue;
if (!$this->returned) {
$this->sentValue = $value;
try {
2020-08-30 16:58:59 +02:00
($this->generator)($this->state, $this->variables, $this->yieldKey, $this->yieldValue, $this->sentValue, $this->sentException, $this->returnValue, $this->returned);
2020-08-23 22:16:23 +02:00
} catch (\Throwable $e) {
$this->returned = true;
throw $e;
} finally {
$this->sentValue = null;
}
}
return $value;
}
2020-08-30 16:58:59 +02:00
/**
* Throw value into generator
*
* @param \Throwable $throwable Excpeption
*
* @return mixed
*/
2020-08-23 22:16:23 +02:00
public function throw(\Throwable $throwable)
{
$this->start();
$value = $this->yieldValue;
if (!$this->returned) {
$this->sentException = $value;
try {
2020-08-30 16:58:59 +02:00
($this->generator)($this->state, $this->variables, $this->yieldKey, $this->yieldValue, $this->sentValue, $this->sentException, $this->returnValue, $this->returned);
2020-08-23 22:16:23 +02:00
} catch (\Throwable $e) {
$this->returned = true;
throw $e;
} finally {
$this->sentException = null;
}
}
return $value;
}
2020-08-30 16:58:59 +02:00
/**
* Get current value
*
* @return mixed
*/
2020-08-23 22:16:23 +02:00
public function current()
{
2020-08-24 12:25:35 +02:00
$this->start();
2020-08-23 22:16:23 +02:00
return $this->yieldValue;
}
2020-08-30 16:58:59 +02:00
/**
* Get current key
*
* @return mixed
*/
2020-08-23 22:16:23 +02:00
public function key()
{
2020-08-24 12:25:35 +02:00
$this->start();
2020-08-23 22:16:23 +02:00
return $this->yieldKey;
}
2020-08-30 16:58:59 +02:00
/**
* Advance generator
*
* @return void
*/
2020-08-23 22:16:23 +02:00
public function next(): void
{
$this->send(null);
}
2020-08-30 16:58:59 +02:00
/**
* Rewind generator
*
* @return void
*/
2020-08-23 22:16:23 +02:00
public function rewind(): void
{
if ($this->started && !$this->returned) {
throw new \Exception('Cannot rewind a generator that was already run');
}
2020-08-30 16:58:59 +02:00
$this->state = 0;
2020-08-23 22:16:23 +02:00
$this->started = false;
$this->returned = false;
$this->returnValue = null;
$this->yieldKey = null;
$this->yieldValue = null;
$this->sentValue = null;
$this->sentException = null;
$this->variables = [];
$this->start();
}
2020-08-30 16:58:59 +02:00
/**
* Check if generator is valid
*
* @return boolean
*/
2020-08-23 22:16:23 +02:00
public function valid(): bool
{
return !$this->returned;
}
}