Completed
Branch 0.4-dev (54e67b)
by Evgenij
02:52
created

AbstractRequestExecutor::executeRequest()   C

Complexity

Conditions 9
Paths 171

Size

Total Lines 52
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 39
CRAP Score 9

Importance

Changes 3
Bugs 0 Features 1
Metric Value
c 3
b 0
f 1
dl 0
loc 52
ccs 39
cts 39
cp 1
rs 6.0761
cc 9
eloc 35
nc 171
nop 0
crap 9

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Async sockets
4
 *
5
 * @copyright Copyright (c) 2015-2016, Efimov Evgenij <[email protected]>
6
 *
7
 * This source file is subject to the MIT license that is bundled
8
 * with this source code in the file LICENSE.
9
 */
10
namespace AsyncSockets\RequestExecutor;
11
12
use AsyncSockets\Configuration\Configuration;
13
use AsyncSockets\Event\Event;
14
use AsyncSockets\Exception\SocketException;
15
use AsyncSockets\Exception\StopRequestExecuteException;
16
use AsyncSockets\RequestExecutor\Metadata\RequestDescriptor;
17
use AsyncSockets\RequestExecutor\Metadata\SocketBag;
18
use AsyncSockets\RequestExecutor\Pipeline\EventCaller;
19
20
/**
21
 * Class AbstractRequestExecutor
22
 */
23
abstract class AbstractRequestExecutor implements RequestExecutorInterface, EventHandlerInterface
24
{
25
    /**
26
     * Flag, indicating stopping request
27
     *
28
     * @var bool
29
     */
30
    private $isRequestStopped = false;
31
32
    /**
33
     * Flag, indicating stopping request is in progress
34
     *
35
     * @var bool
36
     */
37
    private $isRequestStopInProgress = false;
38
39
    /**
40
     * Decider for request limitation
41
     *
42
     * @var LimitationSolverInterface
43
     */
44
    protected $solver;
45
46
    /**
47
     * EventHandlerInterface
48
     *
49
     * @var EventHandlerInterface
50
     */
51
    protected $eventHandler;
52
53
    /**
54
     * Socket bag
55
     *
56
     * @var SocketBag
57
     */
58
    protected $socketBag;
59
60
    /**
61
     * Flag whether request is executing
62
     *
63
     * @var bool
64
     */
65
    private $isExecuting = false;
66
67
    /**
68
     * AbstractRequestExecutor constructor.
69
     *
70
     * @param Configuration $configuration Configuration for executor
71
     */
72 137
    public function __construct(Configuration $configuration)
73
    {
74 137
        $this->socketBag = new SocketBag($this, $configuration->getConnectTimeout(), $configuration->getIoTimeout());
75 137
    }
76
77
    /** {@inheritdoc} */
78 133
    public function socketBag()
79
    {
80 133
        return $this->socketBag;
81
    }
82
83
    /** {@inheritdoc} */
84 100
    public function withEventHandler(EventHandlerInterface $handler = null)
85
    {
86 100
        $this->eventHandler = $handler;
87 100
    }
88
89
    /** {@inheritdoc} */
90 12
    public function withLimitationSolver(LimitationSolverInterface $solver)
91
    {
92 12
        if ($this->isExecuting()) {
93 4
            throw new \BadMethodCallException('Can not change limitation solver during request processing');
94
        }
95
96 8
        $this->solver = $solver;
97 8
    }
98
99
    /** {@inheritdoc} */
100 135
    public function isExecuting()
101
    {
102 135
        return $this->isExecuting;
103
    }
104
105
    /** {@inheritdoc} */
106 133
    public function executeRequest()
107 1
    {
108 133
        if ($this->isExecuting()) {
109 4
            throw new \BadMethodCallException('Request is already in progress');
110
        }
111
112 133
        $this->isRequestStopped = false;
113 133
        $this->solver           = $this->solver ?: new NoLimitationSolver();
114
115 133
        $this->isExecuting = true;
116
117 133
        $eventCaller = new EventCaller($this);
118
        try {
119 133
            if ($this->eventHandler) {
120 111
                $eventCaller->addHandler($this->eventHandler);
121 100
            }
122
123 133
            if ($this->solver instanceof EventHandlerInterface) {
124 4
                $eventCaller->addHandler($this->solver);
125 4
            }
126
127 133
            $this->initializeRequest($eventCaller);
128
129 133
            $eventCaller->addHandler($this);
130
131 133
            $this->solver->initialize($this);
132 133
            $this->doExecuteRequest($eventCaller);
133 57
            $this->solver->finalize($this);
134
135 57
            $this->terminateRequest();
136 133
        } catch (StopRequestExecuteException $e) {
137 4
            $this->isRequestStopInProgress = true;
138 4
            $this->disconnectItems($this->socketBag->getItems());
139 76
        } catch (SocketException $e) {
140 18
            foreach ($this->socketBag->getItems() as $item) {
141 18
                $eventCaller->setCurrentOperation($item);
142 18
                $eventCaller->callExceptionSubscribers($item, $e);
143 18
            }
144
145 18
            $this->disconnectItems($this->socketBag->getItems());
146 72
        } catch (\Exception $e) {
147 54
            $this->isExecuting = false;
148 54
            $this->emergencyShutdown();
149 54
            $this->solver->finalize($this);
150 54
            $this->terminateRequest();
151 54
            throw $e;
152
        }
153
154 79
        $this->solver->finalize($this);
155 79
        $this->terminateRequest();
156 79
        $this->isExecuting = false;
157 79
    }
158
159
    /** {@inheritdoc} */
160 6
    public function stopRequest()
161
    {
162 6
        if (!$this->isExecuting()) {
163 2
            throw new \BadMethodCallException('Can not stop inactive request');
164
        }
165
166 4
        $this->isRequestStopped = true;
167 4
    }
168
169
    /** {@inheritdoc} */
170 124
    public function invokeEvent(Event $event)
171
    {
172 124
        if ($this->isRequestStopped && !$this->isRequestStopInProgress) {
173 4
            $this->isRequestStopInProgress = true;
174 4
            throw new StopRequestExecuteException();
175
        }
176 124
    }
177
178
    /**
179
     * Prepare executor for request
180
     *
181
     * @param EventCaller $eventCaller Event caller
182
     *
183
     * @return void
184
     */
185 133
    protected function initializeRequest(EventCaller $eventCaller)
186
    {
187
        // empty body
188 133
    }
189
190
    /**
191
     * Terminate request in executor
192
     *
193
     * @return void
194
     */
195 133
    protected function terminateRequest()
196
    {
197
        // empty body
198 133
    }
199
200
    /**
201
     * Execute network request
202
     *
203
     * @param EventCaller $eventCaller Event caller object
204
     *
205
     * @return void
206
     */
207
    abstract protected function doExecuteRequest(EventCaller $eventCaller);
208
209
    /**
210
     * Disconnect given sockets
211
     *
212
     * @param RequestDescriptor[] $items Sockets' operations to disconnect
213
     *
214
     * @return mixed
215
     */
216
    abstract protected function disconnectItems(array $items);
217
218
    /**
219
     * Shutdown all sockets in case of unhandled exception
220
     *
221
     * @return void
222
     */
223 54
    private function emergencyShutdown()
224
    {
225 54
        foreach ($this->socketBag->getItems() as $item) {
226
            try {
227 54
                $item->getSocket()->close();
228 54
            } catch (\Exception $e) {
229
                // nothing required
230
            }
231
232 54
            $item->setMetadata(self::META_REQUEST_COMPLETE, true);
233 54
        }
234 54
    }
235
}
236