Completed
Push — EventLoopContainer ( c9a84d...6e77fa )
by Vasily
04:04
created

Connection::stdin()   F

Complexity

Conditions 51
Paths 0

Size

Total Lines 218
Code Lines 133

Duplication

Lines 20
Ratio 9.17 %

Importance

Changes 6
Bugs 2 Features 0
Metric Value
cc 51
eloc 133
c 6
b 2
f 0
nc 0
nop 1
dl 20
loc 218
rs 2

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
namespace PHPDaemon\Clients\PostgreSQL;
3
4
use PHPDaemon\Core\Daemon;
5
use PHPDaemon\Core\Debug;
6
use PHPDaemon\Network\ClientConnection;
7
use PHPDaemon\Structures\StackCallbacks;
8
9
class Connection extends ClientConnection
10
{
11
12
    /**
13
     * @var string Protocol version
14
     */
15
    public $protover = '3.0';
16
17
    /**
18
     * @var integer Maximum packet size
19
     */
20
    public $maxPacketSize = 0x1000000;
21
22
    /**
23
     * @var integer Charset number
24
     */
25
    public $charsetNumber = 0x08;
26
27
    /**
28
     * @var string Database name
29
     */
30
    public $dbname = '';
31
32
    /**
33
     * @var string Username
34
     */
35
    protected $user = 'root';
36
37
    /**
38
     * @var string Password
39
     */
40
    protected $password = '';
41
42
    /**
43
     * @var string Default options
44
     */
45
    public $options = '';
46
47
    /**
48
     * @var integer Connection's state. 0 - start,  1 - got initial packet,  2 - auth. packet sent,  3 - auth. error,  4 - handshaked OK
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 136 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
49
     */
50
    public $state = 0;
51
52
    /**
53
     * @var string State of pointer of incoming data. 0 - Result Set Header Packet,  1 - Field Packet,  2 - Row Packet
54
     */
55
    public $instate = 0;
56
57
    /**
58
     * @var array Resulting rows
59
     */
60
    public $resultRows = [];
61
62
    /**
63
     * @var array Resulting fields
64
     */
65
    public $resultFields = [];
66
67
    /**
68
     * @var string Equals to INSERT_ID().
69
     */
70
    public $insertId;
71
72
    /**
73
     * @var integer Inserted rows number
74
     */
75
    public $insertNum;
76
77
    /**
78
     * @var integer Number of affected rows
79
     */
80
    public $affectedRows;
81
82
    /**
83
     * @var array Runtime parameters from server
84
     */
85
    public $parameters = [];
86
87
    /**
88
     * @var string Backend key
89
     */
90
    public $backendKey;
91
92
    /**
93
     * State: authentication packet sent
94
     */
95
    const STATE_AUTH_PACKET_SENT = 2;
96
97
    /**
98
     * State: authencation error
99
     */
100
    const STATE_AUTH_ERROR = 3;
101
102
    /**
103
     * State: authentication passed
104
     */
105
    const STATE_AUTH_OK = 4;
106
107
    /**
108
     * Called when the connection is ready to accept new data
109
     * @return void
110
     */
111
    public function onReady()
112
    {
113
        $e = explode('.', $this->protover);
114
        $packet = pack('nn', $e[0], $e[1]);
115
116
        if (mb_orig_strlen($this->user)) {
117
            $packet .= "user\x00" . $this->user . "\x00";
118
        }
119
120
        if (mb_orig_strlen($this->dbname)) {
121
            $packet .= "database\x00" . $this->dbname . "\x00";
122
        }
123
124
        if (mb_orig_strlen($this->options)) {
125
            $packet .= "options\x00" . $this->options . "\x00";
126
        }
127
128
        $packet .= "\x00";
129
        $this->sendPacket('', $packet);
130
    }
131
132
    /**
133
     * Executes the given callback when/if the connection is handshaked.
134
     * @param  callable $cb Callback
135
     * @callback $cb ( )
136
     * @return void
137
     */
138 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...
139
    {
140
        if ($this->state === self::STATE_AUTH_ERROR) {
141
            $cb($this, false);
142
        } elseif ($this->state === self::STATE_AUTH_OK) {
143
            $cb($this, true);
144
        } else {
145
            if (!$this->onConnected) {
146
                $this->onConnected = new StackCallbacks();
147
            }
148
            $this->onConnected->push($cb);
149
        }
150
    }
151
152
    /**
153
     * Converts binary string to integer
154
     * @param  string $str Binary string
155
     * @param  boolean $l Optional. Little endian. Default value - true.
156
     * @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...
157
     */
158 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...
159
    {
160
        if ($l) {
161
            $str = strrev($str);
162
        }
163
164
        $dec = 0;
165
        $len = mb_orig_strlen($str);
166
167
        for ($i = 0; $i < $len; ++$i) {
168
            $dec += ord(mb_orig_substr($str, $i, 1)) * pow(0x100, $len - $i - 1);
169
        }
170
171
        return $dec;
172
    }
173
174
    /**
175
     * Converts integer to binary string
176
     * @param  integer $len Length
177
     * @param  integer $int Integer
178
     * @param  boolean $l Optional. Little endian. Default value - true.
179
     * @return string       Resulting binary string
180
     */
181 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...
182
    {
183
        $hexstr = dechex($int);
184
185
        if ($len === null) {
186
            if (mb_orig_strlen($hexstr) % 2) {
187
                $hexstr = "0" . $hexstr;
188
            }
189
        } else {
190
            $hexstr = str_repeat('0', $len * 2 - mb_orig_strlen($hexstr)) . $hexstr;
191
        }
192
193
        $bytes = mb_orig_strlen($hexstr) / 2;
194
        $bin = '';
195
196
        for ($i = 0; $i < $bytes; ++$i) {
197
            $bin .= chr(hexdec(substr($hexstr, $i * 2, 2)));
198
        }
199
200
        return $l ? strrev($bin) : $bin;
201
    }
202
203
    /**
204
     * Send a packet
205
     * @param  string $type Data
206
     * @param  string $packet Packet
207
     * @return boolean         Success
208
     */
209
    public function sendPacket($type, $packet)
210
    {
211
        $header = $type . pack('N', mb_orig_strlen($packet) + 4);
212
213
        $this->write($header);
214
        $this->write($packet);
215
216
        if ($this->pool->config->protologging->value) {
217
            Daemon::log('Client --> Server: ' . Debug::exportBytes($header . $packet) . "\n\n");
218
        }
219
220
        return true;
221
    }
222
223
    /**
224
     * Builds length-encoded binary string
225
     * @param  string $s String
226
     * @return string    Resulting binary string
227
     */
228
    public function buildLenEncodedBinary($s)
229
    {
230
        if ($s === null) {
231
            return "\251";
232
        }
233
234
        $l = mb_orig_strlen($s);
235
236
        if ($l <= 250) {
237
            return chr($l) . $s;
238
        }
239
240
        if ($l <= 0xFFFF) {
241
            return "\252" . $this->int2bytes(2, $l) . $s;
242
        }
243
244
        if ($l <= 0xFFFFFF) {
245
            return "\254" . $this->int2bytes(3, $l) . $s;
246
        }
247
248
        return $this->int2bytes(8, $l) . $s;
249
    }
250
251
    /**
252
     * Parses length-encoded binary
253
     * @param  string &$s Reference to source string
254
     * @param  integer &$p
255
     * @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...
256
     */
257
    public function parseEncodedBinary(&$s, &$p)
258
    {
259
        $f = ord(mb_orig_substr($s, $p, 1));
260
        ++$p;
261
262
        if ($f <= 250) {
263
            return $f;
264
        }
265
266
        if ($s === 251) {
267
            return null;
268
        }
269
270
        if ($s === 255) {
271
            return false;
272
        }
273
274 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...
275
            $o = $p;
276
            $p += 2;
277
278
            return $this->bytes2int(mb_orig_substr($s, $o, 2));
279
        }
280
281 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...
282
            $o = $p;
283
            $p += 3;
284
285
            return $this->bytes2int(mb_orig_substr($s, $o, 3));
286
        }
