DisconnectStage::dispatchFinalizeEvent()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 9
c 0
b 0
f 0
ccs 7
cts 7
cp 1
rs 9.6666
cc 1
eloc 5
nc 1
nop 1
crap 1
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\Pipeline;
11
12
use AsyncSockets\Event\EventType;
13
use AsyncSockets\Exception\SocketException;
14
use AsyncSockets\RequestExecutor\ExecutionContext;
15
use AsyncSockets\RequestExecutor\Metadata\RequestDescriptor;
16
use AsyncSockets\RequestExecutor\RequestExecutorInterface;
17
use AsyncSockets\Socket\AsyncSelector;
18
19
/**
20
 * Class DisconnectStage
21
 */
22
class DisconnectStage extends AbstractStage
23
{
24
    /**
25
     * Selector
26
     *
27
     * @var AsyncSelector
28
     */
29
    private $selector;
30
31
    /**
32
     * DisconnectStage constructor.
33
     *
34
     * @param RequestExecutorInterface $executor         Request executor
35
     * @param EventCaller              $eventCaller      Event caller
36
     * @param ExecutionContext         $executionContext Execution context
37
     * @param AsyncSelector            $selector         Async selector
38
     */
39 141
    public function __construct(
40
        RequestExecutorInterface $executor,
41
        EventCaller $eventCaller,
42
        ExecutionContext $executionContext,
43
        AsyncSelector $selector = null
44
    ) {
45 141
        parent::__construct($executor, $eventCaller, $executionContext);
46 141
        $this->selector = $selector;
47 141
    }
48
49
    /** {@inheritdoc} */
50 89
    public function processStage(array $requestDescriptors)
51
    {
52 89
        foreach ($requestDescriptors as $descriptor) {
53 87
            $this->disconnectSingleSocket($descriptor);
54 79
            $this->processForgottenDescriptor($descriptor);
55 81
        }
56
57 81
        return $requestDescriptors;
58
    }
59
60
    /**
61
     * Disconnect given socket
62
     *
63
     * @param RequestDescriptor $descriptor Operation object
64
     *
65
     * @return void
66
     */
67 87
    private function disconnectSingleSocket(RequestDescriptor $descriptor)
68
    {
69 87
        $meta = $descriptor->getMetadata();
70
71 87
        if ($meta[RequestExecutorInterface::META_REQUEST_COMPLETE]) {
72 5
            return;
73
        }
74
75 86
        if (!$this->isDisconnectRequired($descriptor)) {
76 2
            return;
77
        }
78
79 84
        $this->disconnect($descriptor);
80 72
    }
81
82
    /**
83
     * Disconnects given socket descriptor
84
     *
85
     * @param RequestDescriptor $descriptor Socket descriptor
86
     *
87
     * @return void
88
     */
89 84
    public function disconnect(RequestDescriptor $descriptor)
90
    {
91 84
        $meta   = $descriptor->getMetadata();
92 84
        $socket = $descriptor->getSocket();
93
94 84
        $descriptor->setMetadata(RequestExecutorInterface::META_REQUEST_COMPLETE, true);
95
        try {
96 84
            $socket->close();
97 79
            if ($meta[ RequestExecutorInterface::META_CONNECTION_FINISH_TIME ] !== null) {
98 71
                $this->callSocketSubscribers(
99 71
                    $descriptor,
100 71
                    $this->createEvent($descriptor, EventType::DISCONNECTED)
101 71
                );
102 63
            }
103 84
        } catch (SocketException $e) {
104 9
            $this->callExceptionSubscribers($descriptor, $e);
105
        }
106
107 80
        $this->dispatchFinalizeEvent($descriptor);
108 72
    }
109
110
    /**
111
     * Remove given descriptor from selector
112
     *
113
     * @param RequestDescriptor $operation
114
     *
115
     * @return void
116
     */
117 73
    private function removeOperationsFromSelector(RequestDescriptor $operation)
118
    {
119 73
        if ($this->selector) {
120 39
            $this->selector->removeAllSocketOperations($operation);
121 39
        }
122 73
    }
123
124
    /**
125
     * Check whether given socket should be disconnected
126
     *
127
     * @param RequestDescriptor $descriptor Socket descriptor
128
     *
129
     * @return bool
130
     */
131 86
    private function isDisconnectRequired(RequestDescriptor $descriptor)
132
    {
133 86
        $socket = $descriptor->getSocket();
134 86
        $meta   = $descriptor->getMetadata();
135
136 86
        return !$meta[RequestExecutorInterface::META_KEEP_ALIVE] || (
137 2
                feof($socket->getStreamResource()) !== false ||
138 2
                !stream_socket_get_name($socket->getStreamResource(), true)
139 86
            );
140
    }
141
142
    /**
143
     * Marks forgotten socket as complete
144
     *
145
     * @param RequestDescriptor $descriptor The descriptor
146
     *
147
     * @return void
148
     */
149 79
    private function processForgottenDescriptor(RequestDescriptor $descriptor)
150
    {
151 79
        $meta = $descriptor->getMetadata();
152 79
        if (!$meta[RequestExecutorInterface::META_KEEP_ALIVE] || !$descriptor->isForgotten()) {
153 78
            return;
154
        }
155
156 1
        $descriptor->setMetadata(RequestExecutorInterface::META_REQUEST_COMPLETE, true);
157 1
        $this->dispatchFinalizeEvent($descriptor);
158 1
    }
159
160
    /**
161
     * Dispatches finalize event for socket
162
     *
163
     * @param RequestDescriptor $descriptor Descriptor for dispatching event
164
     *
165
     * @return void
166
     */
167 81
    private function dispatchFinalizeEvent(RequestDescriptor $descriptor)
168
    {
169 81
        $this->callSocketSubscribers(
170 81
            $descriptor,
171 81
            $this->createEvent($descriptor, EventType::FINALIZE)
172 81
        );
173
174 73
        $this->removeOperationsFromSelector($descriptor);
175 73
    }
176
}
177