Completed
Push — master ( 930904...40261d )
by Vasily
12:44
created

Connection::onRead()   F

Complexity

Conditions 50
Paths 0

Size

Total Lines 211

Duplication

Lines 20
Ratio 9.48 %

Importance

Changes 0
Metric Value
cc 50
nc 0
nop 0
dl 20
loc 211
rs 3.3333
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace PHPDaemon\Clients\PostgreSQL;
4
5
use PHPDaemon\Core\Daemon;
6
use PHPDaemon\Core\Debug;
7
use PHPDaemon\Network\ClientConnection;
8
use PHPDaemon\Structures\StackCallbacks;
9
10
class Connection extends ClientConnection
11
{
12
13
    /**
14
     * @var string Protocol version
15
     */
16
    public $protover = '3.0';
17
18
    /**
19
     * @var integer Maximum packet size
20
     */
21
    public $maxPacketSize = 0x1000000;
22
23
    /**
24
     * @var integer Charset number
25
     */
26
    public $charsetNumber = 0x08;
27
28
    /**
29
     * @var string Database name
30
     */
31
    public $dbname = '';
32
33
    /**
34
     * @var string Username
35
     */
36
    protected $user = 'root';
37
38
    /**
39
     * @var string Password
40
     */
41
    protected $password = '';
42
43
    /**
44
     * @var string Default options
45
     */
46
    public $options = '';
47
48
    /**
49
     * @var integer Connection's state. 0 - start,  1 - got initial packet,  2 - auth. packet sent,  3 - auth. error,  4 - handshaked OK
50
     */
51
    public $state = 0;
52
53
    /**
54
     * @var string State of pointer of incoming data. 0 - Result Set Header Packet,  1 - Field Packet,  2 - Row Packet
55
     */
56
    public $instate = 0;
57
58
    /**
59
     * @var array Resulting rows
60
     */
61
    public $resultRows = [];
62
63
    /**
64
     * @var array Resulting fields
65
     */
66
    public $resultFields = [];
67
68
    /**
69
     * @var string Equals to INSERT_ID().
70
     */
71
    public $insertId;
72
73
    /**
74
     * @var integer Inserted rows number
75
     */
76
    public $insertNum;
77
78
    /**
79
     * @var integer Number of affected rows
80
     */
81
    public $affectedRows;
82
83
    /**
84
     * @var array Runtime parameters from server
85
     */
86
    public $parameters = [];
87
88
    /**
89
     * @var string Backend key
90
     */
91
    public $backendKey;
92
93
    /**
94
     * @var int
95
     */
96
    public $errno = 0;
97
98
    /**
99
     * @var string
100
     */
101
    public $errmsg;
102
103
    /**
104
     * @var
105
     */
106
    public $status;
107
108
    /**
109
     * State: authentication packet sent
110
     */
111
    const STATE_AUTH_PACKET_SENT = 2;
112
113
    /**
114
     * State: authencation error
115
     */
116
    const STATE_AUTH_ERROR = 3;
117
118
    /**
119
     * State: authentication passed
120
     */
121
    const STATE_AUTH_OK = 4;
122
123
    /**
124
     * Called when the stream is handshaked (at low-level), and peer is ready to recv. data
125
     * @return void
126
     */
127
    public function onReady()
128
    {
129
        $e = explode('.', $this->protover);
130
        $packet = pack('nn', $e[0], $e[1]);
131
132
        if (strlen($this->user)) {
133
            $packet .= "user\x00" . $this->user . "\x00";
134
        }
135
136
        if (strlen($this->dbname)) {
137
            $packet .= "database\x00" . $this->dbname . "\x00";
138
        }
139
        elseif (strlen($this->path)) {
140
            $packet .= "database\x00" . $this->path . "\x00";
141
        }
142
143
        if (strlen($this->options)) {
144
            $packet .= "options\x00" . $this->options . "\x00";
145
        }
146
147
        $packet .= "\x00";
148
        $this->sendPacket('', $packet);
149
    }
150
151
    /**
152
     * Executes the given callback when/if the connection is handshaked.
153
     * @param  callable $cb Callback
154
     * @callback $cb ( )
155
     * @return void
156
     */
157 View Code Duplication
    public function onConnected($cb)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
158
    {
159
        if ($this->state === self::STATE_AUTH_ERROR) {
160
            $cb($this, false);
161
        } elseif ($this->state === self::STATE_AUTH_OK) {
162
            $cb($this, true);
163
        } else {
164
            if (!$this->onConnected) {
165
                $this->onConnected = new StackCallbacks();
166
            }
167
            $this->onConnected->push($cb);
168
        }
169
    }
170
171
    /**
172
     * Converts binary string to integer
173
     * @param  string $str Binary string
174
     * @param  boolean $l Optional. Little endian. Default value - true.
175
     * @return integer      Resulting integer
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|double?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
176
     */
177 View Code Duplication
    public function bytes2int($str, $l = true)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
178
    {
179
        if ($l) {
180
            $str = strrev($str);
181
        }
182
183
        $dec = 0;
184
        $len = mb_orig_strlen($str);
185
186
        for ($i = 0; $i < $len; ++$i) {
187
            $dec += ord(mb_orig_substr($str, $i, 1)) * pow(0x100, $len - $i - 1);
188
        }
189
190
        return $dec;
191
    }
192
193
    /**
194
     * Converts integer to binary string
195
     * @param  integer $len Length
196
     * @param  integer $int Integer
197
     * @param  boolean $l Optional. Little endian. Default value - true.
198
     * @return string       Resulting binary string
199
     */
200 View Code Duplication
    public function int2bytes($len, $int = 0, $l = true)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
201
    {
202
        $hexstr = dechex($int);
203
204
        if ($len === null) {
205
            if (mb_orig_strlen($hexstr) % 2) {
206
                $hexstr = "0" . $hexstr;
207
            }
208
        } else {
209
            $hexstr = str_repeat('0', $len * 2 - mb_orig_strlen($hexstr)) . $hexstr;
210
        }
211
212
        $bytes = mb_orig_strlen($hexstr) / 2;
213
        $bin = '';
214
215
        for ($i = 0; $i < $bytes; ++$i) {
216
            $bin .= chr(hexdec(substr($hexstr, $i * 2, 2)));
217
        }
218
219
        return $l ? strrev($bin) : $bin;
220
    }
221
222
    /**
223
     * Send a packet
224
     * @param  string $type Data
225
     * @param  string $packet Packet
226
     * @return boolean         Success
227
     */
228
    public function sendPacket($type, $packet)
229
    {
230
        $header = $type . pack('N', mb_orig_strlen($packet) + 4);
231
232
        $this->write($header);
233
        $this->write($packet);
234
235
        if ($this->pool->config->protologging->value) {
236
            Daemon::log('Client --> Server: ' . Debug::exportBytes($header . $packet) . "\n\n");
237
        }
238
239
        return true;
240
    }
241
242
    /**
243
     * Builds length-encoded binary string
244
     * @param  string $s String
245
     * @return string    Resulting binary string
246
     */
247
    public function buildLenEncodedBinary($s)
248
    {
249
        if ($s === null) {
250
            return "\251";
251
        }
252
253
        $l = mb_orig_strlen($s);
254
255
        if ($l <= 250) {
256
            return chr($l) . $s;
257
        }
258
259
        if ($l <= 0xFFFF) {
260
            return "\252" . $this->int2bytes(2, $l) . $s;
261
        }
262
263
        if ($l <= 0xFFFFFF) {
264
            return "\254" . $this->int2bytes(3, $l) . $s;
265
        }
266
267
        return $this->int2bytes(8, $l) . $s;
268
    }
269
270
    /**
271
     * Parses length-encoded binary
272
     * @param  string &$s Reference to source string
273
     * @param  integer &$p
274
     * @return integer     Result
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|null|false|double?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
275
     */
276
    public function parseEncodedBinary(&$s, &$p)
277
    {
278
        $f = ord(mb_orig_substr($s, $p, 1));
279
        ++$p;
280
281
        if ($f <= 250) {
282
            return $f;
283
        }
284
285
        if ($s === 251) {
286
            return null;
287
        }
288
289
        if ($s === 255) {
290
            return false;
291
        }
292
293 View Code Duplication
        if ($f === 252) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
294
            $o = $p;
295
            $p += 2;
296
297
            return $this->bytes2int(mb_orig_substr($s, $o, 2));
298
        }
299
300 View Code Duplication
        if ($f === 253) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
301
            $o = $p;
302
            $p += 3;
303
304
            return $this->bytes2int(mb_orig_substr($s, $o, 3));
305
        }