287
288
        $o = $p;
289
        $p = +8;
290
291
        return $this->bytes2int(mb_orig_substr($s, $o, 8));
292
    }
293
294
    /**
295
     * Parse length-encoded string
296
     * @param  string &$s Reference to source string
297
     * @param  integer &$p Reference to pointer
298
     * @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...
299
     */
300
    public function parseEncodedString(&$s, &$p)
301
    {
302
        $l = $this->parseEncodedBinary($s, $p);
303
304
        if ($l === null || $l === false) {
305
            return $l;
306
        }
307
308
        $o = $p;
309
        $p += $l;
310
311
        return mb_orig_substr($s, $o, $l);
312
    }
313
314
    /**
315
     * Send SQL-query
316
     * @param  string $q Query
317
     * @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...
318
     * @callback $callback ( )
319
     * @return boolean            Success
320
     */
321
    public function query($q, $callback = null)
322
    {
323
        return $this->command('Q', $q . "\x00", $callback);
324
    }
325
326
    /**
327
     * Send echo-request
328
     * @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...
329
     * @callback $callback ( )
330
     * @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...
331
     */
332
    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...
333
    {
334
        // @todo There is no command for echo-request.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
335
        //return $this->command(, '', $callback);
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
336
    }
337
338
    /**
339
     * Sends sync-request
340
     * @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...
341
     * @callback $cb ( )
342
     * @return boolean Success
343
     */
