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 ( d13c9c...463153 )
by Charlotte
03:30
created

src/ProtocolParser.php (3 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 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 12
    function __construct(\Plasma\Drivers\MySQL\Driver $driver, \React\Socket\ConnectionInterface $connection) {
127 12
        $this->driver = $driver;
128 12
        $this->connection = $connection;
129
        
130 12
        $this->registerMessages();
131 12
        $this->addEvents();
132 12
    }
133
    
134
    /**
135
     * Invoke a command to execute.
136
     * @param \Plasma\CommandInterface|null  $command
137
     * @return void
138
     */
139 10
    function invokeCommand(?\Plasma\CommandInterface $command): void {
140 10
        if($command === null) {
141 4
            return;
142
        }
143
        
144 10
        $this->currentCommand = $command;
145 10
        $this->processCommand();
146 10
    }
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 10
    function getHandshakeMessage(): ?\Plasma\Drivers\MySQL\Messages\HandshakeMessage {
184 10
        return $this->handshakeMessage;
185
    }
186
    
187
    /**
188
     * Sends a packet to the server.
189
     * @return void
190
     */
191 10
    function sendPacket(string $packet): void {
192 10
        $length = \Plasma\Drivers\MySQL\Messages\MessageUtility::writeInt3(\strlen($packet));
193 10
        $sequence = \Plasma\Drivers\MySQL\Messages\MessageUtility::writeInt1((++$this->sequenceID));
194
        
195 10
        $this->connection->write($length.$sequence.$packet);
196 10
    }
197
    
198
    /**
199
     * Processes a command.
200
     * @param \Plasma\CommandInterface|null  $command
201
     * @return void
202
     */
203 10
    protected function processCommand(?\Plasma\CommandInterface $command = null) {
204 10
        if($command === null && $this->currentCommand instanceof \Plasma\CommandInterface) {
205 10
            $command = $this->currentCommand;
206
            
207 10
            if($this->currentCommand instanceof \Plasma\Drivers\MySQL\Commands\CommandInterface) {
208 10
                $state = $command->setParserState();
209 10
                if($state !== -1) {
210 10
                    $this->state = $state;
211
                }
212
            }
213
        }
214
        
215 10
        if($command === null) {
216
            return;
217
        }
218
        
219 10
        if($command instanceof \Plasma\Drivers\MySQL\Commands\CommandInterface && $command->resetSequence()) {
220 5
            $this->sequenceID = -1;
221
        }
222
        
223 10
        echo 'Processing command '.get_class($command).PHP_EOL;
224
        
225 10
        $this->sendPacket($command->getEncodedMessage());
226
        
227 10
        if($command !== $this->currentCommand || !$command->waitForCompletion()) {
228
            echo 'Mark command as completed'.PHP_EOL;
229
            $command->onComplete();
230
            
231
            if($command === $this->currentCommand) {
232
                $this->currentCommand = null;
233
            }
234
        }
235 10
    }
236
    
237
    /**
238
     * Processes the buffer.
239
     * @return void
240
     */
241 12
    protected function processBuffer() {
242 12
        $buffer = $this->buffer;
243 12
        $bufferLen = \strlen($this->buffer);
244
        
245 12
        $length = \Plasma\Drivers\MySQL\Messages\MessageUtility::readInt3($buffer);
246 12
        $this->sequenceID = \Plasma\Drivers\MySQL\Messages\MessageUtility::readInt1($buffer);
247
        
248 12
        if($bufferLen < $length) {
249 1
            echo 'returned (insufficent length)'.PHP_EOL;
250 1
            return;
251
        }
252
        
253 12
        if($this->currentCommand instanceof \Plasma\Drivers\MySQL\Commands\PromiseCommand) {
254 2
            var_dump(unpack('C*', $buffer));
0 ignored issues
show
Security Debugging Code introduced by
var_dump(unpack('C*', $buffer)) looks like debug code. Are you sure you do not want to remove it?
Loading history...
255
        }
256
        
257
        /** @var \Plasma\Drivers\MySQL\Messages\MessageInterface  $message */
258 12
        $message = null;
259
        
260 12
        if($this->state === static::STATE_INIT) {
261 12
            $message = new \Plasma\Drivers\MySQL\Messages\HandshakeMessage($this);
262
        } else {
263 10
            $firstChar = \Plasma\Drivers\MySQL\Messages\MessageUtility::readBuffer($buffer, 1);
264 10
            echo 'Received Message char "'.$firstChar.'" (0x'.dechex(ord($firstChar)).')'.PHP_EOL;
265
            
266 10
            if($firstChar === "\xFE" && $this->state < static::STATE_OK) {
267
                $message = new \Plasma\Drivers\MySQL\Messages\AuthSwitchRequestMessage($this);
268
            } elseif(
269 10
                isset($this->messageClasses[$firstChar]) &&
270 10
                ($firstChar !== \Plasma\Drivers\MySQL\Messages\EOFMessage::getID() || $length < 6) &&
271 10
                ($firstChar !== \Plasma\Drivers\MySQL\Messages\AuthMoreDataMessage::getID() || $this->state < static::STATE_OK)
272
            ) {
273 10
                $cl = $this->messageClasses[$firstChar];
274 10
                $message = new $cl($this);
275 1
            } elseif($this->state === static::STATE_OK && $this->currentCommand !== null) {
276 1
                $buffer = $firstChar.$buffer;
277
                
278 1
                $caller = new \Plasma\Drivers\MySQL\ProtocolOnNextCaller($this, $buffer);
279 1
                $this->currentCommand->onNext($caller);
280
            }
281
        }
282
        
283 12
        if(!($message instanceof \Plasma\Drivers\MySQL\Messages\MessageInterface)) {
284 1
            echo 'returned (no message)'.PHP_EOL;
285 1
            $this->buffer = $buffer;
286
            
287 1
            if(\strlen($this->buffer) > 0) {
288 1
                $this->processBuffer();
289
            }
290
            
291 1
            return;
292
        }
293
        
294 12
        echo 'Received Message '.get_class($message).PHP_EOL;
295
        
296 12
        $state = $message->setParserState();
297 12
        if($state !== -1) {
298 12
            $this->state = $state;
299
        }
300
        
301 12
        if($message instanceof \Plasma\Drivers\MySQL\Messages\HandshakeMessage) {
302 12
            $this->handshakeMessage = $message;
303
        }
304
        
305 12
        $this->handleMessage($buffer, $message);
306 12
    }
307
    
308
    /**
309
     * Handles an incoming message.
310
     * @param string                                           $buffer
311
     * @param \Plasma\Drivers\MySQL\Messages\MessageInterface  $message
312
     * @return void
313
     */
314 12
    function handleMessage(string &$buffer, \Plasma\Drivers\MySQL\Messages\MessageInterface $message) {
315
        try {
316 12
            $buffer = $message->parseMessage($buffer, $this);
0 ignored issues
show
The call to Plasma\Drivers\MySQL\Mes...terface::parseMessage() has too many arguments starting with $this. ( Ignorable by Annotation )

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

316
            /** @scrutinizer ignore-call */ 
317
            $buffer = $message->parseMessage($buffer, $this);

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. Please note the @ignore annotation hint above.

Loading history...
317 12
            if($buffer === false) {
318
                echo 'returned handle (unsufficent buffer length)'.PHP_EOL;
319
                return;
320
            }
321
            
322 12
            $this->buffer = $buffer;
0 ignored issues
show
Documentation Bug introduced by
It seems like $buffer can also be of type true. However, the property $buffer is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
323
            
324 12
            if($this->currentCommand !== null) {
325
                if(
326 10
                    ($message instanceof \Plasma\Drivers\MySQL\Messages\OkResponseMessage || $message instanceof \Plasma\Drivers\MySQL\Messages\EOFMessage)
327 10
                    && $this->currentCommand->hasFinished()
328
                ) {
329 9
                    $command = $this->currentCommand;
330 9
                    $this->currentCommand = null;
331
                    
332 9
                    $command->onComplete();
333 3
                } elseif($message instanceof \Plasma\Drivers\MySQL\Messages\ErrResponseMessage) {
334 1
                    $error = new \Plasma\Exception($message->errorMessage, $message->errorCode);
335
                    
336 1
                    $command = $this->currentCommand;
337 1
                    $this->currentCommand = null;
338
                    
339 1
                    $command->onError($error);
340
                } else {
341 2
                    $command = $this->currentCommand;
342 2
                    $command->onNext($message);
343
                    
344 2
                    if($command->hasFinished()) {
345 2
                        if($this->currentCommand === $command) {
346
                            $this->currentCommand = null;
347
                        }
348
                        
349 10
                        $command->onComplete();
350
                    }
351
                }
352 12
            } elseif($message instanceof \Plasma\Drivers\MySQL\Messages\ErrResponseMessage) {
353
                $error = new \Plasma\Exception($message->errorMessage, $message->errorCode);
354
                $this->emit('error', array($error));
355
            }
356
            
357 12
            $this->emit('message', array($message));
358
        } catch (\Plasma\Drivers\MySQL\Messages\ParseException $e) {
359
            $state = $e->getState();
360
            if($state !== null) {
361
                $this->state = $state;
362
            }
363
            
364
            $buffer = $e->getBuffer();
365
            if($buffer !== null) {
366
                $this->buffer = $buffer;
367
            }
368
            
369
            if($this->currentCommand !== null) {
370
                $this->currentCommand->onError($e);
371
            }
372
            
373
            $this->emit('error', array($e));
374
            $this->connection->close();
375
        }
376 12
    }
377
    
378
    /**
379
     * Registers the message classes.
380
     * @return void
381
     */
382 12
    protected function registerMessages() {
383
        $classes = array(
384 12
            (\Plasma\Drivers\MySQL\Messages\AuthMoreDataMessage::getID()) => \Plasma\Drivers\MySQL\Messages\AuthMoreDataMessage::class,
385 12
            (\Plasma\Drivers\MySQL\Messages\EOFMessage::getID()) => \Plasma\Drivers\MySQL\Messages\EOFMessage::class,
386 12
            (\Plasma\Drivers\MySQL\Messages\ErrResponseMessage::getID()) => \Plasma\Drivers\MySQL\Messages\ErrResponseMessage::class,
387 12
            (\Plasma\Drivers\MySQL\Messages\OkResponseMessage::getID()) => \Plasma\Drivers\MySQL\Messages\OkResponseMessage::class
388
        );
389
        
390 12
        foreach($classes as $id => $class) {
391 12
            $this->messageClasses[$id] = $class;
392
        }
393 12
    }
394
    
395
    /**
396
     * Adds the events to the connection.
397
     * @return void
398
     */
399 12
    protected function addEvents() {
400
        $this->connection->on('data', function ($chunk) {
401 12
            $this->buffer .= $chunk;
402 12
            $this->processBuffer();
403 12
        });
404
        
405
        $this->connection->on('close', function () {
406 5
            $this->handleClose();
407 12
        });
408 12
    }
409
    
410
    /**
411
     * Connection close handler.
412
     * @return void
413
     */
414 5
    protected function handleClose() {
415 5
        if($this->state === static::STATE_AUTH || $this->state === static::STATE_AUTH_SENT) {
416
            $this->state = static::STATE_AUTH_ERROR;
417
        }
418 5
    }
419
}
420