generator = $generator; try { $yielded = $this->generator->current(); while (!$yielded instanceof Promise) { if ($yielded instanceof \YieldReturnValue) { $this->resolve($yielded->getReturn()); $this->generator->next(); return; } if (!$this->generator->valid()) { if (PHP_MAJOR_VERSION >= 7) { $this->resolve($this->generator->getReturn()); } else { $this->resolve(null); } return; } if ($yielded instanceof \Generator) { $yielded = new self($yielded); } else { $yielded = $this->generator->send($yielded); } } if ($yielded instanceof self) { $yielded->parentCoroutine = $this; } } catch (\Throwable $exception) { $this->fail($exception); return; } /* * @param \Throwable|null $exception Exception to be thrown into the generator. * @param mixed $value Value to be sent into the generator. */ $this->onResolve = function ($exception, $value) { $this->exception = $exception; $this->value = $value; if (!$this->immediate) { $this->immediate = true; return; } try { do { if ($this->exception) { // Throw exception at current execution point. $yielded = $this->throw($this->exception); } else { // Send the new value and execute to next yield statement. $yielded = $this->generator->send($this->value); } while (!$yielded instanceof Promise) { if ($yielded instanceof \YieldReturnValue) { $this->resolve($yielded->getReturn()); $this->onResolve = null; $this->generator->next(); return; } if (!$this->generator->valid()) { if (PHP_MAJOR_VERSION >= 7) { $this->resolve($this->generator->getReturn()); } else { $this->resolve(null); } $this->onResolve = null; return; } if ($yielded instanceof \Generator) { $yielded = new self($yielded); } else { $yielded = $this->generator->send($yielded); } } if ($yielded instanceof self) { $yielded->parentCoroutine = $this; } $this->immediate = false; $yielded->onResolve($this->onResolve); } while ($this->immediate); $this->immediate = true; } catch (\Throwable $exception) { $this->fail($exception); $this->onResolve = null; } finally { $this->exception = null; $this->value = null; } }; $yielded->onResolve($this->onResolve); } /** * Throw exception into the generator. * * @param \Throwable $reason Exception * * @internal * * @return mixed */ public function throw(\Throwable $reason) { if (!isset($reason->yieldedFrames)) { if (\method_exists($reason, 'updateTLTrace')) { $reason->updateTLTrace($this->getTrace()); } else { $reason->yieldedFrames = $this->getTrace(); } } return $this->generator->throw($reason); } /** * @param \Throwable $reason Failure reason. * * @return void */ public function fail(\Throwable $reason): void { $this->resolve(new Failure($reason)); } public function offsetExists($offset): bool { throw new Exception('Not supported!'); } /** * Get data at an array offset asynchronously. * * @param mixed $offset Offset * * @return Promise */ public function offsetGet($offset) { return Tools::call((function () use ($offset): \Generator { $result = yield $this; return $result[$offset]; })()); } public function offsetSet($offset, $value): Promise { return Tools::call((function () use ($offset, $value): \Generator { $result = yield $this; if ($offset === null) { return $result[] = $value; } return $result[$offset] = $value; })()); } public function offsetUnset($offset): Promise { return Tools::call((function () use ($offset): \Generator { $result = yield $this; unset($result[$offset]); })()); } /** * Get an attribute asynchronously. * * @param string $offset Offset * * @return Promise */ public function __get(string $offset) { return Tools::call((function () use ($offset): \Generator { $result = yield $this; return $result->{$offset}; })()); } public function __call(string $name, array $arguments) { return Tools::call((function () use ($name, $arguments): \Generator { $result = yield $this; return $result->{$name}(...$arguments); })()); } /** * Get current stack trace for running coroutine. * * @return array */ public function getTrace(): array { $frames = []; try { $reflector = new ReflectionGenerator($this->generator); $frames = $reflector->getTrace(); $frames[] = \array_merge($this->parentCoroutine ? $this->parentCoroutine->getFrame() : [], ['function' => $reflector->getFunction()->getName(), 'args' => []]); } catch (\Throwable $e) { } if ($this->parentCoroutine) { $frames = \array_merge($frames, $this->parentCoroutine->getTrace()); } return $frames; } /** * Get current execution frame. * * @return array */ public function getFrame(): array { try { $reflector = new ReflectionGenerator($this->generator); return ['file' => $reflector->getExecutingFile(), 'line' => $reflector->getExecutingLine()]; } catch (\Throwable $e) { } return []; } private const WARNING = 'To obtain a result from a Coroutine object, yield the result or disable async (not recommended). See https://docs.madelineproto.xyz/docs/ASYNC.html for more information on async.'; public function __debugInfo() { return [self::WARNING]; } public function jsonSerialize() { return self::WARNING; } }