344
    public function sync($cb = null)
345
    {
346
        return $this->command('S', '', $cb);
347
    }
348
349
    /**
350
     * Send terminate-request to shutdown the connection
351
     * @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...
352
     * @callback $cb ( )
353
     * @return boolean Success
354
     */
355
    public function terminate($cb = null)
356
    {
357
        return $this->command('X', '', $cb);
358
    }
359
360
    /**
361
     * Sends arbitrary command
362
     * @param  integer $cmd Command's code. See constants above.
363
     * @param  string $q Data
364
     * @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...
365
     * @callback $cb ( )
366
     * @return boolean Success
367
     */
368 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...
369
    {
370
        if ($this->state !== self::STATE_AUTH_OK) {
371
            return false;
372
        }
373
374
        $this->onResponse->push($cb);
0 ignored issues
show
Bug introduced by
It seems like $cb defined by parameter $cb on line 368 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...
375
        $this->sendPacket($cmd, $q);
376
377
        return true;
378
    }
379
380
    /**
381
     * Set default database name
382
     * @param  string $name Database name
383
     * @return boolean       Success
384
     */
385
    public function selectDB($name)
386
    {
387
        $this->dbname = $name;
388
389
        if ($this->state !== 1) {
390
            return $this->query('USE `' . $name . '`');
391
        }
392
393
        return true;
394
    }
395
396
    /**
397
     * Called when new data received
398
     * @param  string $buf New data
399
     * @return void
400
     */
401
    public function stdin($buf)