306
307
        $o = $p;
308
        $p = +8;
309
310
        return $this->bytes2int(mb_orig_substr($s, $o, 8));
311
    }
312
313
    /**
314
     * Parse length-encoded string
315
     * @param  string &$s Reference to source string
316
     * @param  integer &$p Reference to pointer
317
     * @return integer     Result
0 ignored issues
show
Documentation introduced by
Should the return type not be null|false|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
318
     */
319
    public function parseEncodedString(&$s, &$p)
320
    {
321
        $l = $this->parseEncodedBinary($s, $p);
322
323
        if ($l === null || $l === false) {
324
            return $l;
325
        }
326
327
        $o = $p;
328
        $p += $l;
329
330
        return mb_orig_substr($s, $o, $l);
331
    }
332
333
    /**
334
     * Send SQL-query
335
     * @param  string $q Query
336
     * @param  callable $callback Optional. Callback called when response received.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $callback not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
337
     * @callback $callback ( )
338
     * @return boolean            Success
339
     */
340
    public function query($q, $callback = null)
341
    {
342
        return $this->command('Q', $q . "\x00", $callback);
343
    }
344
345
    /**
346
     * Send echo-request
347
     * @param  callable $callback Optional. Callback called when response received
0 ignored issues
show
Documentation introduced by
Should the type for parameter $callback not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
348
     * @callback $callback ( )
349
     * @return boolean Success
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
350
     */
