/** * @param \Generator $generator */ public function __construct(\Generator $generator) { $this->generator = $generator; /** * @param \Throwable|null $exception Exception to be thrown into the generator. * @param mixed $value Value to be sent into the generator. */ $this->when = function ($exception, $value) { if ($this->depth > self::MAX_CONTINUATION_DEPTH) { // Defer continuation to avoid blowing up call stack. Loop::defer(function () use($exception, $value) { ($this->when)($exception, $value); }); return; } try { if ($exception) { // Throw exception at current execution point. $yielded = $this->generator->throw($exception); } else { // Send the new value and execute to next yield statement. $yielded = $this->generator->send($value); } if ($yielded instanceof Promise) { ++$this->depth; $yielded->when($this->when); --$this->depth; return; } if ($this->generator->valid()) { $got = \is_object($yielded) ? \get_class($yielded) : \gettype($yielded); throw new InvalidYieldError($this->generator, \sprintf("Unexpected yield (%s expected, got %s)", Promise::class, $got)); } $this->resolve($this->generator->getReturn()); } catch (\Throwable $exception) { $this->dispose($exception); } }; try { $yielded = $this->generator->current(); if ($yielded instanceof Promise) { ++$this->depth; $yielded->when($this->when); --$this->depth; return; } if ($this->generator->valid()) { $got = \is_object($yielded) ? \get_class($yielded) : \gettype($yielded); throw new InvalidYieldError($this->generator, \sprintf("Unexpected yield (%s expected, got %s)", Promise::class, $got)); } $this->resolve($this->generator->getReturn()); } catch (\Throwable $exception) { $this->dispose($exception); } }
/** * Examines the value yielded from the generator and prepares the next step in interation. * * @param mixed $yielded */ private function next($yielded) { if (!$this->generator->valid()) { $result = $this->generator->getReturn(); if ($result instanceof Awaitable) { $this->reject(new AwaitableReturnedError($result)); return; } if ($result instanceof Generator) { $this->reject(new GeneratorReturnedError($result)); return; } $this->resolve($result); return; } $this->busy = true; if ($yielded instanceof Generator) { $yielded = new self($yielded); } $this->current = $yielded; if ($yielded instanceof Awaitable) { $yielded->done($this->send, $this->capture); } else { Loop\queue($this->send, $yielded); } $this->busy = false; }
/** * Return value that generator has returned or thrown. * @return mixed */ public function getReturnOrThrown() { $this->validateInvalidity(); if ($this->e === null && $this->g->valid() && !$this->valid()) { return $this->g->current(); } if ($this->e) { return $this->e; } return method_exists($this->g, 'getReturn') ? $this->g->getReturn() : null; }
private function checkResult(\Generator $generator, Channel $channel) { if (!$generator->valid()) { foreach ($this->actions as list(, $gen, , $undo)) { if ($gen && $gen !== $generator) { $undo($gen->current()); } } return [$generator->getReturn(), $channel]; } return false; }
/** * Examines the value yielded from the generator and prepares the next step in interation. * * @param mixed $yielded */ private function next($yielded) { if (!$this->generator->valid()) { $this->resolve($this->generator->getReturn()); return; } if ($yielded instanceof Generator) { $yielded = new self($yielded); } $this->current = $yielded; if ($yielded instanceof Awaitable) { $yielded->done($this->send, $this->capture); } else { Loop\queue($this->send, $yielded); } }
/** * @param \Generator $generator */ public function __construct(Generator $generator) { $this->generator = $generator; parent::__construct(function (callable $resolve, callable $reject) { /** * @param mixed $value The value to send to the generator. * @param \Throwable|null $exception Exception object to be thrown into the generator if not null. */ $this->worker = function ($value = null, Throwable $exception = null) use($resolve, $reject) { if ($this->paused) { // If paused, mark coroutine as ready to resume. $this->ready = true; return; } try { if ($this->initial) { // Get result of first yield statement. $this->initial = false; $this->current = $this->generator->current(); } elseif (null !== $exception) { // Throw exception at current execution point. $this->current = $this->generator->throw($exception); } else { // Send the new value and execute to next yield statement. $this->current = $this->generator->send($value); } if (!$this->generator->valid()) { $resolve($this->generator->getReturn()); $this->close(); return; } if ($this->current instanceof Generator) { $this->current = new self($this->current); } if ($this->current instanceof PromiseInterface) { $this->current->done($this->worker, $this->capture); } else { Loop\queue($this->worker, $this->current); } } catch (Throwable $exception) { $reject($exception); $this->close(); } }; /** * @param \Throwable $exception Exception to be thrown into the generator. */ $this->capture = function (Throwable $exception) { if (null !== $this->worker) { // Coroutine may have been closed. ($this->worker)(null, $exception); } }; Loop\queue($this->worker); return function (Throwable $exception) { try { $current = $this->generator->current(); // Get last yielded value. while ($this->generator->valid()) { if ($current instanceof PromiseInterface) { $current->cancel($exception); } $current = $this->generator->throw($exception); } } finally { $this->close(); } }; }); }
private function responseCodec(\Generator $filter, InternalRequest $ireq) : \Generator { while ($filter->valid()) { $cur = $filter->send(yield); if ($cur !== null) { $ireq->responseWriter->send($cur); } } $cur = $filter->getReturn(); if ($cur !== null) { $ireq->responseWriter->send($cur); } $ireq->responseWriter->send(null); }
/** * @param \Generator $generator */ public function __construct(Generator $generator) { $this->generator = $generator; parent::__construct(function (callable $resolve, callable $reject) { $yielded = $this->generator->current(); if (!$this->generator->valid()) { $resolve($this->generator->getReturn()); $this->close(); return; } /** * @param mixed $value The value to send to the generator. */ $this->send = function ($value = null) use($resolve, $reject) { if ($this->paused) { // If paused, save callable and value for resuming. $this->next = [$this->send, $value]; return; } try { // Send the new value and execute to next yield statement. $yielded = $this->generator->send($value); if (!$this->generator->valid()) { $resolve($this->generator->getReturn()); $this->close(); return; } $this->next($yielded); } catch (Throwable $exception) { $reject($exception); $this->close(); } }; /** * @param \Throwable $exception Exception to be thrown into the generator. */ $this->capture = function (Throwable $exception) use($resolve, $reject) { if ($this->paused) { // If paused, save callable and exception for resuming. $this->next = [$this->capture, $exception]; return; } try { // Throw exception at current execution point. $yielded = $this->generator->throw($exception); if (!$this->generator->valid()) { $resolve($this->generator->getReturn()); $this->close(); return; } $this->next($yielded); } catch (Throwable $exception) { $reject($exception); $this->close(); } }; $this->next($yielded); return function (Throwable $exception) { try { $current = $this->generator->current(); // Get last yielded value. if ($current instanceof PromiseInterface) { $current->cancel($exception); } } finally { $this->close(); } }; }); }
private function responseCodec(\Generator $filter, InternalRequest $ireq) : \Generator { while (($yield = yield) !== null) { $cur = $filter->send($yield); if ($yield === false) { if ($cur !== null) { $ireq->responseWriter->send($cur); if (\is_array($cur)) { // in case of headers, to flush a maybe started body too, we need to send false twice $cur = $filter->send(false); if ($cur !== null) { $ireq->responseWriter->send($cur); } } } $ireq->responseWriter->send(false); } elseif ($cur !== null) { $ireq->responseWriter->send($cur); } } $cur = $filter->send(null); if (\is_array($cur)) { $ireq->responseWriter->send($cur); $filter->send(null); } \assert($filter->valid() === false); $cur = $filter->getReturn(); if ($cur !== null) { $ireq->responseWriter->send($cur); } $ireq->responseWriter->send(null); }