402
    {
403
        $this->buf .= $buf;
0 ignored issues
show
Documentation introduced by
The property buf does not exist on object<PHPDaemon\Clients\PostgreSQL\Connection>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
404
405
        if ($this->pool->config->protologging->value) {
406
            Daemon::log('Server --> Client: ' . Debug::exportBytes($buf) . "\n\n");
407
        }
408
409
        start:
410
411
        $this->buflen = mb_orig_strlen($this->buf);
0 ignored issues
show
Documentation introduced by
The property buflen does not exist on object<PHPDaemon\Clients\PostgreSQL\Connection>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property buf does not exist on object<PHPDaemon\Clients\PostgreSQL\Connection>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
412
413
        if ($this->buflen < 5) {
0 ignored issues
show
Documentation introduced by
The property buflen does not exist on object<PHPDaemon\Clients\PostgreSQL\Connection>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
414
            // Not enough data buffered yet
415
            return;
416
        }
417
418
        $type = mb_orig_substr($this->buf, 0, 1);
0 ignored issues
show
Documentation introduced by
The property buf does not exist on object<PHPDaemon\Clients\PostgreSQL\Connection>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
419
420
        list(, $length) = unpack('N', mb_orig_substr($this->buf, 1, 4));
0 ignored issues
show
Documentation introduced by
The property buf does not exist on object<PHPDaemon\Clients\PostgreSQL\Connection>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
421
        $length -= 4;
422
423
        if ($this->buflen < 5 + $length) {
0 ignored issues
show
Documentation introduced by
The property buflen does not exist on object<PHPDaemon\Clients\PostgreSQL\Connection>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
424
            // Not enough data buffered yet
425
            return;
426
        }
427
428
        $packet = mb_orig_substr($this->buf, 5, $length);
0 ignored issues
show
Documentation introduced by
The property buf does not exist on object<PHPDaemon\Clients\PostgreSQL\Connection>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
429
        $this->buf = mb_orig_substr($this->buf, 5 + $length);
0 ignored issues
show
Documentation introduced by
The property buf does not exist on object<PHPDaemon\Clients\PostgreSQL\Connection>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property buf does not exist on object<PHPDaemon\Clients\PostgreSQL\Connection>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
430
431
        if ($type === 'R') {
432
            // Authentication request
433
            list(, $authType) = unpack('N', $packet);
434
435
            if ($authType === 0) {
436
                // Successful
437
                if ($this->pool->config->protologging->value) {
438
                    Daemon::log(__CLASS__ . ': auth. ok.');
439
                }
440
441
                $this->state = self::STATE_AUTH_OK;
442
443
                foreach ($this->onConnected as $cb) {
444
                    $cb($this, true);
445
                }
446
            } // @todo move to constant values
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
447 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...
448
                // KerberosV5
449
                Daemon::log(__CLASS__ . ': Unsupported authentication method: KerberosV5.');
450
                $this->state = self::STATE_AUTH_ERROR; // Auth. error
451
                $this->finish(); // Unsupported,  finish
452
            } elseif ($authType === 3) {
453
                // Cleartext
454
                $this->sendPacket('p', $this->password); // Password Message
455
                $this->state = self::STATE_AUTH_PACKET_SENT;
456
            } elseif ($authType === 4) {
457
                // Crypt
458
                $salt = mb_orig_substr($packet, 4, 2);
459
                $this->sendPacket('p', crypt($this->password, $salt)); // Password Message
460
                $this->state = self::STATE_AUTH_PACKET_SENT;
461
            } elseif ($authType === 5) {
462
                // MD5
463
                $salt = mb_orig_substr($packet, 4, 4);
464
                $this->sendPacket('p', 'md5' . md5(md5($this->password . $this->user) . $salt)); // Password Message
465
                $this->state = self::STATE_AUTH_PACKET_SENT;
466 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...
467
                // SCM
468
                Daemon::log(__CLASS__ . ': Unsupported authentication method: SCM.');
469
                $this->state = self::STATE_AUTH_ERROR; // Auth. error
470
                $this->finish(); // Unsupported,  finish
471
            } elseif ($authType === 9) {
472
                // GSS
473
                Daemon::log(__CLASS__ . ': Unsupported authentication method: GSS.');
474
                $this->state = self::STATE_AUTH_ERROR; // Auth. error
475
                $this->finish(); // Unsupported,  finish
476
            }
477
        } elseif ($type === 'T') {
478
            // Row Description
479
            list(, $numfields) = unpack('n', mb_orig_substr($packet, 0, 2));
480
            $p = 2;
481
482
            for ($i = 0; $i < $numfields; ++$i) {
483
                list($name) = $this->decodeNULstrings($packet, 1, $p);
484
                $field = unpack('NtableOID/nattrNo/NdataType/ndataTypeSize/NtypeMod/nformat',
485
                    mb_orig_substr($packet, $p, 18));
486
                $p += 18;
487
                $field['name'] = $name;
488
                $this->resultFields[] = $field;
489
            }
490
        } elseif ($type === 'D') {
491
            // Data Row
492
            list(, $numfields) = unpack('n', mb_orig_substr($packet, 0, 2));
493
            $p = 2;
494
            $row = [];
495
496
            for ($i = 0; $i < $numfields; ++$i) {
497
                list(, $length) = unpack('N', mb_orig_substr($packet, $p, 4));
498
                $p += 4;
499
500
                if ($length === 0xffffffff) {
501
                    // hack
502
                    $length = -1;
503
                }
504
505
                if ($length === -1) {
506
                    $value = null;
507
                } else {
508
                    $value = mb_orig_substr($packet, $p, $length);
509
                    $p += $length;
510
                }
511
512
                $row[$this->resultFields[$i]['name']] = $value;
513
            }
514
515
            $this->resultRows[] = $row;
516
        } elseif ($type === 'G' || $type === 'H') {
517
            // Copy in response
518
            // The backend is ready to copy data from the frontend to a table; see Section 45.2.5.
519
            if ($this->pool->config->protologging->value) {
520
                Daemon::log(__CLASS__ . ': Caught CopyInResponse');
521
            }
522
        } elseif ($type === 'C') {
523
            // Close command
524
            $type = mb_orig_substr($packet, 0, 1);
525
526
            if ($type === 'S' || $type === 'P') {
527
                list($name) = $this->decodeNULstrings(mb_orig_substr($packet, 1));
528
            } else {
529
                $tag = $this->decodeNULstrings($packet);
530
                $tag = explode(' ', $tag[0]);
531
532
                if ($tag[0] === 'INSERT') {
533
                    $this->insertId = $tag[1];
534
                    $this->insertNum = $tag[2];
535
                } elseif ($tag[0] === 'DELETE' || $tag[0] === 'UPDATE' || $tag[0] === 'MOVE'
536
                    || $tag[0] === 'FETCH' || $tag[0] === 'COPY'
537
                ) {
538
                    $this->affectedRows = $tag[1];
539
                }
540
            }
541
542
            $this->onResultDone();
543
        } elseif ($type === 'n') {
544
            // No Data
545
            $this->onResultDone();
546
        } elseif ($type === 'E') {
547
            // Error Response
548
            $code = ord($packet);
549
            $message = '';
550
551
            foreach ($this->decodeNULstrings(mb_orig_substr($packet, 1), 0xFF) as $p) {
552
                if ($message !== '') {
553
                    $message .= ' ';
554
                    $p = mb_orig_substr($p, 1);
555
                }
556
557
                $message .= $p;
558
            }
559
560
            $this->errno = -1;
0 ignored issues
show
Documentation introduced by
The property errno does not exist on object<PHPDaemon\Clients\PostgreSQL\Connection>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
561
            $this->errmsg = $message;
0 ignored issues
show
Documentation introduced by
The property errmsg does not exist on object<PHPDaemon\Clients\PostgreSQL\Connection>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
562
563 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...
564
                // Auth. error
565
                foreach ($this->onConnected as $cb) {
566
                    $cb($this, false);
567
                }
568
569
                $this->state = self::STATE_AUTH_ERROR;
570
            }