351
    public function ping($callback = null)
0 ignored issues
show
Unused Code introduced by
The parameter $callback is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
352
    {
353
        // @todo There is no command for echo-request.
354
        //return $this->command(, '', $callback);
355
    }
356
357
    /**
358
     * Sends sync-request
359
     * @param  callable $cb Optional. Callback called when response received.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
360
     * @callback $cb ( )
361
     * @return boolean Success
362
     */
363
    public function sync($cb = null)
364
    {
365
        return $this->command('S', '', $cb);
366
    }
367
368
    /**
369
     * Send terminate-request to shutdown the connection
370
     * @param  callable $cb Optional. Callback called when response received.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
371
     * @callback $cb ( )
372
     * @return boolean Success
373
     */
374
    public function terminate($cb = null)
375
    {
376
        return $this->command('X', '', $cb);
377
    }
378
379
    /**
380
     * Sends arbitrary command
381
     * @param  integer $cmd Command's code. See constants above.
382
     * @param  string $q Data
383
     * @param  callable $cb Optional. Callback called when response received.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
384
     * @callback $cb ( )
385
     * @return boolean Success
386
     */
387 View Code Duplication
    public function command($cmd, $q = '', $cb = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
388
    {
389
        if ($this->state !== self::STATE_AUTH_OK) {
390
            return false;
391
        }
392
393
        $this->onResponse->push($cb);
0 ignored issues
show
Bug introduced by
It seems like $cb defined by parameter $cb on line 387 can also be of type null; however, PHPDaemon\Structures\StackCallbacks::push() does only seem to accept callable, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
394
        $this->sendPacket($cmd, $q);
395
396
        return true;
397
    }
398
399
    /**
400
     * Set default database name
401
     * @param  string $name Database name
402
     * @return boolean       Success
403
     */
404
    public function selectDB($name)
405
    {
406
        $this->dbname = $name;
407
408
        if ($this->state !== 1) {
409
            return $this->query('USE `' . $name . '`');
410
        }
411
412
        return true;
413
    }
414
415
    /**
416
     *
417
     */
418
    public function onRead()
419
    {
420
        start:
421
        $l = $this->getInputLength();
422
        if ($l < 5) {
423
            return; // Not enough data buffered yet
424
        }
425
        $type = $this->look(1);
426
427
        list(, $length) = unpack('N', $this->look(4, 1));
428
429
        $length -= 4;
430
431
        if ($l < $length + 5) {
432
            // Not enough data buffered yet
433
            return;
434
        }
435
436
        $this->drain(5);
437
438
        $packet = $this->read($length);
439
440
        if ($type === 'R') {
441
            // Authentication request
442
            list(, $authType) = unpack('N', $packet);
443
444
            if ($authType === 0) {
445
                // Successful
446
                if ($this->pool->config->protologging->value) {
447
                    Daemon::log(self::class . ': auth. ok.');
448
                }
449
450
                $this->state = self::STATE_AUTH_OK;
451
452
                foreach ($this->onConnected as $cb) {
453
                    $cb($this, true);
454
                }
455
            } // @todo move to constant values
456 View Code Duplication
            elseif ($authType === 2) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
457
                // KerberosV5
458
                Daemon::log(self::class . ': Unsupported authentication method: KerberosV5.');
459
                $this->state = self::STATE_AUTH_ERROR; // Auth. error
460
                $this->finish(); // Unsupported,  finish
461
            } elseif ($authType === 3) {
462
                // Cleartext
463
                $this->sendPacket('p', $this->password); // Password Message
464
                $this->state = self::STATE_AUTH_PACKET_SENT;
465
            } elseif ($authType === 4) {
466
                // Crypt
467
                $salt = mb_orig_substr($packet, 4, 2);
468
                $this->sendPacket('p', crypt($this->password, $salt)); // Password Message
469
                $this->state = self::STATE_AUTH_PACKET_SENT;
470
            } elseif ($authType === 5) {
471
                // MD5
472
                $salt = mb_orig_substr($packet, 4, 4);
473
                $this->sendPacket('p', 'md5' . md5(md5($this->password . $this->user) . $salt)); // Password Message
474
                $this->state = self::STATE_AUTH_PACKET_SENT;
475 View Code Duplication
            } elseif ($authType === 6) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
476
                // SCM
477
                Daemon::log(self::class . ': Unsupported authentication method: SCM.');
478
                $this->state = self::STATE_AUTH_ERROR; // Auth. error
479
                $this->finish(); // Unsupported,  finish
480
            } elseif ($authType === 9) {
481
                // GSS
482
                Daemon::log(self::class . ': Unsupported authentication method: GSS.');
483
                $this->state = self::STATE_AUTH_ERROR; // Auth. error
484
                $this->finish(); // Unsupported,  finish
485
            }
486
        } elseif ($type === 'T') {
487
            // Row Description
488
            list(, $numfields) = unpack('n', mb_orig_substr($packet, 0, 2));
489
            $p = 2;
490
491
            for ($i = 0; $i < $numfields; ++$i) {
492
                list($name) = $this->decodeNULstrings($packet, 1, $p);
0 ignored issues
show
Security Bug introduced by
It seems like $packet can also be of type false; however, PHPDaemon\Clients\Postgr...ion::decodeNULstrings() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
493
                $field = unpack('NtableOID/nattrNo/NdataType/ndataTypeSize/NtypeMod/nformat',
494
                    mb_orig_substr($packet, $p, 18));
495
                $p += 18;
496
                $field['name'] = $name;
497
                $this->resultFields[] = $field;
498
            }
499
        } elseif ($type === 'D') {
500
            // Data Row
501
            list(, $numfields) = unpack('n', mb_orig_substr($packet, 0, 2));
502
            $p = 2;
503
            $row = [];
504
505
            for ($i = 0; $i < $numfields; ++$i) {
506
                list(, $length) = unpack('N', mb_orig_substr($packet, $p, 4));
507
                $p += 4;
508
509
                if ($length === 0xffffffff) {
510
                    // hack
511
                    $length = -1;
512
                }
513
514
                if ($length === -1) {
515
                    $value = null;
516
                } else {
517
                    $value = mb_orig_substr($packet, $p, $length);
518
                    $p += $length;
519
                }
520
521
                $row[$this->resultFields[$i]['name']] = $value;
522
            }
523
524
            $this->resultRows[] = $row;
525
        } elseif ($type === 'G' || $type === 'H') {
526
            // Copy in response
527
            // The backend is ready to copy data from the frontend to a table; see Section 45.2.5.
528
            if ($this->pool->config->protologging->value) {
529
                Daemon::log(self::class . ': Caught CopyInResponse');
530
            }
531
        } elseif ($type === 'C') {
532
            // Close command
533
            $type = mb_orig_substr($packet, 0, 1);
534
535
            if ($type === 'S' || $type === 'P') {
536
                list($name) = $this->decodeNULstrings(mb_orig_substr($packet, 1));
537
            } else {
538
                $tag = $this->decodeNULstrings($packet);
0 ignored issues
show
Security Bug introduced by
It seems like $packet defined by $this->read($length) on line 438 can also be of type false; however, PHPDaemon\Clients\Postgr...ion::decodeNULstrings() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
539
                $tag = explode(' ', $tag[0]);
540
541
                if ($tag[0] === 'INSERT') {
542
                    $this->insertId = $tag[1];
543
                    $this->insertNum = $tag[2];
544
                } elseif ($tag[0] === 'DELETE' || $tag[0] === 'UPDATE' || $tag[0] === 'MOVE'
545
                    || $tag[0] === 'FETCH' || $tag[0] === 'COPY'
546
                ) {
547
                    $this->affectedRows = $tag[1];
548
                }
549
            }
550
551
            $this->onResultDone();
552
        } elseif ($type === 'n') {
553
            // No Data
554
            $this->onResultDone();
555
        } elseif ($type === 'E') {
556
            // Error Response
557
            $code = ord($packet);
558
            $message = '';
559
560
            foreach ($this->decodeNULstrings(mb_orig_substr($packet, 1), 0xFF) as $p) {
561
                if ($message !== '') {
562
                    $message .= ' ';
563
                    $p = mb_orig_substr($p, 1);
564
                }
565
566
                $message .= $p;
567
            }
568
569
            $this->errno = -1;
570
            $this->errmsg = $message;
571
572 View Code Duplication
            if ($this->state === self::STATE_AUTH_PACKET_SENT) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
573
                // Auth. error
574
                foreach ($this->onConnected as $cb) {
575
                    $cb($this, false);
576
                }
577
578
                $this->state = self::STATE_AUTH_ERROR;
579
            }
