Completed
Push — master ( b83b4d...175f97 )
by Joel
11s
created

Promise   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 172
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 94.59%

Importance

Changes 0
Metric Value
wmc 21
lcom 1
cbo 2
dl 0
loc 172
ccs 35
cts 37
cp 0.9459
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
C then() 0 38 7
A resolve() 0 10 2
A doResolve() 0 8 2
A reject() 0 10 2
A doReject() 0 8 2
A getState() 0 4 1
A wait() 0 14 4
1
<?php
2
3
namespace Http\Adapter\React;
4
5
use React\EventLoop\LoopInterface;
6
use Http\Client\Exception;
7
use Http\Promise\Promise as HttpPromise;
8
use Psr\Http\Message\ResponseInterface;
9
10
/**
11
 * React promise adapter implementation.
12
 *
13
 * @author Stéphane Hulard <[email protected]>
14
 *
15
 * @internal
16
 */
17
final class Promise implements HttpPromise
18
{
19
    /**
20
     * Promise status.
21
     *
22
     * @var string
23
     */
24
    private $state = HttpPromise::PENDING;
25
26
    /**
27
     * PSR7 received response.
28
     *
29
     * @var ResponseInterface
30
     */
31
    private $response;
32
33
    /**
34
     * Execution error.
35
     *
36
     * @var Exception
37
     */
38
    private $exception;
39
40
    /**
41
     * @var callable|null
42
     */
43
    private $onFulfilled;
44
45
    /**
46
     * @var callable|null
47
     */
48
    private $onRejected;
49
50
    /**
51
     * React Event Loop used for synchronous processing.
52
     *
53
     * @var LoopInterface
54
     */
55
    private $loop;
56
57
    public function __construct(LoopInterface $loop)
58 108
    {
59
        $this->loop = $loop;
60 108
    }
61
62 105
    /**
63 105
     * Allow to apply callable when the promise resolve.
64 108
     *
65
     * @param callable|null $onFulfilled
66 3
     * @param callable|null $onRejected
67 3
     *
68 3
     * @return Promise
69 108
     */
70 108
    public function then(callable $onFulfilled = null, callable $onRejected = null)
71 108
    {
72
        $newPromise = new self($this->loop);
73
74
        $onFulfilled = $onFulfilled !== null ? $onFulfilled : function (ResponseInterface $response) {
75
            return $response;
76
        };
77
78
        $onRejected = $onRejected !== null ? $onRejected : function (Exception $exception) {
79
            throw $exception;
80
        };
81 55
82
        $this->onFulfilled = function (ResponseInterface $response) use ($onFulfilled, $newPromise) {
83
            try {
84 53
                $newPromise->resolve($onFulfilled($response));
85 53
            } catch (Exception $exception) {
86 53
                $newPromise->reject($exception);
87 55
            }
88 2
        };
89 2
90
        $this->onRejected = function (Exception $exception) use ($onRejected, $newPromise) {
0 ignored issues
show
Unused Code introduced by
The parameter $exception is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
91 55
            try {
92
                $newPromise->resolve($onRejected($exception));
93 55
            } catch (Exception $exception) {
94
                $newPromise->reject($exception);
95
            }
96
        };
97
98
        if ($this->state === HttpPromise::FULFILLED) {
99 108
            $this->doResolve($this->response);
100
        }
101 108
102
        if ($this->state === HttpPromise::REJECTED) {
103
            $this->doReject($this->exception);
104
        }
105
106
        return $newPromise;
107
    }
108
109
    /**
110
     * Resolve this promise.
111 108
     *
112
     * @param ResponseInterface $response
113 108
     *
114
     * @internal
115 108
     */
116
    public function resolve(ResponseInterface $response)
117
    {
118
        if ($this->state !== HttpPromise::PENDING) {
119
            throw new \RuntimeException('Promise is already resolved');
120
        }
121 108
122
        $this->state = HttpPromise::FULFILLED;
123 108
        $this->response = $response;
124
        $this->doResolve($response);
125
    }
126 108
127 108
    private function doResolve(ResponseInterface $response)
128 108
    {
129
        $onFulfilled = $this->onFulfilled;
130 108
131 105
        if (null !== $onFulfilled) {
132 1
            $onFulfilled($response);
133
        }
134
    }
135 104
136
    /**
137 3
     * Reject this promise.
138
     *
139
     * @param Exception $exception
140
     *
141
     * @internal
142
     */
143
    public function reject(Exception $exception)
144
    {
145
        if ($this->state !== HttpPromise::PENDING) {
146
            throw new \RuntimeException('Promise is already resolved');
147
        }
148
149
        $this->state = HttpPromise::REJECTED;
150
        $this->exception = $exception;
151
        $this->doReject($exception);
152
    }
153
154
    private function doReject(Exception $exception)
155
    {
156
        $onRejected = $this->onRejected;
157
158
        if (null !== $onRejected) {
159
            $onRejected($exception);
160
        }
161
    }
162
163
    /**
164
     * {@inheritdoc}
165
     */
166
    public function getState()
167
    {
168
        return $this->state;
169
    }
170
171
    /**
172
     * {@inheritdoc}
173
     */
174
    public function wait($unwrap = true)
175
    {
176
        while (HttpPromise::PENDING === $this->getState()) {
177
            $this->loop->tick();
178
        }
179
180
        if ($unwrap) {
181
            if (HttpPromise::REJECTED == $this->getState()) {
182
                throw $this->exception;
183
            }
184
185
            return $this->response;
186
        }
187
    }
188
}
189