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 ( 9c8b5e...1077ea )
by Charlotte
03:13
created

src/ProtocolParser.php (1 issue)

Labels
Severity
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 string[]
108
     */
109
    protected $messageClasses = array();
110
    
111
    /**
112
     * @var \Plasma\Drivers\MySQL\Messages\HandshakeMessage|null
113
     */
114
    protected $handshakeMessage;
115
    
116
    /**
117
     * @var \Plasma\CommandInterface|null
118
     */
119
    protected $currentCommand;
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->registerMessages();
131 13
        $this->addEvents();
132 13
    }
133
    
134
    /**
135
     * Invoke a command to execute.
136
     * @param \Plasma\CommandInterface|null  $command
137
     * @return void
138
     */
139 11
    function invokeCommand(?\Plasma\CommandInterface $command): void {
140 11
        if($command === null) {
141 3
            return;
142
        }
143
        
144 11
        $this->currentCommand = $command;
145 11
        $this->processCommand();
146 11
    }
147
    
148
    /**
149
     * Executes a command, without handling any aftermath.
150
     * The `onComplete` callback will be immediately invoked, regardless of the `waitForCompletion` value.
151
     * @param \Plasma\CommandInterface  $command
152
     * @return void
153
     */
154
    function executeCommand(\Plasma\CommandInterface $command): void {
155
        $this->processCommand($command);
156
    }
157
    
158
    /**
159
     * Marks the command itself as finished, if currently running.
160
     * @param \Plasma\Drivers\MySQL\Commands\CommandInterface  $command
161
     * @return void
162
     */
163 2
    function markCommandAsFinished(\Plasma\CommandInterface $command): void {
164 2
        if($command === $this->currentCommand) {
165 2
            $this->currentCommand = null;
166
        }
167
        
168 2
        $command->onComplete();
169 2
    }
170
    
171
    /**
172
     * Get the parser state.
173
     * @return int
174
     */
175 1
    function getState(): int {
176 1
        return $this->state;
177
    }
178
    
179
    /**
180
     * Get the handshake message, or null.
181
     * @return \Plasma\Drivers\MySQL\Messages\HandshakeMessage|null
182
     */
183 11
    function getHandshakeMessage(): ?\Plasma\Drivers\MySQL\Messages\HandshakeMessage {
184 11
        return $this->handshakeMessage;
185
    }
186
    
187
    /**
188
     * Sends a packet to the server.
189
     * @return void
190
     */
191 11
    function sendPacket(string $packet): void {
192 11
        $length = \Plasma\Drivers\MySQL\Messages\MessageUtility::writeInt3(\strlen($packet));
193 11
        $sequence = \Plasma\Drivers\MySQL\Messages\MessageUtility::writeInt1((++$this->sequenceID));
194
        
195 11
        $this->connection->write($length.$sequence.$packet);
196 11
    }
197
    
198
    /**
199
     * Processes a command.
200
     * @param \Plasma\CommandInterface|null  $command
201
     * @return void
202
     */
203 11
    protected function processCommand(?\Plasma\CommandInterface $command = null) {
204 11
        if($command === null && $this->currentCommand instanceof \Plasma\CommandInterface) {
205 11
            $command = $this->currentCommand;
206
            
207 11
            if($this->currentCommand instanceof \Plasma\Drivers\MySQL\Commands\CommandInterface) {
208 11
                $state = $command->setParserState();
209 11
                if($state !== -1) {
210 11
                    $this->state = $state;
211
                }
212
            }
213
        }
214
        
215 11
        if($command === null) {
216
            return;
217
        }
218
        
219 11
        if($command instanceof \Plasma\Drivers\MySQL\Commands\CommandInterface && $command->resetSequence()) {
220 6
            $this->sequenceID = -1;
221
        }
222
        
223 11
        \Plasma\Drivers\MySQL\Messages\MessageUtility::debug('Processing command '.get_class($command));
224
        
225 11
        $this->sendPacket($command->getEncodedMessage());
226
        
227 11
        if($command !== $this->currentCommand || !$command->waitForCompletion()) {
228
            \Plasma\Drivers\MySQL\Messages\MessageUtility::debug('Mark command as completed');
229
            $command->onComplete();
230
            
231
            if($command === $this->currentCommand) {
232
                $this->currentCommand = null;
233
            }
234
        }
235 11
    }
236
    
237
    /**
238
     * Processes the buffer.
239
     * @return void
240
     */