580
581
            $this->onError();
582
583
            if ($this->pool->config->protologging->value) {
584
                Daemon::log(self::class . ': Error response caught (0x' . dechex($code) . '): ' . $message);
585
            }
586
        } elseif ($type === 'I') {
587
            // Empty Query Response
588
            $this->errno = -1;
589
            $this->errmsg = 'Query was empty';
590
            $this->onError();
591
        } elseif ($type === 'S') {
592
            // Portal Suspended
593
            if ($this->pool->config->protologging->value) {
594
                Daemon::log(self::class . ': Caught PortalSuspended');
595
            }
596
        } elseif ($type === 'S') {
597
            // Parameter Status
598
            $u = $this->decodeNULstrings($packet, 2);
0 ignored issues
show
Security Bug introduced by
It seems like $packet defined by $this->read($length) on line 438 can also be of type false; however, PHPDaemon\Clients\Postgr...ion::decodeNULstrings() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
599
600
            if (isset($u[0])) {
601
                $this->parameters[$u[0]] = isset($u[1]) ? $u[1] : null;
602
603
                if ($this->pool->config->protologging->value) {
604
                    Daemon::log(self::class . ': Parameter ' . $u[0] . ' = \'' . $this->parameters[$u[0]] . '\'');
605
                }
606
            }
607
        } elseif ($type === 'K') {
608
            // Backend Key Data
609
            list(, $this->backendKey) = unpack('N', $packet);
610
            $this->backendKey = isset($u[1]) ? $u[1] : null;
611
612
            if ($this->pool->config->protologging->value) {
613
                Daemon::log(self::class . ': BackendKey is ' . $this->backendKey);
614
            }
615
        } elseif ($type === 'Z') {
616
            // Ready For Query
617
            $this->status = $packet;
618
619
            if ($this->pool->config->protologging->value) {
620
                Daemon::log(self::class . ': Ready For Query. Status: ' . $this->status);
621
            }
622
        } else {
623
            Daemon::log(self::class . ': Caught message with unsupported type - ' . $type);
624
        }
