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 ( 18c025...2702d5 )
by Charlotte
03:32
created

ProtocolParser::getCurrentCommand()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
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 1
    function __construct(\Plasma\Drivers\MySQL\Driver $driver, \React\Socket\ConnectionInterface $connection) {
127 1
        $this->driver = $driver;
128 1
        $this->connection = $connection;
129
        
130 1
        $this->registerMessages();
131 1
        $this->addEvents();
132 1
    }
133
    
134
    /**
135
     * Get the currently executing command.
136
     * @return \Plasma\CommandInterface|null
137
     */
138
    function getCurrentCommand(): ?\Plasma\CommandInterface {
139
        return $this->currentCommand;
140
    }
141
    
142
    /**
143
     * Invoke a command to execute.
144
     * @param \Plasma\CommandInterface|null  $command
145
     * @return void
146
     */
147
    function invokeCommand(?\Plasma\CommandInterface $command): void {
148
        if($command === null) {
149
            return;
150
        }
151
        
152
        $this->currentCommand = $command;
153
        $this->processCommand();
154
    }
155
    
156
    /**
157
     * Executes a command, without handling any aftermath.
158
     * The `onComplete` callback will be immediately invoked, regardless of the `waitForCompletion` value.
159
     * @param \Plasma\CommandInterface  $command
160
     * @return void
161
     */
162
    function executeCommand(\Plasma\CommandInterface $command): void {
163
        $this->processCommand($command);
164
    }
165
    
166
    /**
167
     * Marks the command itself as finished, if currently running.
168
     * @param \Plasma\Drivers\MySQL\Commands\CommandInterface  $command
169
     * @return void
170
     */
171
    function markCommandAsFinished(\Plasma\CommandInterface $command): void {
172
        if($command === $this->currentCommand) {
173
            $this->currentCommand = null;
174
        }
175
        
176
        $command->onComplete();
177
    }
178
    
179
    /**
180
     * Get the parser state.
181
     * @return int
182
     */
183
    function getState(): int {
184
        return $this->state;
185
    }
186
    
187
    /**
188
     * Get the handshake message, or null.
189
     * @return \Plasma\Drivers\MySQL\Messages\HandshakeMessage|null
190
     */
191
    function getHandshakeMessage(): ?\Plasma\Drivers\MySQL\Messages\HandshakeMessage {
192
        return $this->handshakeMessage;
193
    }
194
    
195
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $packet should have a doc-comment as per coding-style.
Loading history...
196
     * Sends a packet to the server.
197
     * @return void
198
     */
199
    function sendPacket(string $packet): void {
200
        $length = \Plasma\Drivers\MySQL\Messages\MessageUtility::writeInt3(\strlen($packet));
201
        $sequence = \Plasma\Drivers\MySQL\Messages\MessageUtility::writeInt1((++$this->sequenceID));
202
        
203
        $this->connection->write($length.$sequence.$packet);
204
    }
205
    
206
    /**
207
     * Unshifts a command.
208
     * @return void
209
     */
210
    protected function unshiftCommand() {
211
        $this->invokeCommand($this->driver->getNextCommand());
212
    }
213
    
214
    /**
215
     * Processes a command.
216
     * @param \Plasma\CommandInterface|null  $command
217
     * @return void
218
     */
219
    protected function processCommand(?\Plasma\CommandInterface $command = null) {
220
        if($command === null && $this->currentCommand instanceof \Plasma\Drivers\MySQL\Commands\CommandInterface) {
221
            $command = $this->currentCommand;
222
            
223
            $state = $command->setParserState();
224
            if($state !== -1) {
225
                $this->state = $state;
226
            }
227
        }
228
        
229
        if($command === null) {
230
            return;
231
        }
232
        
233
        $this->sendPacket($command->getEncodedMessage());
234
        
235
        if($command !== $this->currentCommand || !$command->waitForCompletion()) {
236
            $command->onComplete();
237
            
238
            if($command === $this->currentCommand) {
239
                $this->currentCommand = null;
240
            }
241
        }
242
    }
243
    
244
    /**
245
     * Processes the buffer.
246
     * @return void
247
     */
248 1
    protected function processBuffer() {
249 1
        $original = $this->buffer;
250
        
251 1
        $length = \Plasma\Drivers\MySQL\Messages\MessageUtility::readInt3($this->buffer);
252 1
        $sequence = \Plasma\Drivers\MySQL\Messages\MessageUtility::readInt1($this->buffer);
253
        
254 1
        if(\strlen($this->buffer) < $length) {
255 1
            $this->buffer = $original;
256 1
            return;
257
        }
258
        
259
        $original = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $original is dead and can be removed.
Loading history...
260
        if($this->state === static::STATE_OK) {
261
            $this->sequenceID = $sequence;
262
        }
263
        
264
        $firstChar = \Plasma\Drivers\MySQL\Messages\MessageUtility::readBuffer($this->buffer, 1);
265
        
266
        /** @var \Plasma\Drivers\MySQL\Messages\MessageInterface  $message */
267
        $message = null;
268
        
269
        if($this->state === static::STATE_INIT) {
270
            $message = new \Plasma\Drivers\MySQL\Messages\HandshakeMessage($this);
271
        } elseif($firstChar === "\xFE" && $this->state < static::STATE_OK) {
272
            $message = new \Plasma\Drivers\MySQL\Messages\AuthSwitchRequestMessage($this);
273
        } elseif(isset($this->messageClasses[$firstChar]) && ($firstChar !== \Plasma\Drivers\MySQL\Messages\EOFMessage::getID() || $length < 6)) {
274
            $cl = $this->messageClasses[$firstChar];
275
            $message = new $cl($this);
276
        } elseif($this->state === static::STATE_OK && $this->currentCommand !== null) {
277
            $this->buffer = $firstChar.$this->buffer;
278
            
279
            $caller = new \Plasma\Drivers\MySQL\ProtocolOnNextCaller($this, $this->buffer);
280
            $message = $this->currentCommand->onNext($caller);
281
        }
282
        
283
        if(!($message instanceof \Plasma\Drivers\MySQL\Messages\MessageInterface)) {
284
            return;
285
        }
286
        
287
        $state = $message->setParserState();
288
        if($state !== -1) {
289
            $this->state = $state;
290
        }
291
        
292
        if($message instanceof \Plasma\Drivers\MySQL\Messages\HandshakeMessage) {
293
            $this->handshakeMessage = $message;
294
        }
295
        
296
        $this->handleMessage($message);
297
    }
