1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Dazzle\MySQL\Protocol; |
4
|
|
|
|
5
|
|
|
use BinPHP\BinSupport; |
6
|
|
|
use Dazzle\Event\BaseEventEmitter; |
7
|
|
|
use Dazzle\MySQL\Protocol\Command; |
8
|
|
|
use Dazzle\MySQL\Protocol\CommandInterface; |
9
|
|
|
use Dazzle\MySQL\Support\Queue\Queue; |
10
|
|
|
use Dazzle\MySQL\Support\Queue\QueueInterface; |
11
|
|
|
use Dazzle\Socket\SocketInterface; |
12
|
|
|
use Dazzle\Stream\StreamInterface; |
13
|
|
|
use Exception; |
14
|
|
|
use SplQueue; |
15
|
|
|
|
16
|
|
|
class ProtocolParser extends BaseEventEmitter |
17
|
|
|
{ |
18
|
|
|
/** |
19
|
|
|
* @var int |
20
|
|
|
*/ |
21
|
|
|
const PHASE_INIT = 1; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* @var int |
25
|
|
|
*/ |
26
|
|
|
const PHASE_AUTH_SENT = 2; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @var int |
30
|
|
|
*/ |
31
|
|
|
const PHASE_AUTH_ERR = 3; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @var int |
35
|
|
|
*/ |
36
|
|
|
const PHASE_HANDSHAKED = 4; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @var int |
40
|
|
|
*/ |
41
|
|
|
const RS_STATE_HEADER = 0; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @var int |
45
|
|
|
*/ |
46
|
|
|
const RS_STATE_FIELD = 1; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @var int |
50
|
|
|
*/ |
51
|
|
|
const RS_STATE_ROW = 2; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* @var int |
55
|
|
|
*/ |
56
|
|
|
const STATE_STANDBY = 0; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @var int |
60
|
|
|
*/ |
61
|
|
|
const STATE_BODY = 1; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @var string |
65
|
|
|
*/ |
66
|
|
|
protected $user = 'root'; |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @var string |
70
|
|
|
*/ |
71
|
|
|
protected $pass = ''; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* @var string |
75
|
|
|
*/ |
76
|
|
|
protected $dbname = ''; |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* @var CommandInterface |
80
|
|
|
*/ |
81
|
|
|
protected $currCommand; |
82
|
|
|
|
83
|
|
|
protected $debug = false; |
84
|
|
|
|
85
|
|
|
protected $state = 0; |
86
|
|
|
|
87
|
|
|
protected $phase = 0; |
88
|
|
|
|
89
|
|
|
public $seq = 0; |
90
|
|
|
public $clientFlags = 239237; |
91
|
|
|
|
92
|
|
|
public $warnCount; |
93
|
|
|
public $message; |
94
|
|
|
|
95
|
|
|
protected $maxPacketSize = 0x1000000; |
96
|
|
|
|
97
|
|
|
protected $charsetNumber = 0x21; |
98
|
|
|
|
99
|
|
|
protected $serverVersion; |
100
|
|
|
protected $threadId; |
101
|
|
|
protected $scramble; |
102
|
|
|
|
103
|
|
|
protected $serverCaps; |
104
|
|
|
protected $serverLang; |
105
|
|
|
protected $serverStatus; |
106
|
|
|
|
107
|
|
|
protected $rsState = 0; |
108
|
|
|
protected $pctSize = 0; |
109
|
|
|
protected $resultRows = []; |
110
|
|
|
protected $resultFields = []; |
111
|
|
|
|
112
|
|
|
protected $insertId; |
113
|
|
|
protected $affectedRows; |
114
|
|
|
|
115
|
|
|
public $protocalVersion = 0; |
116
|
|
|
|
117
|
|
|
protected $errno = 0; |
118
|
|
|
protected $errmsg = ''; |
119
|
|
|
|
120
|
|
|
protected $buffer = ''; |
121
|
|
|
protected $bufferPos = 0; |
122
|
|
|
|
123
|
|
|
protected $connectOptions; |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* @var StreamInterface |
127
|
|
|
*/ |
128
|
|
|
protected $stream; |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* @var QueueInterface |
132
|
|
|
*/ |
133
|
|
|
protected $executor; |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* @var SplQueue |
137
|
|
|
*/ |
138
|
|
|
protected $queue; |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* @param SocketInterface $stream |
142
|
|
|
* @param QueueInterface $executor |
143
|
|
|
* @param mixed[] $config |
144
|
|
|
*/ |
145
|
|
|
public function __construct(SocketInterface $stream, QueueInterface $executor, $config = []) |
146
|
|
|
{ |
147
|
|
|
$this->stream = $stream; |
148
|
|
|
$this->executor = $executor; |
149
|
|
|
$this->queue = new SplQueue($this); |
|
|
|
|
150
|
|
|
$this->configure($config); |
151
|
|
|
$executor->on('new', [ $this, 'handleNewCommand' ]); |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
public function start() |
155
|
|
|
{ |
156
|
|
|
$this->stream->on('data', [ $this, 'handleData' ]); |
157
|
|
|
$this->stream->on('close', [ $this, 'handleClose' ]); |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
public function handleNewCommand() |
161
|
|
|
{ |
162
|
|
|
if ($this->queue->isEmpty()) |
163
|
|
|
{ |
164
|
|
|
$this->nextRequest(); |
165
|
|
|
} |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
public function debug($message) |
169
|
|
|
{ |
170
|
|
|
if ($this->debug) |
171
|
|
|
{ |
172
|
|
|
$bt = debug_backtrace(); |
173
|
|
|
$caller = array_shift($bt); |
174
|
|
|
printf("[DEBUG] <%s:%d> %s\n", $caller['class'], $caller['line'], $message); |
175
|
|
|
} |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
public function handleData($stream, $data) |
179
|
|
|
{ |
180
|
|
|
$this->append($data); |
181
|
|
|
packet: |
182
|
|
|
if ($this->state === self::STATE_STANDBY) |
183
|
|
|
{ |
184
|
|
|
if ($this->length() < 4) |
185
|
|
|
{ |
186
|
|
|
return; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
$this->pctSize = BinSupport::bytes2int($this->read(3), true); |
|
|
|
|
190
|
|
|
$this->state = self::STATE_BODY; |
191
|
|
|
$this->seq = ord($this->read(1)) + 1; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
$len = $this->length(); |
195
|
|
|
|
196
|
|
|
if ($len < $this->pctSize) |
197
|
|
|
{ |
198
|
|
|
$this->debug('Buffer not enouth, return'); |
199
|
|
|
return; |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
$this->state = self::STATE_STANDBY; |
203
|
|
|
|
204
|
|
|
if ($this->phase === 0) |
205
|
|
|
{ |
206
|
|
|
$this->phase = self::PHASE_INIT; |
207
|
|
|
$this->protocalVersion = ord($this->read(1)); |
208
|
|
|
$this->debug(sprintf("Protocol Version: %d", $this->protocalVersion)); |
209
|
|
|
if ($this->protocalVersion === 0xFF) |
210
|
|
|
{ |
211
|
|
|
$fieldCount = $this->protocalVersion; |
212
|
|
|
$this->protocalVersion = 0; |
213
|
|
|
printf("Error:\n"); |
214
|
|
|
|
215
|
|
|
$this->rsState = self::RS_STATE_HEADER; |
216
|
|
|
$this->resultFields = []; |
217
|
|
|
$this->resultRows = []; |
218
|
|
|
if ($this->phase === self::PHASE_AUTH_SENT || $this->phase === self::PHASE_INIT) |
219
|
|
|
{ |
220
|
|
|
$this->phase = self::PHASE_AUTH_ERR; |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
goto field; |
224
|
|
|
} |
225
|
|
|
if (($p = $this->search("\x00")) === false) |
226
|
|
|
{ |
227
|
|
|
return; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
$options = &$this->connectOptions; |
231
|
|
|
|
232
|
|
|
$options['serverVersion'] = $this->read($p, 1); |
233
|
|
|
$options['threadId'] = BinSupport::bytes2int($this->read(4), true); |
234
|
|
|
$this->scramble = $this->read(8, 1); |
235
|
|
|
$options['ServerCaps'] = BinSupport::bytes2int($this->read(2), true); |
236
|
|
|
$options['serverLang'] = ord($this->read(1)); |
237
|
|
|
$options['serverStatus'] = BinSupport::bytes2int($this->read(2, 13), true); |
238
|
|
|
$restScramble = $this->read(12, 1); |
239
|
|
|
$this->scramble .= $restScramble; |
240
|
|
|
|
241
|
|
|
$this->nextRequest(true); |
242
|
|
|
} |
243
|
|
|
else |
244
|
|
|
{ |
245
|
|
|
$fieldCount = ord($this->read(1)); |
246
|
|
|
field: |
247
|
|
|
if ($fieldCount === 0xFF) |
248
|
|
|
{ |
249
|
|
|
//error packet |
250
|
|
|
$u = unpack('v', $this->read(2)); |
251
|
|
|
$this->errno = $u[1]; |
252
|
|
|
$state = $this->read(6); |
|
|
|
|
253
|
|
|
$this->errmsg = $this->read($this->pctSize - $len + $this->length()); |
254
|
|
|
$this->debug(sprintf("Error Packet:%d %s\n", $this->errno, $this->errmsg)); |
255
|
|
|
|
256
|
|
|
$this->nextRequest(); |
257
|
|
|
$this->onError(); |
258
|
|
|
} |
259
|
|
|
else if ($fieldCount === 0x00) |
260
|
|
|
{ |
261
|
|
|
$this->debug('Ok Packet'); |
262
|
|
|
|
263
|
|
|
$isAuthenticated = false; |
264
|
|
|
if ($this->phase === self::PHASE_AUTH_SENT) |
265
|
|
|
{ |
266
|
|
|
$this->phase = self::PHASE_HANDSHAKED; |
267
|
|
|
$isAuthenticated = true; |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
$this->affectedRows = $this->parseEncodedBinSupport(); |
271
|
|
|
$this->insertId = $this->parseEncodedBinSupport(); |
272
|
|
|
|
273
|
|
|
$u = unpack('v', $this->read(2)); |
274
|
|
|
$this->serverStatus = $u[1]; |
275
|
|
|
|
276
|
|
|
$u = unpack('v', $this->read(2)); |
277
|
|
|
$this->warnCount = $u[1]; |
278
|
|
|
|
279
|
|
|
$this->message = $this->read($this->pctSize - $len + $this->length()); |
280
|
|
|
|
281
|
|
|
if ($isAuthenticated) |
282
|
|
|
{ |
283
|
|
|
$this->onAuthenticated(); |
284
|
|
|
} |
285
|
|
|
else |
286
|
|
|
{ |
287
|
|
|
$this->onSuccess(); |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
$this->debug(sprintf("AffectedRows: %d, InsertId: %d, WarnCount:%d", $this->affectedRows, $this->insertId, $this->warnCount)); |
291
|
|
|
$this->nextRequest(); |
292
|
|
|
|
293
|
|
|
} |
294
|
|
|
// EOF |
295
|
|
|
else if ($fieldCount === 0xFE) |
296
|
|
|
{ |
297
|
|
|
$this->debug('EOF Packet'); |
298
|
|
|
if ($this->rsState === self::RS_STATE_ROW) |
299
|
|
|
{ |
300
|
|
|
$this->debug('result done'); |
301
|
|
|
|
302
|
|
|
$this->nextRequest(); |
303
|
|
|
$this->onResultDone(); |
304
|
|
|
} |
305
|
|
|
else |
306
|
|
|
{ |
307
|
|
|
++ $this->rsState; |
308
|
|
|
} |
309
|
|
|
} |
310
|
|
|
//Data packet |
311
|
|
|
else |
312
|
|
|
{ |
313
|
|
|
$this->debug('Data Packet'); |
314
|
|
|
$this->prepend(chr($fieldCount)); |
315
|
|
|
|
316
|
|
|
if ($this->rsState === self::RS_STATE_HEADER) |
317
|
|
|
{ |
318
|
|
|
$this->debug('Header packet of Data packet'); |
319
|
|
|
$extra = $this->parseEncodedBinSupport(); |
|
|
|
|
320
|
|
|
//var_dump($extra); |
321
|
|
|
$this->rsState = self::RS_STATE_FIELD; |
322
|
|
|
} |
323
|
|
|
else if ($this->rsState === self::RS_STATE_FIELD) |
324
|
|
|
{ |
325
|
|
|
$this->debug('Field packet of Data packet'); |
326
|
|
|
$field = [ |
327
|
|
|
'catalog' => $this->parseEncodedString(), |
328
|
|
|
'db' => $this->parseEncodedString(), |
329
|
|
|
'table' => $this->parseEncodedString(), |
330
|
|
|
'org_table' => $this->parseEncodedString(), |
331
|
|
|
'name' => $this->parseEncodedString(), |
332
|
|
|
'org_name' => $this->parseEncodedString() |
333
|
|
|
]; |
334
|
|
|
|
335
|
|
|
$this->skip(1); |
336
|
|
|
$u = unpack('v', $this->read(2)); |
337
|
|
|
$field['charset'] = $u[1]; |
338
|
|
|
|
339
|
|
|
$u = unpack('v', $this->read(4)); |
340
|
|
|
$field['length'] = $u[1]; |
341
|
|
|
|
342
|
|
|
$field['type'] = ord($this->read(1)); |
343
|
|
|
|
344
|
|
|
$u = unpack('v', $this->read(2)); |
345
|
|
|
$field['flags'] = $u[1]; |
346
|
|
|
$field['decimals'] = ord($this->read(1)); |
347
|
|
|
//var_dump($field); |
348
|
|
|
$this->resultFields[] = $field; |
349
|
|
|
|
350
|
|
|
} |
351
|
|
|
else if ($this->rsState === self::RS_STATE_ROW) |
352
|
|
|
{ |
353
|
|
|
$this->debug('Row packet of Data packet'); |
354
|
|
|
$row = []; |
355
|
|
|
for ($i = 0, $nf = sizeof($this->resultFields); $i < $nf; ++$i) |
356
|
|
|
{ |
357
|
|
|
$row[$this->resultFields[$i]['name']] = $this->parseEncodedString(); |
358
|
|
|
} |
359
|
|
|
$this->resultRows[] = $row; |
360
|
|
|
$command = $this->queue->dequeue(); |
361
|
|
|
//$command->emit('success', [ $command, [ $row ] ]); |
|
|
|
|
362
|
|
|
$this->queue->unshift($command); |
363
|
|
|
} |
364
|
|
|
} |
365
|
|
|
} |
366
|
|
|
$this->restBuffer($this->pctSize - $len + $this->length()); |
367
|
|
|
goto packet; |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
protected function onError() |
371
|
|
|
{ |
372
|
|
|
$command = $this->queue->dequeue(); |
373
|
|
|
$error = new Exception($this->errmsg, $this->errno); |
374
|
|
|
$command->setError($error); |
375
|
|
|
$command->emit('error', [ $command, $error ]); |
376
|
|
|
$this->errmsg = ''; |
377
|
|
|
$this->errno = 0; |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
protected function onResultDone() |
381
|
|
|
{ |
382
|
|
|
$command = $this->queue->dequeue(); |
383
|
|
|
|
384
|
|
|
$command->resultRows = $this->resultRows; |
385
|
|
|
$command->resultFields = $this->resultFields; |
386
|
|
|
|
387
|
|
|
$command->emit('success', [ $command, $this->resultRows ]); |
388
|
|
|
|
389
|
|
|
$this->rsState = self::RS_STATE_HEADER; |
390
|
|
|
$this->resultRows = $this->resultFields = []; |
391
|
|
|
} |
392
|
|
|
|
393
|
|
|
protected function onSuccess() |
394
|
|
|
{ |
395
|
|
|
$command = $this->queue->dequeue(); |
396
|
|
|
|
397
|
|
|
$command->affectedRows = $this->affectedRows; |
398
|
|
|
$command->insertId = $this->insertId; |
399
|
|
|
$command->warnCount = $this->warnCount; |
400
|
|
|
$command->message = $this->message; |
401
|
|
|
|
402
|
|
|
$command->emit('success', [ $command ]); |
403
|
|
|
} |
404
|
|
|
|
405
|
|
|
protected function onAuthenticated() |
406
|
|
|
{ |
407
|
|
|
$command = $this->queue->dequeue(); |
408
|
|
|
$command->emit('success', [ $command, $this->connectOptions ]); |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
protected function handleClose() |
412
|
|
|
{ |
413
|
|
|
$this->emit('close'); |
414
|
|
|
if ($this->queue->count()) |
415
|
|
|
{ |
416
|
|
|
$command = $this->queue->dequeue(); |
417
|
|
|
if ($command->equals(Command::QUIT)) |
418
|
|
|
{ |
419
|
|
|
$command->emit('success', [ $command ]); |
420
|
|
|
} |
421
|
|
|
} |
422
|
|
|
} |
423
|
|
|
|
424
|
|
|
public function append($str) |
425
|
|
|
{ |
426
|
|
|
$this->buffer .= $str; |
427
|
|
|
} |
428
|
|
|
|
429
|
|
|
public function prepend($str) |
430
|
|
|
{ |
431
|
|
|
$this->buffer = $str . substr($this->buffer, $this->bufferPos); |
432
|
|
|
$this->bufferPos = 0; |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
public function read($len, $skiplen = 0) |
436
|
|
|
{ |
437
|
|
|
if (strlen($this->buffer) - $this->bufferPos - $len - $skiplen < 0) |
438
|
|
|
{ |
439
|
|
|
throw new \LogicException('Logic Error'); |
440
|
|
|
} |
441
|
|
|
$buffer = substr($this->buffer, $this->bufferPos, $len); |
442
|
|
|
$this->bufferPos += $len; |
443
|
|
|
if ($skiplen) |
444
|
|
|
{ |
445
|
|
|
$this->bufferPos += $skiplen; |
446
|
|
|
} |
447
|
|
|
|
448
|
|
|
return $buffer; |
449
|
|
|
} |
450
|
|
|
|
451
|
|
|
public function skip($len) |
452
|
|
|
{ |
453
|
|
|
$this->bufferPos += $len; |
454
|
|
|
} |
455
|
|
|
|
456
|
|
|
public function restBuffer($len) |
457
|
|
|
{ |
458
|
|
|
if (strlen($this->buffer) === ($this->bufferPos+$len)) |
459
|
|
|
{ |
460
|
|
|
$this->buffer = ''; |
461
|
|
|
} |
462
|
|
|
else |
463
|
|
|
{ |
464
|
|
|
$this->buffer = substr($this->buffer,$this->bufferPos+$len); |
465
|
|
|
} |
466
|
|
|
$this->bufferPos = 0; |
467
|
|
|
} |
468
|
|
|
|
469
|
|
|
public function length() |
470
|
|
|
{ |
471
|
|
|
return strlen($this->buffer) - $this->bufferPos; |
472
|
|
|
} |
473
|
|
|
|
474
|
|
|
public function search($what) |
475
|
|
|
{ |
476
|
|
|
if (($p = strpos($this->buffer, $what, $this->bufferPos)) !== false) |
477
|
|
|
{ |
478
|
|
|
return $p - $this->bufferPos; |
479
|
|
|
} |
480
|
|
|
|
481
|
|
|
return false; |
482
|
|
|
} |
483
|
|
|
/* end of buffer operation APIs */ |
484
|
|
|
|
485
|
|
|
public function authenticate() |
486
|
|
|
{ |
487
|
|
|
if ($this->phase !== self::PHASE_INIT) |
488
|
|
|
{ |
489
|
|
|
return; |
490
|
|
|
} |
491
|
|
|
$this->phase = self::PHASE_AUTH_SENT; |
492
|
|
|
|
493
|
|
|
$clientFlags = |
494
|
|
|
Protocol::CLIENT_LONG_PASSWORD | |
495
|
|
|
Protocol::CLIENT_LONG_FLAG | |
496
|
|
|
Protocol::CLIENT_LOCAL_FILES | |
497
|
|
|
Protocol::CLIENT_PROTOCOL_41 | |
498
|
|
|
Protocol::CLIENT_INTERACTIVE | |
499
|
|
|
Protocol::CLIENT_TRANSACTIONS | |
500
|
|
|
Protocol::CLIENT_SECURE_CONNECTION | |
501
|
|
|
Protocol::CLIENT_MULTI_RESULTS | |
502
|
|
|
Protocol::CLIENT_MULTI_STATEMENTS | |
503
|
|
|
Protocol::CLIENT_CONNECT_WITH_DB; |
504
|
|
|
|
505
|
|
|
$packet = pack('VVc', $clientFlags, $this->maxPacketSize, $this->charsetNumber) |
506
|
|
|
. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" |
507
|
|
|
. $this->user . "\x00" |
508
|
|
|
. $this->getAuthToken($this->scramble, $this->pass) |
509
|
|
|
. ($this->dbname ? $this->dbname . "\x00" : ''); |
510
|
|
|
|
511
|
|
|
$this->sendPacket($packet); |
512
|
|
|
$this->debug('Auth packet sent'); |
513
|
|
|
} |
514
|
|
|
|
515
|
|
|
public function getAuthToken($scramble, $password = '') |
516
|
|
|
{ |
517
|
|
|
if ($password === '') |
518
|
|
|
{ |
519
|
|
|
return "\x00"; |
520
|
|
|
} |
521
|
|
|
$token = sha1($scramble . sha1($hash1 = sha1($password, true), true), true) ^ $hash1; |
522
|
|
|
|
523
|
|
|
return $this->buildLenEncodedBinSupport($token); |
524
|
|
|
} |
525
|
|
|
|
526
|
|
|
/** |
527
|
|
|
* Builds length-encoded BinSupport string |
528
|
|
|
* @param string String |
529
|
|
|
* @return string Resulting BinSupport string |
530
|
|
|
*/ |
531
|
|
|
public function buildLenEncodedBinSupport($s) |
532
|
|
|
{ |
533
|
|
|
if ($s === null) |
534
|
|
|
{ |
535
|
|
|
return "\251"; |
536
|
|
|
} |
537
|
|
|
|
538
|
|
|
$l = strlen($s); |
539
|
|
|
|
540
|
|
|
if ($l <= 250) |
541
|
|
|
{ |
542
|
|
|
return chr($l) . $s; |
543
|
|
|
} |
544
|
|
|
|
545
|
|
|
if ($l <= 0xFFFF) |
546
|
|
|
{ |
547
|
|
|
return "\252" . BinSupport::int2bytes(2, true) . $s; |
|
|
|
|
548
|
|
|
} |
549
|
|
|
|
550
|
|
|
if ($l <= 0xFFFFFF) |
551
|
|
|
{ |
552
|
|
|
return "\254" . BinSupport::int2bytes(3, true) . $s; |
|
|
|
|
553
|
|
|
} |
554
|
|
|
|
555
|
|
|
return BinSupport::int2bytes(8, $l, true) . $s; |
556
|
|
|
} |
557
|
|
|
|
558
|
|
|
/** |
559
|
|
|
* Parses length-encoded BinSupport integer |
560
|
|
|
* @return integer Result |
|
|
|
|
561
|
|
|
*/ |
562
|
|
|
public function parseEncodedBinSupport() |
563
|
|
|
{ |
564
|
|
|
$f = ord($this->read(1)); |
565
|
|
|
if ($f <= 250) |
566
|
|
|
{ |
567
|
|
|
return $f; |
568
|
|
|
} |
569
|
|
|
if ($f === 251) |
570
|
|
|
{ |
571
|
|
|
return null; |
572
|
|
|
} |
573
|
|
|
if ($f === 255) |
574
|
|
|
{ |
575
|
|
|
return false; |
576
|
|
|
} |
577
|
|
|
if ($f === 252) |
578
|
|
|
{ |
579
|
|
|
return BinSupport::bytes2int($this->read(2), true); |
580
|
|
|
} |
581
|
|
|
if ($f === 253) |
582
|
|
|
{ |
583
|
|
|
return BinSupport::bytes2int($this->read(3), true); |
584
|
|
|
} |
585
|
|
|
|
586
|
|
|
return BinSupport::bytes2int($this->read(8), true); |
587
|
|
|
} |
588
|
|
|
|
589
|
|
|
/** |
590
|
|
|
* Parse length-encoded string |
591
|
|
|
* @return integer Result |
|
|
|
|
592
|
|
|
*/ |
593
|
|
|
public function parseEncodedString() |
594
|
|
|
{ |
595
|
|
|
$l = $this->parseEncodedBinSupport(); |
596
|
|
|
if (($l === null) || ($l === false)) |
597
|
|
|
{ |
598
|
|
|
return $l; |
599
|
|
|
} |
600
|
|
|
|
601
|
|
|
return $this->read($l); |
602
|
|
|
} |
603
|
|
|
|
604
|
|
|
/** |
605
|
|
|
* Send packet to the server. |
606
|
|
|
* |
607
|
|
|
* @param string $packet |
608
|
|
|
* @return bool |
609
|
|
|
*/ |
610
|
|
|
protected function sendPacket($packet) |
611
|
|
|
{ |
612
|
|
|
return $this->stream->write(BinSupport::int2bytes(3, strlen($packet), true) . chr($this->seq++) . $packet); |
613
|
|
|
} |
614
|
|
|
|
615
|
|
|
/** |
616
|
|
|
* Parse next request. |
617
|
|
|
* |
618
|
|
|
* @param bool $isHandshake |
619
|
|
|
* @return bool |
620
|
|
|
*/ |
621
|
|
|
protected function nextRequest($isHandshake = false) |
622
|
|
|
{ |
623
|
|
|
if (!$isHandshake && $this->phase != self::PHASE_HANDSHAKED) |
624
|
|
|
{ |
625
|
|
|
return false; |
626
|
|
|
} |
627
|
|
|
if (!$this->executor->isEmpty()) |
628
|
|
|
{ |
629
|
|
|
$command = $this->executor->dequeue(); |
630
|
|
|
$this->queue->enqueue($command); |
631
|
|
|
|
632
|
|
|
if ($command->equals(Command::INIT_AUTHENTICATE)) |
633
|
|
|
{ |
634
|
|
|
$this->authenticate(); |
635
|
|
|
} |
636
|
|
|
else |
637
|
|
|
{ |
638
|
|
|
$this->seq = 0; |
639
|
|
|
$this->sendPacket(chr($command->getID()) . $command->getSQL()); |
640
|
|
|
} |
641
|
|
|
} |
642
|
|
|
|
643
|
|
|
return true; |
644
|
|
|
} |
645
|
|
|
|
646
|
|
|
/** |
647
|
|
|
* Configure protocol parser. |
648
|
|
|
* |
649
|
|
|
* @param mixed[] $options |
650
|
|
|
*/ |
651
|
|
|
protected function configure($options) |
652
|
|
|
{ |
653
|
|
|
foreach ($options as $option => $value) |
654
|
|
|
{ |
655
|
|
|
if (property_exists($this, $option)) |
656
|
|
|
{ |
657
|
|
|
$this->$option = $value; |
658
|
|
|
} |
659
|
|
|
} |
660
|
|
|
} |
661
|
|
|
} |
662
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.