Completed
Branch master (1d1574)
by Evgenij
18:38
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 129
    public function __construct(Configuration $configuration)
73
    {
74 129
        $this->socketBag = new SocketBag($this, $configuration->getConnectTimeout(), $configuration->getIoTimeout());
75 129
    }
76
77
    /** {@inheritdoc} */
78 125
    public function socketBag()
79
    {
80 125
        return $this->socketBag;
81
    }
82
83
    /** {@inheritdoc} */
84 92
    public function withEventHandler(EventHandlerInterface $handler = null)
85
    {
86 92
        $this->eventHandler = $handler;
87 92
    }
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 127
    public function isExecuting()
101
    {
102 127
        return $this->isExecuting;
103
    }
104
105
    /** {@inheritdoc} */
106 125
    public function executeRequest()
107 1
    {
108 125
        if ($this->isExecuting()) {
109 4
            throw new \BadMethodCallException('Request is already in progress');
110
        }
111
112 125
        $this->isRequestStopped = false;
113 125
        $this->solver           = $this->solver ?: new NoLimitationSolver();
114
115 125
        $this->isExecuting = true;
116
117 125
        $eventCaller = new EventCaller($this);
118
        try {
119 125
            if ($this->eventHandler) {
120 103
                $eventCaller->addHandler($this->eventHandler);
121 92
            }
122
123 125
            if ($this->solver instanceof EventHandlerInterface) {
124 4
                $eventCaller->addHandler($this->solver);
125 4
            }
126
127 125
            $this->initializeRequest($eventCaller);
128
129 125
            $eventCaller->addHandler($this);
130
131 125
            $this->solver->initialize($this);
132 125
            $this->doExecuteRequest($eventCaller);
133 57
            $this->solver->finalize($this);
134
135 57
            $this->terminateRequest();
136 125
        } catch (StopRequestExecuteException $e) {
137 4
            $this->isRequestStopInProgress = true;
138 4
            $this->disconnectItems($this->socketBag->getItems());
139 68
        } catch (SocketException $e) {
140 14
            foreach ($this->socketBag->getItems() as $item) {
141 14
                $eventCaller->setCurrentOperation($item);
142 14
                $eventCaller->callExceptionSubscribers($item, $e);
143 14
            }
144
145 14
            $this->disconnectItems($this->socketBag->getItems());
146 64
        } catch (\Exception $e) {
147 50
            $this->isExecuting = false;
148 50
            $this->emergencyShutdown();
149 50
            $this->solver->finalize($this);
150 50
            $this->terminateRequest();
151 50
            throw $e;
152
        }
153
154 75
        $this->solver->finalize($this);
155 75
        $this->terminateRequest();
156 75
        $this->isExecuting = false;
157 75
    }
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 116
    public function invokeEvent(Event $event)
171
    {
172 116
        if ($this->isRequestStopped && !$this->isRequestStopInProgress) {
173 4
            $this->isRequestStopInProgress = true;
174 4
            throw new StopRequestExecuteException();
175
        }
176 116
    }
177
178
    /**
179
     * Prepare executor for request
180
     *
181
     * @param EventCaller $eventCaller Event caller
182
     *
183
     * @return void
184
     */
185 125
    protected function initializeRequest(EventCaller $eventCaller)
186
    {
187
        // empty body
188 125
    }
189
190
    /**
191
     * Terminate request in executor
192
     *
193
     * @return void
194
     */
195 125
    protected function terminateRequest()
196
    {
197
        // empty body
198 125
    }
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 50
    private function emergencyShutdown()
224
    {
225 50
        foreach ($this->socketBag->getItems() as $item) {
226
            try {
227 50
                $item->getSocket()->close();
228 50
            } catch (\Exception $e) {
229
                // nothing required
230
            }
231
232 50
            $item->setMetadata(self::META_REQUEST_COMPLETE, true);
233 50
        }
234 50
    }
235
}
236