298
    
299
    /**
300
     * Handles an incoming message.
301
     * @param \Plasma\Drivers\MySQL\Messages\MessageInterface  $message
302
     * @return void
303
     */
304
    function handleMessage(\Plasma\Drivers\MySQL\Messages\MessageInterface $message) {
305
        try {
306
            $this->buffer = $message->parseMessage($this->buffer, $this);
0 ignored issues
show
Unused Code introduced by
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

306
            /** @scrutinizer ignore-call */ 
307
            $this->buffer = $message->parseMessage($this->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...
Documentation Bug introduced by
It seems like $message->parseMessage($this->buffer, $this) can also be of type boolean. 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...
307
            
308
            if($this->currentCommand !== null) {
309
                if(
1 ignored issue
show
Coding Style introduced by
First condition of a multi-line IF statement must directly follow the opening parenthesis
Loading history...
310
                    ($message instanceof \Plasma\Drivers\MySQL\Messages\OkResponseMessage || $message instanceof \Plasma\Drivers\MySQL\Messages\EOFMessage)
1 ignored issue
show
Coding Style introduced by
Each line in a multi-line IF statement must begin with a boolean operator
Loading history...
311
                    && $this->currentCommand->isFinished()
0 ignored issues
show
Bug introduced by
The method isFinished() does not exist on Plasma\CommandInterface. ( Ignorable by Annotation )

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

311
                    && $this->currentCommand->/** @scrutinizer ignore-call */ isFinished()

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
312
                ) {
313
                    $this->currentCommand->onComplete();
314
                    $this->currentCommand = null;
315
                } elseif($message instanceof \Plasma\Drivers\MySQL\Messages\ErrResponseMessage) {
316
                    $error = new \Plasma\Exception($message->errorMessage, $message->errorCode);
317
                    
318
                    if(!$this->currentCommand->isFinished()) {
319
                        $this->currentCommand->onError($error);
320
                    } else {
321
                        $this->emit('error', array($error));
322
                    }
323
                } else {
324
                    $this->currentCommand->onNext($message);
325
                    
326
                    if($this->currentCommand->isFinished()) {
327
                        $this->currentCommand->onComplete();
328
                        $this->currentCommand = null;
329
                    }
330
                }
331
            } else {
332
                $this->unshiftCommand();
333
            }
334
            
335
            $this->emit('message', array($message));
336
        } catch (\Plasma\Drivers\MySQL\Messages\ParseException $e) {
337
            $state = $e->getState();
338
            if($state !== null) {
1 ignored issue
show
introduced by
The condition $state !== null is always true.
Loading history...
339
                $this->state = $state;
340
            }
341
            
342
            $buffer = $e->getBuffer();
343
            if($buffer !== null) {
1 ignored issue
show
introduced by
The condition $buffer !== null is always true.
Loading history...
344
                $this->buffer = $buffer;
345
            }
346
            
347
            if($this->currentCommand !== null) {
348
                $this->currentCommand->onError($e);
349
            }
350
            
351
            $this->emit('error', array($e));
352
            $this->connection->close();
353
        }
354
    }
355
    
356
    /**
357
     * Registers the message classes.
358
     * @return void
359
     */
360 1
    protected function registerMessages() {
361
        $classes = array(
362 1
            (\Plasma\Drivers\MySQL\Messages\AuthMoreDataMessage::getID()) => \Plasma\Drivers\MySQL\Messages\AuthMoreDataMessage::class,
363 1
            (\Plasma\Drivers\MySQL\Messages\EOFMessage::getID()) => \Plasma\Drivers\MySQL\Messages\EOFMessage::class,
364 1
            (\Plasma\Drivers\MySQL\Messages\ErrResponseMessage::getID()) => \Plasma\Drivers\MySQL\Messages\ErrResponseMessage::class,
365 1
            (\Plasma\Drivers\MySQL\Messages\OkResponseMessage::getID()) => \Plasma\Drivers\MySQL\Messages\OkResponseMessage::class
366
        );
367
        
368 1
        foreach($classes as $id => $class) {
369 1
            $this->messageClasses[$id] = $class;
370
        }
371 1
    }
372
    
373
    /**
374
     * Adds the events to the connection.
375
     * @return void
376
     */
377 1
    protected function addEvents() {
378
        $this->connection->on('data', function ($chunk) {
379 1
            $this->buffer .= $chunk;
380 1
            $this->processBuffer();
381 1
        });
382
        
383
        $this->connection->on('close', function () {
384 1
            $this->handleClose();
385 1
        });
386 1
    }
387
    
388
    /**
389
     * Connection close handler.
390
     * @return void
391
     */
392 1
    protected function handleClose() {
393 1
        if($this->state === static::STATE_AUTH || $this->state === static::STATE_AUTH_SENT) {
394
            $this->state = static::STATE_AUTH_ERROR;
395
        }
396 1
    }
397
}
398