Completed
Push — EventLoopContainer ( c9a84d...6e77fa )
by Vasily
04:04
created

Connection::onRead()   F

Complexity

Conditions 31
Paths 0

Size

Total Lines 147
Code Lines 120

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 31
eloc 120
c 2
b 1
f 0
nc 0
nop 0
dl 0
loc 147
rs 2

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
namespace PHPDaemon\Clients\Gibson;
3
4
use PHPDaemon\Network\ClientConnection;
5
use PHPDaemon\Utils\Binary;
6
7
/**
8
 * Class Connection
9
 */
10
class Connection extends ClientConnection
11
{
12
    /**
13
     * Generic error while executing the query.
14
     */
15
    const REPL_ERR = 0x00;
16
    /**
17
     * Specified key was not found.
18
     */
19
    const REPL_ERR_NOT_FOUND = 0x01;
20
    /**
21
     * Expected a number ( TTL or TIME ) but the specified value was invalid.
22
     */
23
    const REPL_ERR_NAN = 0x02;
24
    /**
25
     * The server reached configuration memory limit and will not accept any new value until its freeing routine will be executed.
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 130 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
26
     */
27
    const REPL_ERR_MEM = 0x03;
28
    /**
29
     * The specificed key was locked by a OP_LOCK or a OP_MLOCK query.
30
     */
31
    const REPL_ERR_LOCKED = 0x04;
32
    /**
33
     * Query succesfully executed, no data follows.
34
     */
35
    const REPL_OK = 0x05;
36
    /**
37
     * Query succesfully executed, value data follows.
38
     */
39
    const REPL_VAL = 0x06;
40
    /**
41
     * Query succesfully executed, multiple key => value data follows.
42
     */
43
    const REPL_KVAL = 0x07;
44
    const STATE_PACKET_HDR = 0x01;
45
    const STATE_PACKET_DATA = 0x02;
46
    /**
47
     * Raw string data follows.
48
     */
49
    const GB_ENC_PLAIN = 0x00;
50
    /**
51
     * Compressed data, this is a reserved value not used for replies.
52
     */
53
    const GB_ENC_LZF = 0x01;
54
    /**
55
     * Packed long number follows, four bytes for 32bit architectures, eight bytes for 64bit.
56
     */
57
    const GB_ENC_NUMBER = 0x02;
58
    /**
59
     * @var string error message
60
     */
61
    public $error;
62
    public $responseCode;
63
    public $encoding;
64
    public $responseLength;
65
    public $result;
66
    public $isFinal = false;
67
    public $totalNum;
68
    public $readedNum;
69
    /**
70
     * @var integer Default low mark. Minimum number of bytes in buffer
71
     */
72
    protected $lowMark = 2;
73
    protected $maxQueue = 10;
74
75
    /**
76
     * Called when the connection is handshaked (at low-level), and peer is ready to recv. data
77
     * @return void
78
     */
79
    public function onReady()
80
    {
81
        parent::onReady();
82
        $this->setWatermark(null, $this->pool->maxAllowedPacket);
83
    }
84
85
    /**
86
     * @TODO isFinal
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
87
     * @return boolean
88
     */
89
    public function isFinal()
90
    {
91
        return $this->isFinal;
92
    }
93
94
    /**
95
     * @TODO getTotalNum
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
96
     * @return integer
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
97
     */
98
    public function getTotalNum()
99
    {
100
        return $this->totalNum;
101
    }
102
103
    /**
104
     * @TODO getReadedNum
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
105
     * @return integer
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
106
     */
107
    public function getReadedNum()
108
    {
109
        return $this->readedNum;
110
    }
111
112
    /**
113
     * @TODO getResponseCode
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
114
     * @return integer
115
     */
116
    public function getResponseCode()
117
    {
118
        return $this->responseCode;
119
    }
120
121
    /**
122
     * onRead
123
     * @return void
124
     */
125
    protected function onRead()
126
    {
127
        start:
128
        if ($this->state === static::STATE_STANDBY) {
129
            if (($hdr = $this->readExact(2)) === false) {
130
                return; // not enough data
131
            }
132
133
            $u = unpack('S', $hdr);
134
            $this->responseCode = $u[1];
135
            $this->state = static::STATE_PACKET_HDR;
136
        }
137
        if ($this->state === static::STATE_PACKET_HDR) {
138
            if ($this->responseCode === static::REPL_KVAL) {
139
                $this->result = [];
140
                if (($hdr = $this->readExact(9)) === false) {
141
                    return; // not enough data
142
                }
143
                $this->encoding = Binary::getByte($hdr);
144
                $this->responseLength = Binary::getDword($hdr, true) - 4;
145
                $this->totalNum = Binary::getDword($hdr, true);
146
                $this->readedNum = 0;
147
                $this->state = static::STATE_PACKET_DATA;
148
            } else {
149
                if (($hdr = $this->lookExact(5)) === false) {
150
                    return; // not enough data
151
                }
152
                $this->encoding = Binary::getByte($hdr);
153
                $pl = Binary::getDword($hdr, true);
154
                if ($this->getInputLength() < 5 + $pl) {
155
                    return; // not enough data
156
                }
157
                $this->drain(5);
158
                $this->responseLength = $pl;
159
                if ($this->responseLength > $this->pool->maxAllowedPacket) {
160
                    $this->log('max-allowed-packet (' . $this->pool->config->maxallowedpacket->getHumanValue() . ') exceed, aborting connection');
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 146 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
161
                    $this->finish();
162
                    return;
163
                }
164
                if ($this->responseCode === static::REPL_ERR_NOT_FOUND) {
165
                    $this->drain($this->responseLength);
166
                    $this->result = null;
167
                    $this->isFinal = true;
168
                    $this->totalNum = 0;
169
                    $this->readedNum = 0;
170
                    $this->executeCb();
171
                } elseif ($this->responseCode === static::REPL_OK) {
172
                    $this->drain($this->responseLength);
173
                    $this->result = true;
174
                    $this->isFinal = true;
175
                    $this->totalNum = 0;
176
                    $this->readedNum = 0;
177
                    $this->executeCb();
178
                } elseif (($this->responseCode === static::REPL_ERR_MEM) ||
179
                    ($this->responseCode === static::REPL_ERR_NAN) ||
180
                    ($this->responseCode === static::REPL_ERR_LOCKED)
181
                ) {
182
                    $this->drain($this->responseLength);
183
                    $this->result = false;
184
                    $this->isFinal = true;
185
                    $this->totalNum = 0;
186
                    $this->readedNum = 0;
187
                    $this->executeCb();
188
                } else {
189
                    if ($this->responseCode === static::REPL_KVAL && $this->totalNum <= 0) {
190
                        $this->drain($this->responseLength);
191
                        $this->isFinal = true;
192
                        $this->totalNum = 0;
193
                        $this->readedNum = 0;
194
                        $this->result = [];
195
                        $this->executeCb();
196
                    } else {
197
                        $this->state = static::STATE_PACKET_DATA;
198
                    }
199
                }
200
            }
201
        }
202
        if ($this->state === static::STATE_PACKET_DATA) {
203
            if ($this->responseCode === static::REPL_KVAL) {
204
                $keyAdded = false;
205
                nextElement:
206
                $l = $this->getInputLength();
207
                if ($l < 9) {
208
                    goto cursorCall;
209
                }
210
                if (($hdr = $this->lookExact($o = 4)) === false) {
211
                    goto cursorCall;
212
                }
213
                $keyLen = Binary::getDword($hdr, true);
214
                if (($key = $this->lookExact($keyLen, $o)) === false) {
215
                    goto cursorCall;
216
                }
217
                $o += $keyLen;
218
                if (($encoding = $this->lookExact(1, $o)) === false) {
219
                    goto cursorCall;
220
                }
221
                $encoding = ord($encoding);
222
                ++$o;
223
                if (($hdr = $this->lookExact(4, $o)) === false) {
224
                    goto cursorCall;
225
                }
226
                $o += 4;
227
                $valLen = Binary::getDword($hdr, true);
228
                if ($o + $valLen > $l) {
229
                    goto cursorCall;
230
                }
231
                $this->drain($o);
232
                if ($encoding === static::GB_ENC_NUMBER) {
233
                    $val = $this->read($valLen);
234
                    $this->result[$key] = $valLen === 8
235
                        ? Binary::getQword($val, true)
236
                        : Binary::getDword($val, true);
237
                } else {
238
                    $this->result[$key] = $this->read($valLen);
239
                }
240
                $keyAdded = true;
241
                if (++$this->readedNum >= $this->totalNum) {
242
                    $this->isFinal = true;
243
                    $this->executeCb();
244
                    goto start;
245
                } else {
246
                    goto nextElement;
247
                }
248
                cursorCall:
249
                if ($keyAdded) {
250
                    $this->onResponse->executeAndKeepOne($this);
251
                }
252
                return;
253
            } else {
254
                if (($this->result = $this->readExact($this->responseLength)) === false) {
255
                    $this->setWatermark($this->responseLength);
256
                    return;
257
                }
258
                $this->setWatermark(2, $this->pool->maxAllowedPacket);
259
                if ($this->encoding === static::GB_ENC_NUMBER) {
260
                    $this->result = $this->responseLength === 8
261
                        ? Binary::getQword($this->result, true)
262
                        : Binary::getDword($this->result, true);
263
                }
264
                $this->isFinal = true;
265
                $this->totalNum = 1;
266
                $this->readedNum = 1;
267
                $this->executeCb();
268
            }
269
        }
270
        goto start;
271
    }
272
273
    /**
274
     * @TODO executeCb
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
275
     * @return void
276
     */
277
    protected function executeCb()
278
    {
279
        $this->state = static::STATE_STANDBY;
280
        $this->onResponse->executeOne($this);
281
        $this->encoding = null;
282
        $this->responseLength = null;
283
        $this->result = null;
284
        $this->totalNum = null;
285
        $this->readedNum = null;
286
        $this->isFinal = false;
287
        $this->checkFree();
288
    }
289
}
290