1
0
mirror of https://github.com/danog/amp.git synced 2025-01-22 21:31:18 +01:00
amp/lib/Observer.php

174 lines
4.3 KiB
PHP
Raw Normal View History

2016-05-24 11:47:14 -05:00
<?php
namespace Amp;
2016-05-27 15:44:01 -05:00
final class Observer {
/**
2016-05-29 11:35:09 -05:00
* @var \Amp\Disposable
2016-05-27 15:44:01 -05:00
*/
private $subscriber;
2016-05-29 11:35:09 -05:00
/**
2016-05-29 23:49:44 -05:00
* @var mixed[]
2016-05-29 11:35:09 -05:00
*/
2016-05-29 23:49:44 -05:00
private $values = [];
/**
* @var \Amp\Future[]
*/
private $futures = [];
2016-05-29 11:35:09 -05:00
/**
2016-05-29 23:49:44 -05:00
* @var int
2016-05-29 11:35:09 -05:00
*/
2016-05-29 23:49:44 -05:00
private $position = -1;
/**
* @var \Amp\Deferred|null
*/
private $deferred;
2016-05-29 11:35:09 -05:00
/**
* @var bool
*/
2016-05-30 10:24:03 -05:00
private $resolved = false;
2016-05-29 11:35:09 -05:00
/**
* @var mixed
*/
2016-05-29 23:49:44 -05:00
private $result;
2016-05-29 11:35:09 -05:00
/**
2016-05-29 23:49:44 -05:00
* @var \Throwable|\Exception|null
2016-05-29 11:35:09 -05:00
*/
private $exception;
2016-05-27 15:44:01 -05:00
/**
* @param \Amp\Observable $observable
*/
public function __construct(Observable $observable) {
2016-05-29 11:35:09 -05:00
$deferred = &$this->deferred;
2016-05-29 23:49:44 -05:00
$values = &$this->values;
$futures = &$this->futures;
2016-05-29 11:35:09 -05:00
2016-05-29 23:49:44 -05:00
$this->subscriber = $observable->subscribe(static function ($value) use (&$deferred, &$values, &$futures) {
$values[] = $value;
$futures[] = $future = new Future;
2016-05-29 11:35:09 -05:00
2016-05-29 23:49:44 -05:00
if ($deferred !== null) {
$temp = $deferred;
$deferred = null;
$temp->resolve($value);
}
2016-05-29 11:35:09 -05:00
return $future;
});
2016-05-30 10:24:03 -05:00
$resolved = &$this->resolved;
2016-05-29 23:49:44 -05:00
$result = &$this->result;
2016-05-29 11:35:09 -05:00
$error = &$this->exception;
2016-05-30 10:24:03 -05:00
$this->subscriber->when(static function ($exception, $value) use (&$deferred, &$result, &$error, &$resolved) {
$resolved = true;
2016-05-29 11:35:09 -05:00
if ($exception) {
2016-05-29 23:49:44 -05:00
$result = null;
2016-05-29 11:35:09 -05:00
$error = $exception;
2016-05-29 23:49:44 -05:00
if ($deferred !== null) {
2016-05-29 11:35:09 -05:00
$deferred->fail($exception);
}
return;
}
2016-05-29 23:49:44 -05:00
$result = $value;
if ($deferred !== null) {
2016-05-29 11:35:09 -05:00
$deferred->resolve(false);
}
});
2016-05-27 15:44:01 -05:00
}
/**
2016-05-29 11:35:09 -05:00
* Disposes of the subscriber.
2016-05-27 15:44:01 -05:00
*/
public function __destruct() {
2016-05-30 10:24:03 -05:00
if (!$this->resolved) {
2016-05-29 23:49:44 -05:00
$this->subscriber->dispose();
}
2016-05-29 11:35:09 -05:00
2016-05-29 23:49:44 -05:00
foreach ($this->futures as $future) {
$future->resolve();
2016-05-29 11:35:09 -05:00
}
2016-05-27 15:44:01 -05:00
}
2016-05-24 11:47:14 -05:00
/**
2016-05-30 10:24:03 -05:00
* Succeeds with true if an emitted value is available by calling getCurrent() or false if the observable has
* resolved. If the observable fails, the returned awaitable will fail with the same exception.
2016-05-24 11:47:14 -05:00
*
* @return \Interop\Async\Awaitable
*/
2016-05-30 10:24:03 -05:00
public function next() {
2016-05-29 23:49:44 -05:00
if (isset($this->futures[$this->position])) {
$future = $this->futures[$this->position];
unset($this->values[$this->position], $this->futures[$this->position]);
$future->resolve();
}
++$this->position;
if (isset($this->values[$this->position])) {
return new Success(true);
}
2016-05-30 10:24:03 -05:00
if ($this->resolved) {
2016-05-29 11:35:09 -05:00
if ($this->exception) {
return new Failure($this->exception);
}
return new Success(false);
}
2016-05-29 23:49:44 -05:00
$this->deferred = new Deferred;
2016-05-29 11:35:09 -05:00
return $this->deferred->getAwaitable();
2016-05-27 15:44:01 -05:00
}
2016-05-24 11:47:14 -05:00
/**
* Gets the last emitted value or throws an exception if the observable has completed.
*
* @return mixed Value emitted from observable.
*
2016-05-30 10:24:03 -05:00
* @throws \LogicException If the observable has resolved or next() was not called before calling this method.
2016-05-24 11:47:14 -05:00
*/
2016-05-27 15:44:01 -05:00
public function getCurrent() {
2016-05-30 10:24:03 -05:00
if (empty($this->values) && $this->resolved) {
2016-05-29 23:49:44 -05:00
throw new \LogicException("The observable has completed");
2016-05-29 11:35:09 -05:00
}
2016-05-29 23:49:44 -05:00
if (!isset($this->values[$this->position])) {
2016-05-30 10:24:03 -05:00
throw new \LogicException("Awaitable returned from next() must resolve before calling this method");
2016-05-29 11:35:09 -05:00
}
2016-05-29 23:49:44 -05:00
return $this->values[$this->position];
2016-05-27 15:44:01 -05:00
}
2016-05-24 11:47:14 -05:00
/**
2016-05-30 10:24:03 -05:00
* Gets the result of the observable or throws the failure reason. Also throws an exception if the observable has
* not completed.
2016-05-24 11:47:14 -05:00
*
* @return mixed Final return value of the observable.
*
2016-05-29 11:35:09 -05:00
* @throws \LogicException If the observable has not completed.
* @throws \Throwable|\Exception The exception used to fail the observable.
2016-05-24 11:47:14 -05:00
*/
2016-05-30 10:24:03 -05:00
public function getResult() {
if (!$this->resolved) {
throw new \LogicException("The observable has not resolved");
2016-05-29 11:35:09 -05:00
}
if ($this->exception) {
throw $this->exception;
}
2016-05-29 23:49:44 -05:00
return $this->result;
2016-05-27 15:44:01 -05:00
}
2016-05-24 11:47:14 -05:00
}