Completed
Push — master ( 95ea2a...2fea48 )
by Evgenij
02:43
created

AbstractClientIo::isEndOfFrameReached()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 4

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.2
cc 4
eloc 4
nc 5
nop 2
crap 4
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\Socket\Io;
11
12
use AsyncSockets\Exception\ConnectionException;
13
use AsyncSockets\Exception\FrameException;
14
use AsyncSockets\Exception\NetworkSocketException;
15
use AsyncSockets\Frame\FramePickerInterface;
16
use AsyncSockets\Frame\PartialFrame;
17
18
/**
19
 * Class AbstractClientIo
20
 */
21
abstract class AbstractClientIo extends AbstractIo
22
{
23
    /**
24
     * Disconnected state
25
     */
26
    const STATE_DISCONNECTED = 0;
27
28
    /**
29
     * Connected state
30
     */
31
    const STATE_CONNECTED = 1;
32
33
    /**
34
     * Unhandled portion of data at the end of frame
35
     *
36
     * @var string
37
     */
38
    private $unhandledData = '';
39
40
    /**
41
     * Socket state
42
     *
43
     * @var int
44
     */
45
    private $state = self::STATE_DISCONNECTED;
46
47
    /**
48
     * Amount of attempts to write data before treat request as failed
49
     *
50
     * @var int
51
     */
52
    private $writeAttempts = self::IO_ATTEMPTS;
53
54
    /**
55
     * Read raw data from network into given picker
56
     *
57
     * @param FramePickerInterface $picker Frame picker
58
     *
59
     * @return string Data after end of frame
60
     */
61
    abstract protected function readRawDataIntoPicker(FramePickerInterface $picker);
62
63
    /**
64
     * Write data to socket
65
     *
66
     * @param string $data Data to write
67
     *
68
     * @return int Number of written bytes
69
     */
70
    abstract protected function writeRawData($data);
71
72
    /**
73
     * Check whether given socket resource is connected
74
     *
75
     * @return bool
76
     */
77
    abstract protected function isConnected();
78
79
    /**
80
     * Return true if frame can be collected in nearest future, false otherwise
81
     *
82
     * @return bool
83
     */
84
    abstract protected function canReachFrame();
85
86
    /**
87
     * Return remote socket ip address
88
     *
89
     * @return string
90
     */
91
    abstract protected function getRemoteAddress();
92
93
    /** {@inheritdoc} */
94 46
    final public function read(FramePickerInterface $picker)
95
    {
96 46
        $this->setConnectedState();
97 40
        $isEndOfFrameReached = $this->handleUnreadData($picker);
98 40
        if (!$isEndOfFrameReached) {
99 40
            $this->unhandledData = $this->readRawDataIntoPicker($picker);
100
101 39
            $isEndOfFrameReached = $picker->isEof();
102 39
            if (!$isEndOfFrameReached && !$this->canReachFrame()) {
103 5
                throw new FrameException($picker, $this->socket, 'Failed to receive desired frame.');
104
            }
105 35
        }
106
107 35
        $frame = $picker->createFrame();
108 35
        if (!$isEndOfFrameReached) {
109 2
            $frame = new PartialFrame($frame);
110 2
        }
111
112 35
        return $frame;
113
    }
114
115
    /** {@inheritdoc} */
116 21
    final public function write($data)
117
    {
118 21
        $this->setConnectedState();
119
120 13
        $result              = $this->writeRawData($data);
121 6
        $this->writeAttempts = $result > 0 ? self::IO_ATTEMPTS : $this->writeAttempts - 1;
122
123 6
        $this->throwNetworkSocketExceptionIf(
124 6
            !$this->writeAttempts && $result !== strlen($data),
125
            'Failed to send data.'
126 6
        );
127
128 6
        return $result;
129
    }
130
131
    /**
132
     * Verify, that we are in connected state
133
     *
134
     * @return void
135
     * @throws ConnectionException
136
     */
137 67
    private function setConnectedState()
138
    {
139 67
        $resource = $this->socket->getStreamResource();
140 67
        if (!is_resource($resource)) {
141 12
            $message = $this->state === self::STATE_CONNECTED ?
142 12
                'Connection was unexpectedly closed.' :
143 12
                'Can not start io operation on uninitialized socket.';
144 12
            throw new ConnectionException($this->socket, $message);
145
        }
146
147 55
        if ($this->state !== self::STATE_CONNECTED) {
148 55
            if (!$this->isConnected()) {
149 2
                throw new ConnectionException($this->socket, 'Connection refused.');
150
            }
151
152 53
            $this->state = self::STATE_CONNECTED;
153 53
        }
154 53
    }
155
156
    /**
157
     * Checks that we are in connected state
158
     *
159
     * @param string $message Message to pass in exception
160
     *
161
     * @return void
162
     * @throws NetworkSocketException
163
     */
164 2
    final protected function throwExceptionIfNotConnected($message)
165
    {
166 2
        $this->throwNetworkSocketExceptionIf(
167 2
            !$this->isConnected(),
168
            $message
169 2
        );
170 1
    }
171
172
    /**
173
     * Read unhandled data if there is something from the previous operation
174
     *
175
     * @param FramePickerInterface $picker Frame picker to use
176
     *
177
     * @return bool Flag whether it is the end of the frame
178
     */
179 40
    private function handleUnreadData(FramePickerInterface $picker)
180
    {
181 40
        if ($this->unhandledData) {
182 9
            $this->unhandledData = $picker->pickUpData($this->unhandledData, $this->getRemoteAddress());
183 9
        }
184
185 40
        return $picker->isEof();
186
    }
187
}
188