compatibility with 2.6.0 and 3.1.0
[m6w6/seekat] / lib / API / Invoker.php
1 <?php
2
3 namespace seekat\API;
4
5 use Generator;
6 use http\Client;
7 use React\Promise\{
8 Deferred,
9 ExtendedPromiseInterface,
10 PromiseInterface,
11 function all
12 };
13
14 class Invoker extends Deferred
15 {
16 /**
17 * The HTTP client
18 * @var Client
19 */
20 private $client;
21
22 /**
23 * The return value of the generator
24 * @var mixed
25 */
26 private $result;
27
28 /**
29 * Cancellation flag
30 * @var bool
31 */
32 private $cancelled = false;
33
34 /**
35 * Create a new generator invoker
36 * @param Client $client
37 */
38 function __construct(Client $client) {
39 $this->client = $client;
40
41 parent::__construct(function($resolve, $reject) {
42 return $this->cancel($resolve, $reject);
43 });
44 }
45
46 /**
47 * Invoke $generator to create a \Generator which yields promises
48 *
49 * @param callable $generator as function():\Generator, creating a generator yielding promises
50 * @return Invoker
51 */
52 function invoke(callable $generator) : Invoker {
53 $this->iterate($generator());
54 return $this;
55 }
56
57 /**
58 * Iterate over $gen, a \Generator yielding promises
59 *
60 * @param Generator $gen
61 * @return Invoker
62 */
63 function iterate(Generator $gen) : Invoker {
64 $this->cancelled = false;
65
66 foreach ($gen as $promise) {
67 if ($this->cancelled) {
68 break;
69 }
70 $this->give($promise, $gen);
71 }
72
73 if (!$this->cancelled) {
74 $this->resolve($this->result = $gen->getReturn());
75 }
76 return $this;
77 }
78
79 /**
80 * Get the generator's result
81 *
82 * @return ExtendedPromiseInterface
83 */
84 function result() : ExtendedPromiseInterface {
85 return $this->promise();
86 }
87
88 /**
89 * Promise handler
90 *
91 * @param array|PromiseInterface $promise
92 * @param Generator $gen
93 */
94 private function give($promise, Generator $gen) {
95 if ($promise instanceof PromiseInterface) {
96 $promise->then(function($result) use($gen) {
97 if (($promise = $gen->send($result))) {
98 $this->give($promise, $gen);
99 }
100 });
101 } else {
102 all($promise)->then(function($results) use($gen) {
103 if (($promise = $gen->send($results))) {
104 $this->give($promise, $gen);
105 }
106 });
107 }
108 $this->client->send();
109 }
110
111 /**
112 * Cancellation callback
113 *
114 * @param callable $resolve
115 * @param callable $reject
116 */
117 private function cancel(callable $resolve, callable $reject) {
118 $this->cancelled = true;
119
120 /* did we finish in the meantime? */
121 if ($this->result) {
122 $resolve($this->result);
123 } else {
124 $reject("Cancelled");
125 }
126 }
127 }