4
use GuzzleHttp\Event\BeforeEvent;
5
use GuzzleHttp\Event\ErrorEvent;
6
use GuzzleHttp\Event\CompleteEvent;
7
use GuzzleHttp\Event\EndEvent;
8
use GuzzleHttp\Exception\StateException;
9
use GuzzleHttp\Exception\RequestException;
10
use GuzzleHttp\Message\FutureResponse;
11
use GuzzleHttp\Message\MessageFactoryInterface;
12
use GuzzleHttp\Ring\Future\FutureInterface;
15
* Responsible for transitioning requests through lifecycle events.
21
private $maxTransitions;
23
public function __construct(
25
MessageFactoryInterface $messageFactory,
28
$this->mf = $messageFactory;
29
$this->maxTransitions = $maxTransitions;
30
$this->handler = $handler;
34
* Runs the state machine until a terminal state is entered or the
35
* optionally supplied $finalState is entered.
37
* @param Transaction $trans Transaction being transitioned.
39
* @throws \Exception if a terminal state throws an exception.
41
public function __invoke(Transaction $trans)
43
$trans->_transitionCount = 0;
46
$trans->state = 'before';
51
if (++$trans->_transitionCount > $this->maxTransitions) {
52
throw new StateException("Too many state transitions were "
53
. "encountered ({$trans->_transitionCount}). This likely "
54
. "means that a combination of event listeners are in an "
58
switch ($trans->state) {
59
case 'before': goto before;
60
case 'complete': goto complete;
61
case 'error': goto error;
62
case 'retry': goto retry;
63
case 'send': goto send;
65
default: throw new StateException("Invalid state: {$trans->state}");
70
$trans->request->getEmitter()->emit('before', new BeforeEvent($trans));
71
$trans->state = 'send';
72
if ((bool) $trans->response) {
73
$trans->state = 'complete';
75
} catch (\Exception $e) {
76
$trans->state = 'error';
77
$trans->exception = $e;
84
if ($trans->response instanceof FutureInterface) {
85
// Futures will have their own end events emitted when
89
$trans->state = 'end';
90
$trans->response->setEffectiveUrl($trans->request->getUrl());
91
$trans->request->getEmitter()->emit('complete', new CompleteEvent($trans));
92
} catch (\Exception $e) {
93
$trans->state = 'error';
94
$trans->exception = $e;
101
// Convert non-request exception to a wrapped exception
102
$trans->exception = RequestException::wrapException(
103
$trans->request, $trans->exception
105
$trans->state = 'end';
106
$trans->request->getEmitter()->emit('error', new ErrorEvent($trans));
107
// An intercepted request (not retried) transitions to complete
108
if (!$trans->exception && $trans->state !== 'retry') {
109
$trans->state = 'complete';
111
} catch (\Exception $e) {
112
$trans->state = 'end';
113
$trans->exception = $e;
120
$trans->response = null;
121
$trans->exception = null;
122
$trans->state = 'before';
127
$fn = $this->handler;
128
$trans->response = FutureResponse::proxy(
129
$fn(RingBridge::prepareRingRequest($trans)),
130
function ($value) use ($trans) {
131
RingBridge::completeRingResponse($trans, $value, $this->mf, $this);
133
return $trans->response;
140
$trans->request->getEmitter()->emit('end', new EndEvent($trans));
141
// Throw exceptions in the terminal event if the exception
142
// was not handled by an "end" event listener.
143
if ($trans->exception) {
144
if (!($trans->exception instanceof RequestException)) {
145
$trans->exception = RequestException::wrapException(
146
$trans->request, $trans->exception
149
throw $trans->exception;