571
572
            $this->onError();
573
574
            if ($this->pool->config->protologging->value) {
575
                Daemon::log(__CLASS__ . ': Error response caught (0x' . dechex($code) . '): ' . $message);
576
            }
577
        } elseif ($type === 'I') {
578
            // Empty Query Response
579
            $this->errno = -1;
0 ignored issues
show
Documentation introduced by
The property errno does not exist on object<PHPDaemon\Clients\PostgreSQL\Connection>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
580
            $this->errmsg = 'Query was empty';
0 ignored issues
show
Documentation introduced by
The property errmsg does not exist on object<PHPDaemon\Clients\PostgreSQL\Connection>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
581
            $this->onError();
582
        } elseif ($type === 'S') {
583
            // Portal Suspended
584
            if ($this->pool->config->protologging->value) {
585
                Daemon::log(__CLASS__ . ': Caught PortalSuspended');
586
            }
587
        } elseif ($type === 'S') {
588
            // Parameter Status
589
            $u = $this->decodeNULstrings($packet, 2);
590
591
            if (isset($u[0])) {
592
                $this->parameters[$u[0]] = isset($u[1]) ? $u[1] : null;
593
594
                if ($this->pool->config->protologging->value) {
595
                    Daemon::log(__CLASS__ . ': Parameter ' . $u[0] . ' = \'' . $this->parameters[$u[0]] . '\'');
596
                }
597
            }
598
        } elseif ($type === 'K') {
599
            // Backend Key Data
600
            list(, $this->backendKey) = unpack('N', $packet);
601
            $this->backendKey = isset($u[1]) ? $u[1] : null;
602
603
            if ($this->pool->config->protologging->value) {
604
                Daemon::log(__CLASS__ . ': BackendKey is ' . $this->backendKey);
605
            }
606
        } elseif ($type === 'Z') {
607
            // Ready For Query
608
            $this->status = $packet;
0 ignored issues
show
Documentation introduced by
The property status does not exist on object<PHPDaemon\Clients\PostgreSQL\Connection>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
609
610
            if ($this->pool->config->protologging->value) {
611
                Daemon::log(__CLASS__ . ': Ready For Query. Status: ' . $this->status);
0 ignored issues
show
Documentation introduced by
The property status does not exist on object<PHPDaemon\Clients\PostgreSQL\Connection>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
612
            }
613
        } else {
614
            Daemon::log(__CLASS__ . ': Caught message with unsupported type - ' . $type);
615
        }
