AbstractRequestExecutor::executeRequest()   C
last analyzed

Complexity

Conditions 10
Paths 341

Size

Total Lines 56
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 41
CRAP Score 10

Importance

Changes 0
Metric Value
dl 0
loc 56
ccs 41
cts 41
cp 1
rs 5.0704
c 0
b 0
f 0
cc 10
eloc 37
nc 341
nop 1
crap 10

How to fix   Long Method    Complexity   

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