GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Test Failed
Push — master ( d54200...f2da15 )
by Charlotte
03:55
created

src/ProtocolParser.php (13 issues)

1
<?php
2
/**
3
 * Plasma Driver MySQL component
4
 * Copyright 2018 PlasmaPHP, All Rights Reserved
5
 *
6
 * Website: https://github.com/PlasmaPHP
7
 * License: https://github.com/PlasmaPHP/driver-mysql/blob/master/LICENSE
8
*/
9
10
namespace Plasma\Drivers\MySQL;
11
12
/**
13
 * The MySQL Protocol Parser.
14
 * @internal
15
 */
16
class ProtocolParser implements \Evenement\EventEmitterInterface {
17
    use \Evenement\EventEmitterTrait;
18
    
19
    /**
20
     * @var int
21
     */
22
    const STATE_INIT = 0;
23
    
24
    /**
25
     * @var int
26
     */
27
    const STATE_HANDSHAKE = 1;
28
    
29
    /**
30
     * @var int
31
     */
32
    const STATE_HANDSHAKE_ERROR = 2;
33
    
34
    /**
35
     * @var int
36
     */
37
    const STATE_AUTH = 5;
38
    
39
    /**
40
     * @var int
41
     */
42
    const STATE_AUTH_SENT = 6;
43
    
44
    /**
45
     * @var int
46
     */
47
    const STATE_AUTH_ERROR = 7;
48
    
49
    /**
50
     * @var int
51
     */
52
    const STATE_OK = 9;
53
    
54
    /**
55
     * @var int
56
     */
57
    const CLIENT_CAPABILITIES = (
58
        \Plasma\Drivers\MySQL\CapabilityFlags::CLIENT_LONG_PASSWORD |
59
        \Plasma\Drivers\MySQL\CapabilityFlags::CLIENT_LONG_FLAG |
60
        \Plasma\Drivers\MySQL\CapabilityFlags::CLIENT_LOCAL_FILES |
61
        \Plasma\Drivers\MySQL\CapabilityFlags::CLIENT_INTERACTIVE |
62
        \Plasma\Drivers\MySQL\CapabilityFlags::CLIENT_TRANSACTIONS |
63
        \Plasma\Drivers\MySQL\CapabilityFlags::CLIENT_SECURE_CONNECTION |
64
        \Plasma\Drivers\MySQL\CapabilityFlags::CLIENT_PROTOCOL_41 |
65
        \Plasma\Drivers\MySQL\CapabilityFlags::CLIENT_DEPRECATE_EOF
66
    );
67
    
68
    /**
69
     * @var int
70
     */
71
    const CLIENT_MAX_PACKET_SIZE = 0x1000000;
72
    
73
    /**
74
     * @var int
75
     */
76
    const CLIENT_CHARSET_NUMBER = 0x21;
77
    
78
    /**
79
     * @var \Plasma\Drivers\MySQL\Driver
80
     */
81
    protected $driver;
82
    
83
    /**
84
     * @var \React\Socket\ConnectionInterface
85
     */
86
    protected $connection;
87
    
88
    /**
89
     * @var int
90
     */
91
    protected $state = ProtocolParser::STATE_INIT;
92
    
93
    /**
94
     * @var string
95
     */
96
    protected $buffer = '';
97
    
98
    /**
99
     * The sequence ID is incremented with each packet and may wrap around.
100
     * It starts at 0 and is reset to 0 when a new command begins in the Command Phase.
101
     * @var int
102
     * @see https://dev.mysql.com/doc/internals/en/sequence-id.html
103
     */
104
    protected $sequenceID = 0;
105
    
106
    /**
107
     * @var \Plasma\Drivers\MySQL\Messages\HandshakeMessage|null
108
     */
109
    protected $handshakeMessage;
110
    
111
    /**
112
     * @var \Plasma\CommandInterface|null
113
     */
114
    protected $currentCommand;
115
    
116
    /**
117
     * @var callable|null
118
     */
119
    protected $parseCallback;
120
    
121
    /**
122
     * Constructor.
123
     * @param \Plasma\Drivers\MySQL\Driver       $driver
124
     * @param \React\Socket\ConnectionInterface  $connection
125
     */
126 13
    function __construct(\Plasma\Drivers\MySQL\Driver $driver, \React\Socket\ConnectionInterface $connection) {
127 13
        $this->driver = $driver;
128 13
        $this->connection = $connection;
129
        
130 13
        $this->addEvents();
131 13
    }
132
    
133
    /**
134
     * Invoke a command to execute.
135
     * @param \Plasma\CommandInterface|null  $command
136
     * @return void
137
     */
138 11
    function invokeCommand(?\Plasma\CommandInterface $command): void {
139 11
        if($command === null) {
140 3
            return;
141
        }
142
        
143 11
        $this->currentCommand = $command;
144 11
        $this->processCommand();
145 11
    }
146
    
147
    /**
148
     * Executes a command, without handling any aftermath.
149
     * The `onComplete` callback will be immediately invoked, regardless of the `waitForCompletion` value.
150
     * @param \Plasma\CommandInterface  $command
151
     * @return void
152
     */
153
    function executeCommand(\Plasma\CommandInterface $command): void {
154
        $this->processCommand($command);
155
    }
156
    
157
    /**
158
     * Marks the command itself as finished, if currently running.
159
     * @param \Plasma\Drivers\MySQL\Commands\CommandInterface  $command
160
     * @return void
161
     */
162 2
    function markCommandAsFinished(\Plasma\CommandInterface $command): void {
163 2
        if($command === $this->currentCommand) {
164 2
            $this->currentCommand = null;
165
        }
166
        
167 2
        $command->onComplete();
168 2
    }
169
    
170
    /**
171
     * Get the parser state.
172
     * @return int
173
     */
174 1
    function getState(): int {
175 1
        return $this->state;
176
    }
177
    
178
    /**
179
     * Get the handshake message, or null.
180
     * @return \Plasma\Drivers\MySQL\Messages\HandshakeMessage|null
181
     */
182 11
    function getHandshakeMessage(): ?\Plasma\Drivers\MySQL\Messages\HandshakeMessage {
183 11
        return $this->handshakeMessage;
184
    }
185
    
186
    /**
187
     * Sends a packet to the server.
188
     * @return void
189
     */
190 11
    function sendPacket(string $packet): void {
191 11
        $length = \Plasma\Drivers\MySQL\Messages\MessageUtility::writeInt3(\strlen($packet));
192 11
        $sequence = \Plasma\Drivers\MySQL\Messages\MessageUtility::writeInt1((++$this->sequenceID));
193
        
194 11
        $this->connection->write($length.$sequence.$packet);
195 11
    }
196
    
197
    /**
198
     * Sets the parse callback.
199
     * @param callable $callback
200
     * @return void
201
     */
202
    function setParseCallback(callable $callback): void {
203
        $this->parseCallback($callback);
204
    }
205
    
206
    /**
207
     * Processes a command.
208
     * @param \Plasma\CommandInterface|null  $command
209
     * @return void
210
     */
211 11
    protected function processCommand(?\Plasma\CommandInterface $command = null) {
212 11
        if($command === null && $this->currentCommand instanceof \Plasma\CommandInterface) {
213 11
            $command = $this->currentCommand;
214
            
215 11
            if($this->currentCommand instanceof \Plasma\Drivers\MySQL\Commands\CommandInterface) {
216 11
                $state = $command->setParserState();
217 11
                if($state !== -1) {
218 11
                    $this->state = $state;
219
                }
220
            }
221
        }
222
        
223 11
        if($command === null) {
224
            return;
225
        }
226
        
227 11
        if($command instanceof \Plasma\Drivers\MySQL\Commands\CommandInterface && $command->resetSequence()) {
228 6
            $this->sequenceID = -1;
229
        }
230
        
231 11
        \Plasma\Drivers\MySQL\Messages\MessageUtility::debug('Processing command '.get_class($command));
232
        
233 11
        $this->sendPacket($command->getEncodedMessage());
234
        
235 11
        if($command !== $this->currentCommand || !$command->waitForCompletion()) {
236
            \Plasma\Drivers\MySQL\Messages\MessageUtility::debug('Mark command as completed');
237
            $command->onComplete();
238
            
239
            if($command === $this->currentCommand) {
240
                $this->currentCommand = null;
241
            }
242
        }
243 11
    }
244
    
245
    /**
246
     * Processes the buffer.
247
     * @return void
248
     */
249 13
    protected function processBuffer() {
250 13
        \Plasma\Drivers\MySQL\Messages\MessageUtility::debug('ProtocolParser::processBuffer called');
251
        
252 13
        $buffer = $this->buffer;
253
        
254 13
        $length = \Plasma\Drivers\MySQL\Messages\MessageUtility::readInt3($buffer);
255 13
        $this->sequenceID = \Plasma\Drivers\MySQL\Messages\MessageUtility::readInt1($buffer);
256
        
257 13
        $stmtExecuteCmd = ($this->currentCommand instanceof \Plasma\Drivers\MySQL\Commands\StatementExecuteCommand);
258 13
        $isOKresponse = ($buffer[0] === "\x00" && $this->buffer[0] !== "\x00");
259
        
260 13
        if(!$stmtExecuteCmd) {
261 13
            if(\strlen($buffer) < $length) {
262
                \Plasma\Drivers\MySQL\Messages\MessageUtility::debug('returned, insufficent length: '.\strlen($buffer).', '.$length.' required');
263 13
                return;
264
            }
265
        } elseif(!$isOKresponse) {
266
            $buffer = $this->buffer;
267
        }
268
        
269 13
        \Plasma\Drivers\MySQL\Messages\MessageUtility::debug('Read length and sequence');
270
        
271
        //var_dump(unpack('C*', substr($this->buffer, 10)));
272
        
273
        /** @var \Plasma\Drivers\MySQL\Messages\MessageInterface  $message */
274 13
        $message = null;
275
        
276 13
        if($this->state === static::STATE_INIT) {
277 13
            $message = new \Plasma\Drivers\MySQL\Messages\HandshakeMessage($this);
278
        } else {
279 11
            $firstChar = \Plasma\Drivers\MySQL\Messages\MessageUtility::readBuffer($buffer, 1);
280 11
            \Plasma\Drivers\MySQL\Messages\MessageUtility::debug('Received Message char "'.$firstChar.'" (0x'.\dechex(\ord($firstChar)).') - buffer length: '.\strlen($buffer));
281
            
282
            switch(true) {
283 11
                case ($firstChar === \Plasma\Drivers\MySQL\Messages\ErrResponseMessage::getID()):
1 ignored issue
show
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
284 1
                    $message = new \Plasma\Drivers\MySQL\Messages\ErrResponseMessage($this);
285 1
                break;
1 ignored issue
show
Case breaking statement indented incorrectly; expected 20 spaces, found 16
Loading history...
286 10
                case ($firstChar === \Plasma\Drivers\MySQL\Messages\EOFMessage::getID() && $length < 6):
1 ignored issue
show
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
287
                    $message = new \Plasma\Drivers\MySQL\Messages\EOFMessage($this);
288
                break;
1 ignored issue
show
Case breaking statement indented incorrectly; expected 20 spaces, found 16
Loading history...
289 10
                case ($this->currentCommand instanceof \Plasma\Drivers\MySQL\Commands\PrepareCommand && $firstChar === "\x00"):
1 ignored issue
show
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
290 1
                    $message = new \Plasma\Drivers\MySQL\Messages\PrepareStatementOkMessage($this);
291 1
                break;
1 ignored issue
show
Case breaking statement indented incorrectly; expected 20 spaces, found 16
Loading history...
292 10
                case ($isOKresponse):
1 ignored issue
show
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
293 10
                    $message = new \Plasma\Drivers\MySQL\Messages\OkResponseMessage($this);
294 10
                break;
1 ignored issue
show
Case breaking statement indented incorrectly; expected 20 spaces, found 16
Loading history...
295
                default:
0 ignored issues
show
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
296 2
                    if($this->parseCallback !== null) {
1 ignored issue
show
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
297
                        $buffer = $firstChar.$buffer;
298
                        
299
                        $parse = $this->parseCallback;
300
                        $this->parseCallback = null;
301
                        
302
                        $buffer = $firstChar.$buffer;
303
                        
304
                        $caller = new \Plasma\Drivers\MySQL\ProtocolOnNextCaller($this, $buffer);
305
                        $parse($caller);
306
                        
307
                        $buffer = $caller->getBuffer();
308
                    } else {
0 ignored issues
show
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
309 2
                        $buffer = $firstChar.$buffer;
310
                        
311 2
                        $caller = new \Plasma\Drivers\MySQL\ProtocolOnNextCaller($this, $buffer);
312 2
                        $this->currentCommand->onNext($caller);
313
                        
314 2
                        $buffer = $caller->getBuffer();
315
                    }
0 ignored issues
show
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
316 2
                break;
0 ignored issues
show
Case breaking statement indented incorrectly; expected 20 spaces, found 16
Loading history...
317
            }
318
        }
319
        
320 13
        if(!($message instanceof \Plasma\Drivers\MySQL\Messages\MessageInterface)) {
321 2
            $this->buffer = $buffer;
322
            
323 2
            if(\strlen($this->buffer) > 0) {
324
                $this->driver->getLoop()->futureTick(function () {
325 2
                    $this->processBuffer();
326 2
                });
327
            }
328
            
329 2
            return;
330
        }
331
        
332 13
        \Plasma\Drivers\MySQL\Messages\MessageUtility::debug('Received Message '.\get_class($message));
333
        
334 13
        $state = $message->setParserState();
335 13
        if($state !== -1) {
336 13
            $this->state = $state;
337
        }
338
        
339 13
        if($message instanceof \Plasma\Drivers\MySQL\Messages\HandshakeMessage) {
340 13
            $this->handshakeMessage = $message;
341
        }
342
        
343 13
        $this->handleMessage($buffer, $message);
344 13
    }
345
    
346
    /**
347
     * Handles an incoming message.
348
     * @param string                                           $buffer
349
     * @param \Plasma\Drivers\MySQL\Messages\MessageInterface  $message
350
     * @return void
351
     */
352 13
    function handleMessage(string &$buffer, \Plasma\Drivers\MySQL\Messages\MessageInterface $message) {
353
        try {
354 13
            $buffer = $message->parseMessage($buffer, $this);
355 13
            if($buffer === false) {
356
                \Plasma\Drivers\MySQL\Messages\MessageUtility::debug('returned handle (unsufficent buffer length)');
357
                return;
358
            }
359
            
360 13
            $this->buffer = $buffer;
361
            
362 13
            if($this->currentCommand !== null) {
363
                if(
364 11
                    ($message instanceof \Plasma\Drivers\MySQL\Messages\OkResponseMessage || $message instanceof \Plasma\Drivers\MySQL\Messages\EOFMessage)
365 11
                    && $this->currentCommand->hasFinished()
366
                ) {
367 10
                    $command = $this->currentCommand;
368 10
                    $this->currentCommand = null;
369
                    
370 10
                    $command->onComplete();
371 4
                } elseif($message instanceof \Plasma\Drivers\MySQL\Messages\ErrResponseMessage) {
372 1
                    $error = new \Plasma\Exception($message->errorMessage, $message->errorCode);
373
                    
374 1
                    $command = $this->currentCommand;
375 1
                    $this->currentCommand = null;
376
                    
377 1
                    $command->onError($error);
378
                } else {
379 3
                    $command = $this->currentCommand;
380 3
                    $command->onNext($message);
381
                    
382 3
                    if($command->hasFinished()) {
383 2
                        if($this->currentCommand === $command) {
384
                            $this->currentCommand = null;
385
                        }
386
                        
387 11
                        $command->onComplete();
388
                    }
389
                }
390 13
            } elseif($message instanceof \Plasma\Drivers\MySQL\Messages\ErrResponseMessage) {
391
                $error = new \Plasma\Exception($message->errorMessage, $message->errorCode);
392
                $this->emit('error', array($error));
393
            }
394
            
395 13
            $this->emit('message', array($message));
396
        } catch (\Plasma\Drivers\MySQL\Messages\ParseException $e) {
397
            $state = $e->getState();
398
            if($state !== null) {
399
                $this->state = $state;
400
            }
401
            
402
            $buffer = $e->getBuffer();
403
            if($buffer !== null) {
404
                $this->buffer = $buffer;
405
            }
406
            
407
            if($this->currentCommand !== null) {
408
                $this->currentCommand->onError($e);
409
            }
410
            
411
            $this->emit('error', array($e));
412
            $this->connection->close();
413
        }
414
        
415 13
        if(\strlen($this->buffer) > 0) {
416
            $this->driver->getLoop()->futureTick(function () {
417 1
                $this->processBuffer();
418 1
            });
419
        }
420 13
    }
421
    
422
    /**
423
     * Adds the events to the connection.
424
     * @return void
425
     */
426 13
    protected function addEvents() {
427
        $this->connection->on('data', function ($chunk) {
428 13
            $this->buffer .= $chunk;
429 13
            $this->processBuffer();
430 13
        });
431
        
432
        $this->connection->on('close', function () {
433 5
            $this->handleClose();
434 13
        });
435 13
    }
436
    
437
    /**
438
     * Connection close handler.
439
     * @return void
440
     */
441 5
    protected function handleClose() {
442 5
        if($this->state === static::STATE_AUTH || $this->state === static::STATE_AUTH_SENT) {
443
            $this->state = static::STATE_AUTH_ERROR;
444
        }
445 5
    }
446
}
447