625
626
        goto start;
627
628
    }
629
630
    /**
631
     * Decode strings from the NUL-terminated representation
632
     * @param  string $data Binary data
633
     * @param  integer $limit Optional. Limit of count. Default is 1.
634
     * @param  reference &$p Optional. Pointer.
635
     * @return array            Decoded strings
636
     */
637
    public function decodeNULstrings($data, $limit = 1, &$p = 0)
638
    {
639
        $r = [];
640
641
        for ($i = 0; $i < $limit; ++$i) {
642
            $pos = mb_orig_strpos($data, "\x00", $p);
643
644
            if ($pos === false) {
645
                break;
646
            }
647
648
            $r[] = mb_orig_substr($data, $p, $pos - $p);
649
650
            $p = $pos + 1;
651
        }
652
653
        return $r;
654
    }
655
656
    /**
657
     * Called when the whole result received
658
     * @return void
659
     */
660
    public function onResultDone()
661
    {
662
        $this->instate = 0;
0 ignored issues
show
Documentation Bug introduced by
The property $instate was declared of type string, but 0 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
663
        $this->onResponse->executeOne($this, true);
664
        $this->resultRows = [];
665
        $this->resultFields = [];
666
667
        if ($this->pool->config->protologging->value) {
668
            Daemon::log(__METHOD__);
669
        }
670
    }
671
672
    /**
673
     * Called when error occured
674
     * @return void
675
     */
676
    public function onError()
677
    {
678
        $this->instate = 0;
0 ignored issues
show
Documentation Bug introduced by
The property $instate was declared of type string, but 0 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
679
        $this->onResponse->executeOne($this, false);
680
        $this->resultRows = [];
681
        $this->resultFields = [];
682
683
        if ($this->state === self::STATE_AUTH_PACKET_SENT) {
684
            // in case of auth error
685
            $this->state = self::STATE_AUTH_ERROR;
686
            $this->finish();
687
        }
688
689
        Daemon::log(__METHOD__ . ' #' . $this->errno . ': ' . $this->errmsg);
690
    }
691
}
692