241 13
    protected function processBuffer() {
242 13
        \Plasma\Drivers\MySQL\Messages\MessageUtility::debug('ProtocolParser::processBuffer called');
243
        
244 13
        $buffer = $this->buffer;
245 13
        $bufferLen = \strlen($this->buffer);
246
        
247 13
        $length = \Plasma\Drivers\MySQL\Messages\MessageUtility::readInt3($buffer);
248 13
        $this->sequenceID = \Plasma\Drivers\MySQL\Messages\MessageUtility::readInt1($buffer);
249
        
250 13
        if($bufferLen < $length) {
251
            \Plasma\Drivers\MySQL\Messages\MessageUtility::debug('returned, insufficent length: '.$bufferLen.', '.$length.' required');
252
            return;
253
        }
254
        
255
        /** @var \Plasma\Drivers\MySQL\Messages\MessageInterface  $message */
256 13
        $message = null;
257
        
258 13
        if($this->state === static::STATE_INIT) {
259 13
            $message = new \Plasma\Drivers\MySQL\Messages\HandshakeMessage($this);
260
        } else {
261 11
            $firstChar = \Plasma\Drivers\MySQL\Messages\MessageUtility::readBuffer($buffer, 1);
262 11
            \Plasma\Drivers\MySQL\Messages\MessageUtility::debug('Received Message char "'.$firstChar.'" (0x'.\dechex(\ord($firstChar)).') - buffer length: '.\strlen($buffer));
263
            
264 11
            if($firstChar === "\xFE" && $this->state < static::STATE_OK) {
265
                $message = new \Plasma\Drivers\MySQL\Messages\AuthSwitchRequestMessage($this);
266 11
            } elseif($this->currentCommand instanceof \Plasma\Drivers\MySQL\Commands\PrepareCommand && $firstChar === "\x00") {
267 1
                $message = new \Plasma\Drivers\MySQL\Messages\PrepareStatementOkMessage($this);
268
            } elseif(
269 11
                isset($this->messageClasses[$firstChar]) &&
270 11
                ($firstChar !== \Plasma\Drivers\MySQL\Messages\EOFMessage::getID() || $length < 6) &&
271 11
                ($firstChar !== \Plasma\Drivers\MySQL\Messages\AuthMoreDataMessage::getID() || $this->state < static::STATE_OK)
272
            ) {
273 11
                $cl = $this->messageClasses[$firstChar];
274 11
                $message = new $cl($this);
275 2
            } elseif($this->state === static::STATE_OK && $this->currentCommand !== null) {
276 2
                $buffer = $firstChar.$buffer;
277
                
278 2
                $caller = new \Plasma\Drivers\MySQL\ProtocolOnNextCaller($this, $buffer);
279 2
                $this->currentCommand->onNext($caller);
280
                
281 2
                $buffer = $caller->getBuffer();
282
            }
283
        }
284
        
285 13
        if(!($message instanceof \Plasma\Drivers\MySQL\Messages\MessageInterface)) {
286 2
            $this->buffer = $buffer;
287
            
288 2
            if(\strlen($this->buffer) > 0) {
289
                $this->driver->getLoop()->futureTick(function () {
290 2
                    $this->processBuffer();
291 2
                });
292
            }
293
            
294 2
            return;
295
        }
296
        
297 13
        \Plasma\Drivers\MySQL\Messages\MessageUtility::debug('Received Message '.\get_class($message));
298
        
299 13
        $state = $message->setParserState();
300 13
        if($state !== -1) {
301 13
            $this->state = $state;
302
        }
303
        
304 13
        if($message instanceof \Plasma\Drivers\MySQL\Messages\HandshakeMessage) {
305 13
            $this->handshakeMessage = $message;
306
        }
307
        
308 13
        $this->handleMessage($buffer, $message);
309 13
    }
310
    
311
    /**
312
     * Handles an incoming message.
313
     * @param string                                           $buffer
314
     * @param \Plasma\Drivers\MySQL\Messages\MessageInterface  $message
315
     * @return void
316
     */