616
617
        goto start;
618
    }
619
620
    /**
621
     * Decode strings from the NUL-terminated representation
622
     * @param  string $data Binary data
623
     * @param  integer $limit Optional. Limit of count. Default is 1.
624
     * @param  reference &$p Optional. Pointer.
625
     * @return array            Decoded strings
626
     */
627
    public function decodeNULstrings($data, $limit = 1, &$p = 0)
628
    {
629
        $r = [];
630
631
        for ($i = 0; $i < $limit; ++$i) {
632
            $pos = mb_orig_strpos($data, "\x00", $p);
633
634
            if ($pos === false) {
635
                break;
636
            }
637
638
            $r[] = mb_orig_substr($data, $p, $pos - $p);
639
640
            $p = $pos + 1;
641
        }
642
643
        return $r;
644
    }
645
646
    /**
647
     * Called when the whole result received
648
     * @return void
649
     */
650
    public function onResultDone()
651
    {
652
        $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...
653
        $this->onResponse->executeOne($this, true);
654
        $this->resultRows = [];
655
        $this->resultFields = [];
656
657
        if ($this->pool->config->protologging->value) {
658
            Daemon::log(__METHOD__);
659
        }
660
    }
661
662
    /**
663
     * Called when error occured
664
     * @return void
665
     */
666
    public function onError()
667
    {
668
        $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...
669
        $this->onResponse->executeOne($this, false);
670
        $this->resultRows = [];
671
        $this->resultFields = [];
672
673
        if ($this->state === self::STATE_AUTH_PACKET_SENT) {
674
            // in case of auth error
675
            $this->state = self::STATE_AUTH_ERROR;
676
            $this->finish();
677
        }
678
679
        Daemon::log(__METHOD__ . ' #' . $this->errno . ': ' . $this->errmsg);
0 ignored issues
show
Documentation introduced by
The property errno does not exist on object<PHPDaemon\Clients\PostgreSQL\Connection>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property errmsg does not exist on object<PHPDaemon\Clients\PostgreSQL\Connection>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
680
    }
681
}
682