317 13
    function handleMessage(string &$buffer, \Plasma\Drivers\MySQL\Messages\MessageInterface $message) {
318
        try {
319 13
            $buffer = $message->parseMessage($buffer, $this);
320 13
            if($buffer === false) {
321
                \Plasma\Drivers\MySQL\Messages\MessageUtility::debug('returned handle (unsufficent buffer length)');
322
                return;
323
            }
324
            
325 13
            $this->buffer = $buffer;
326
            
327 13
            if($this->currentCommand !== null) {
328
                if(
329 11
                    ($message instanceof \Plasma\Drivers\MySQL\Messages\OkResponseMessage || $message instanceof \Plasma\Drivers\MySQL\Messages\EOFMessage)
330 11
                    && $this->currentCommand->hasFinished()
331
                ) {
332 10
                    $command = $this->currentCommand;
333 10
                    $this->currentCommand = null;
334
                    
335 10
                    $command->onComplete();
336 4
                } elseif($message instanceof \Plasma\Drivers\MySQL\Messages\ErrResponseMessage) {
337 1
                    $error = new \Plasma\Exception($message->errorMessage, $message->errorCode);
338
                    
339 1
                    $command = $this->currentCommand;
340 1
                    $this->currentCommand = null;
341
                    
342 1
                    $command->onError($error);
343
                } else {
344 3
                    $command = $this->currentCommand;
345 3
                    $command->onNext($message);
346
                    
347 3
                    if($command->hasFinished()) {
348 2
                        if($this->currentCommand === $command) {
349
                            $this->currentCommand = null;
350
                        }
351
                        
352 11
                        $command->onComplete();
353
                    }
354
                }
355 13
            } elseif($message instanceof \Plasma\Drivers\MySQL\Messages\ErrResponseMessage) {
356
                $error = new \Plasma\Exception($message->errorMessage, $message->errorCode);
357
                $this->emit('error', array($error));
358
            }
359
            
360 13
            $this->emit('message', array($message));
361
        } catch (\Plasma\Drivers\MySQL\Messages\ParseException $e) {
362
            $state = $e->getState();
363
            if($state !== null) {
364
                $this->state = $state;
365
            }
366
            
367
            $buffer = $e->getBuffer();
368
            if($buffer !== null) {
369
                $this->buffer = $buffer;
370
            }
371
            
372
            if($this->currentCommand !== null) {
373
                $this->currentCommand->onError($e);
374
            }
375
            
376
            $this->emit('error', array($e));
377
            $this->connection->close();
378
        }
379
        
380 13
        if(\strlen($this->buffer) > 0) {
0 ignored issues
show
It seems like $this->buffer can also be of type true; however, parameter $string of strlen() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

380
        if(\strlen(/** @scrutinizer ignore-type */ $this->buffer) > 0) {
Loading history...
381
            $this->driver->getLoop()->futureTick(function () {
382 1
                $this->processBuffer();
383 1
            });
384
        }
385 13
    }
386
    
387
    /**
388
     * Registers the message classes.
389
     * @return void
390
     */
391 13
    protected function registerMessages() {
392
        $classes = array(
393 13
            (\Plasma\Drivers\MySQL\Messages\AuthMoreDataMessage::getID()) => \Plasma\Drivers\MySQL\Messages\AuthMoreDataMessage::class,
394 13
            (\Plasma\Drivers\MySQL\Messages\EOFMessage::getID()) => \Plasma\Drivers\MySQL\Messages\EOFMessage::class,
395 13
            (\Plasma\Drivers\MySQL\Messages\ErrResponseMessage::getID()) => \Plasma\Drivers\MySQL\Messages\ErrResponseMessage::class,
396 13
            (\Plasma\Drivers\MySQL\Messages\OkResponseMessage::getID()) => \Plasma\Drivers\MySQL\Messages\OkResponseMessage::class
397
        );
398
        
399 13
        foreach($classes as $id => $class) {
400 13
            $this->messageClasses[$id] = $class;
401
        }
402 13
    }
403
    
404
    /**
405
     * Adds the events to the connection.
406
     * @return void
407
     */
408 13
    protected function addEvents() {
409
        $this->connection->on('data', function ($chunk) {
410 13
            $this->buffer .= $chunk;
411 13
            $this->processBuffer();
412 13
        });
413
        
414
        $this->connection->on('close', function () {
415 5
            $this->handleClose();
416 13
        });
417 13
    }
418
    
419
    /**
420
     * Connection close handler.
421
     * @return void
422
     */
423 5
    protected function handleClose() {
424 5
        if($this->state === static::STATE_AUTH || $this->state === static::STATE_AUTH_SENT) {
425
            $this->state = static::STATE_AUTH_ERROR;
426
        }
427 5
    }
428
}
429