IOStream   F
last analyzed

Complexity

Total Complexity 151

Size/Duplication

Total Lines 955
Duplicated Lines 9.01 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
dl 86
loc 955
rs 1.645
c 0
b 0
f 0
wmc 151
lcom 1
cbo 7

47 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 31 8
A __get() 0 11 5
A isFreed() 0 4 1
A isFinished() 0 4 1
A getBev() 0 4 1
A getFd() 0 4 1
A setContext() 0 5 1
F setFd() 16 80 21
A setTimeout() 0 4 1
A setTimeouts() 0 8 2
A setPriority() 0 5 1
A setWatermark() 0 13 4
A init() 0 3 1
A readLine() 0 7 3
A drain() 0 4 1
B drainIfMatch() 0 25 9
A lookExact() 0 10 3
A prependInput() 0 7 2
A prependOutput() 0 7 2
A look() 10 10 3
A substr() 0 7 2
A search() 0 4 1
A readExact() 10 10 4
A getInputLength() 0 4 1
A gracefulShutdown() 0 5 1
A freezeInput() 0 7 2
A unfreezeInput() 0 7 2
A freezeOutput() 0 7 2
A unfreezeOutput() 0 7 2
A onWrite() 0 3 1
B write() 19 20 6
A writeln() 16 17 5
A finish() 0 12 3
A onFinish() 0 3 1
A close() 0 14 4
A unsetFd() 0 5 1
A log() 0 4 1
A onReadEv() 0 15 4
A onRead() 0 3 1
A onReady() 0 3 1
A onWriteOnce() 0 8 2
B onWriteEv() 0 40 10
C onStateEv() 0 31 12
A moveToBuffer() 7 7 2
A writeFromBuffer() 8 8 2
A read() 0 14 4
A readUnlimited() 0 11 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like IOStream often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use IOStream, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace PHPDaemon\Network;
3
4
use PHPDaemon\Core\Daemon;
5
use PHPDaemon\Core\EventLoop;
6
use PHPDaemon\Structures\StackCallbacks;
7
use PHPDaemon\Traits\EventLoopContainer;
8
9
/**
10
 * IOStream
11
 * @package PHPDaemon\Network
12
 * @author  Vasily Zorin <[email protected]>
13
 */
14
abstract class IOStream
15
{
16
    use \PHPDaemon\Traits\ClassWatchdog;
17
    use \PHPDaemon\Traits\StaticObjectWatchdog;
18
    use \PHPDaemon\Traits\EventHandlers;
19
    use EventLoopContainer;
20
21
    /**
22
     * @var object Associated pool
23
     */
24
    public $pool;
25
26
    /**
27
     * @var string EOL
28
     */
29
    protected $EOL = "\n";
30
31
    /**
32
     * @var integer EOLS_* switch
33
     */
34
    protected $EOLS;
35
36
    /**
37
     * @var object EventBufferEvent
38
     */
39
    protected $bev;
40
41
    /**
42
     * @var resource File descriptor
43
     */
44
    protected $fd;
45
46
    /**
47
     * @var boolean Finished?
48
     */
49
    protected $finished = false;
50
51
    /**
52
     * @var boolean Ready?
53
     */
54
    protected $ready = false;
55
56
    /**
57
     * @var boolean Writing?
58
     */
59
    protected $writing = true;
60
61
    /**
62
     * @var boolean Timeout?
63
     */
64
    protected $timedout = false;
65
66
    /**
67
     * @var integer Default low mark. Minimum number of bytes in buffer
68
     */
69
    protected $lowMark = 1;
70
71
    /**
72
     * @var integer Default high mark. Maximum number of bytes in buffer
73
     */
74
    protected $highMark = 0xFFFF; // initial value of the maximum amout of bytes in buffer
75
76
    /**
77
     * @var integer Priority
78
     */
79
    protected $priority;
80
81
    /**
82
     * @var boolean Initialized?
83
     */
84
    protected $inited = false;
85
86
    /**
87
     * @var integer Current state
88
     */
89
    protected $state = 0; // stream state of the connection (application protocol level)
90
91
    /**
92
     * Alias of STATE_STANDBY
93
     */
94
    const STATE_ROOT = 0;
95
96
    /**
97
     * Standby state (default state)
98
     */
99
    const STATE_STANDBY = 0;
100
101
    /**
102
     * @var object Stack of callbacks called when writing is done
103
     */
104
    protected $onWriteOnce;
105
106
    /**
107
     * @var integer Timeout
108
     */
109
    protected $timeout = null;
110
111
    /**
112
     * @var string URL
113
     */
114
    protected $url;
115
116
    /**
117
     * @var boolean Alive?
118
     */
119
    protected $alive = false;
120
121
    /**
122
     * @var boolean Is bevConnect used?
123
     */
124
    protected $bevConnect = false;
125
126
    /**
127
     * @var boolean Should we can onReadEv() in next onWriteEv()?
128
     */
129
    protected $wRead = false;
130
131
    /**
132
     * @var boolean Freed?
133
     */
134
    protected $freed = false;
135
136
    /**
137
     * @var object Context
138
     */
139
    protected $ctx;
140
141
    /**
142
     * @var object Context name
143
     */
144
    protected $ctxname;
145
146
    /**
147
     * @var integer Defines context-related flag
148
     */
149
    protected $ctxMode;
150
151
    /**
152
     * @var boolean SSL?
153
     */
154
    protected $ssl = false;
155
156
    /**
157
     * @var float Read timeout
158
     */
159
    protected $timeoutRead;
160
161
    /**
162
     * @var float Write timeout
163
     */
164
    protected $timeoutWrite;
165
166
    /**
167
     * IOStream constructor
168
     * @param resource $fd File descriptor. Optional
0 ignored issues
show
Documentation introduced by
Should the type for parameter $fd not be resource|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...
169
     * @param object $pool Pool. Optional
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pool not be object|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...
170
     */
171
    public function __construct($fd = null, $pool = null)
172
    {
173
        if ($pool) {
174
            $this->pool = $pool;
175
            $this->eventLoop = $pool->eventLoop;
176
            $this->pool->attach($this);
177
            if (isset($this->pool->config->timeout->value)) {
178
                $this->timeout = $this->pool->config->timeout->value;
179
            }
180
            if (isset($this->pool->config->timeoutread->value)) {
181
                $this->timeoutRead = $this->pool->config->timeoutread->value;
182
            }
183
            if (isset($this->pool->config->timeoutwrite->value)) {
184
                $this->timeoutWrite = $this->pool->config->timeoutwrite->value;
185
            }
186
        }
187
188
        if ($fd !== null) {
189
            $this->setFd($fd);
190
        }
191
192
        if ($this->EOL === "\n") {
193
            $this->EOLS = \EventBuffer::EOL_LF;
194
        } elseif ($this->EOL === "\r\n") {
195
            $this->EOLS = \EventBuffer::EOL_CRLF;
196
        } else {
197
            $this->EOLS = \EventBuffer::EOL_ANY;
198
        }
199
200
        $this->onWriteOnce = new StackCallbacks;
201
    }
202
203
    /**
204
     * Getter
205
     * @param  string $name Name
206
     * @return mixed
207
     */
208
    public function __get($name)
209
    {
210
        if ($name === 'finished'
211
            || $name === 'alive'
212
            || $name === 'freed'
213
            || $name === 'url'
214
        ) {
215
            return $this->{$name};
216
        }
217
        return null;
218
    }
219
220
221
    /**
222
     * Freed?
223
     * @return boolean
224
     */
225
    public function isFreed()
226
    {
227
        return $this->freed;
228
    }
229
230
    /**
231
     * Finished?
232
     * @return boolean
233
     */
234
    public function isFinished()
235
    {
236
        return $this->finished;
237
    }
238
239
    /**
240
     * Get EventBufferEvent
241
     * @return EventBufferEvent
242
     */
243
    public function getBev()
244
    {
245
        return $this->bev;
246
    }
247
248
    /**
249
     * Get file descriptor
250
     * @return resource File descriptor
251
     */
252
    public function getFd()
253
    {
254
        return $this->fd;
255
    }
256
257
    /**
258
     * Sets context mode
259
     * @param  object $ctx Context
260
     * @param  integer $mode Mode
261
     * @return void
262
     */
263
264
    public function setContext($ctx, $mode)
265
    {
266
        $this->ctx = $ctx;
267
        $this->ctxMode = $mode;
268
    }
269
270
    /**
271
     * Sets fd
272
     * @param  resource $fd File descriptor
273
     * @param  object $bev EventBufferEvent
0 ignored issues
show
Documentation introduced by
Should the type for parameter $bev not be object|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...
274
     * @return void
275
     */
276
    public function setFd($fd, $bev = null)
277
    {
278
        if ($this->eventLoop === null) {
279
            $this->eventLoop = EventLoop::$instance;
280
        }
281
        $this->fd = $fd;
282
        if ($this->fd === false) {
283
            $this->finish();
284
            return;
285
        }
286
        if ($bev !== null) {
287
            $this->bev = $bev;
288
            $this->bev->setCallbacks([$this, 'onReadEv'], [$this, 'onWriteEv'], [$this, 'onStateEv']);
289
            if (!$this->bev) {
290
                return;
291
            }
292
            $this->ready = true;
293
            $this->alive = true;
294
        } else {
295
            $flags = !is_resource($this->fd) ? \EventBufferEvent::OPT_CLOSE_ON_FREE : 0;
296
            $flags |= \EventBufferEvent::OPT_DEFER_CALLBACKS; /* buggy option */
297
            if ($this->ctx) {
298
                if ($this->ctx instanceof \EventSslContext) {
0 ignored issues
show
Bug introduced by
The class EventSslContext does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
299
                    $this->bev = $this->eventLoop->bufferEventSsl(
300
                        $this->fd,
301
                        $this->ctx,
302
                        $this->ctxMode,
303
                        $flags
304
                    );
305
306 View Code Duplication
                    if ($this->bev) {
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...
307
                        $this->bev->setCallbacks([$this, 'onReadEv'], [$this, 'onWriteEv'], [$this, 'onStateEv']);
308
                    }
309
                    $this->ssl = true;
310
                } else {
311
                    $this->log('unsupported type of context: ' . ($this->ctx ? get_class($this->ctx) : 'undefined'));
312
                    return;
313
                }
314 View Code Duplication
            } else {
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...
315
                $this->bev = $this->eventLoop->bufferEvent(
316
                    $this->fd,
317
                    $flags,
318
                    [$this, 'onReadEv'],
319
                    [$this, 'onWriteEv'],
320
                    [$this, 'onStateEv']
321
                );
322
            }
323
            if (!$this->bev) {
324
                return;
325
            }
326
        }
327
        if ($this->priority !== null) {
328
            $this->bev->priority = $this->priority;
329
        }
330
        $this->setTimeouts(
331
            $this->timeoutRead !== null ? $this->timeoutRead : $this->timeout,
332
            $this->timeoutWrite !== null ? $this->timeoutWrite : $this->timeout
333
        );
334
        if ($this->bevConnect && ($this->fd === null)) {
335
            //$this->bev->connectHost(Daemon::$process->dnsBase, $this->hostReal, $this->port);
336
            $this->bev->connect($this->addr);
0 ignored issues
show
Documentation introduced by
The property addr does not exist on object<PHPDaemon\Network\IOStream>. 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...
337
        }
338
        if (!$this->bev) {
339
            $this->finish();
340
            return;
341
        }
342 View Code Duplication
        if (!$this->bev->enable(\Event::READ | \Event::WRITE | \Event::TIMEOUT | \Event::PERSIST)) {
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...
343
            $this->finish();
344
            return;
345
        }
346
        $this->bev->setWatermark(\Event::READ, $this->lowMark, $this->highMark);
347
        init:
348
        if ($this->keepalive && $this->fd != null) {
0 ignored issues
show
Documentation introduced by
The property keepalive does not exist on object<PHPDaemon\Network\IOStream>. 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...
349
            $this->setKeepalive(true);
0 ignored issues
show
Documentation Bug introduced by
The method setKeepalive does not exist on object<PHPDaemon\Network\IOStream>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
350
        }
351
        if (!$this->inited) {
352
            $this->inited = true;
353
            $this->init();
354
        }
355
    }
356
357
    /**
358
     * Set timeout
359
     * @param  integer $rw Timeout
360
     * @return void
361
     */
362
    public function setTimeout($rw)
363
    {
364
        $this->setTimeouts($rw, $rw);
365
    }
366
367
    /**
368
     * Set timeouts
369
     * @param  integer $read Read timeout in seconds
370
     * @param  integer $write Write timeout in seconds
371
     * @return void
372
     */
373
    public function setTimeouts($read, $write)
374
    {
375
        $this->timeoutRead = $read;
0 ignored issues
show
Documentation Bug introduced by
The property $timeoutRead was declared of type double, but $read 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...
376
        $this->timeoutWrite = $write;
0 ignored issues
show
Documentation Bug introduced by
The property $timeoutWrite was declared of type double, but $write 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...
377
        if ($this->bev) {
378
            $this->bev->setTimeouts($this->timeoutRead, $this->timeoutWrite);
379
        }
380
    }
381
382
    /**
383
     * Sets priority
384
     * @param  integer $p Priority
385
     * @return void
386
     */
387
    public function setPriority($p)
388
    {
389
        $this->priority = $p;
390
        $this->bev->priority = $p;
391
    }
392
393
    /**
394
     * Sets watermark
395
     * @param  integer|null $low Low
396
     * @param  integer|null $high High
397
     * @return void
398
     */
399
    public function setWatermark($low = null, $high = null)
400
    {
401
        if ($low !== null) {
402
            $this->lowMark = $low;
403
        }
404
        if ($high !== null) {
405
            $this->highMark = $high;
406
        }
407
        if ($this->highMark > 0) {
408
            $this->highMark = max($this->lowMark, $this->highMark);
409
        }
410
        $this->bev->setWatermark(\Event::READ, $this->lowMark, $this->highMark);
411
    }
412
413
    /**
414
     * Constructor
415
     * @return void
416
     */
417
    protected function init()
418
    {
419
    }
420
421
    /**
422
     * Reads line from buffer
423
     * @param  integer $eol EOLS_*
0 ignored issues
show
Documentation introduced by
Should the type for parameter $eol not be integer|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...
424
     * @return string|null
425
     */
426
    public function readLine($eol = null)
427
    {
428
        if (!isset($this->bev)) {
429
            return null;
430
        }
431
        return $this->bev->input->readLine($eol ?: $this->EOLS);
432
    }
433
434
    /**
435
     * Drains buffer
436
     * @param  integer $n Numbers of bytes to drain
437
     * @return boolean    Success
438
     */
439
    public function drain($n)
440
    {
441
        return $this->bev->input->drain($n);
442
    }
443
444
    /**
445
     * Drains buffer it matches the string
446
     * @param  string $str Data
447
     * @return boolean|null      Success
448
     */
449
    public function drainIfMatch($str)
450
    {
451
        if (!isset($this->bev)) {
452
            return false;
453
        }
454
        $in = $this->bev->input;
455
        $l = mb_orig_strlen($str);
456
        $ll = $in->length;
457
        if ($ll === 0) {
458
            return $l === 0 ? true : null;
459
        }
460
        if ($ll < $l) {
461
            return $in->search(substr($str, 0, $ll)) === 0 ? null : false;
462
        }
463
        if ($ll === $l) {
464
            if ($in->search($str) === 0) {
465
                $in->drain($l);
466
                return true;
467
            }
468
        } elseif ($in->search($str, 0, $l) === 0) {
469
            $in->drain($l);
470
            return true;
471
        }
472
        return false;
473
    }
474
475
    /**
476
     * Reads exact $n bytes of buffer without draining
477
     * @param  integer $n Number of bytes to read
478
     * @param  integer $o Offset
479
     * @return string|false
480
     */
481
    public function lookExact($n, $o = 0)
482
    {
483
        if (!isset($this->bev)) {
484
            return false;
485
        }
486
        if ($o + $n > $this->bev->input->length) {
487
            return false;
488
        }
489
        return $this->bev->input->substr($o, $n);
490
    }
491
492
    /**
493
     * Prepends data to input buffer
494
     * @param  string $str Data
495
     * @return boolean      Success
496
     */
497
    public function prependInput($str)
498
    {
499
        if (!isset($this->bev)) {
500
            return false;
501
        }
502
        return $this->bev->input->prepend($str);
503
    }
504
505
    /**
506
     * Prepends data to output buffer
507
     * @param  string $str Data
508
     * @return boolean      Success
509
     */
510
    public function prependOutput($str)
511
    {
512
        if (!isset($this->bev)) {
513
            return false;
514
        }
515
        return $this->bev->output->prepend($str);
516
    }
517
518
    /**
519
     * Read from buffer without draining
520
     * @param integer $n Number of bytes to read
521
     * @param integer $o Offset
522
     * @return string|false
523
     */
524 View Code Duplication
    public function look($n, $o = 0)
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...
525
    {
526
        if (!isset($this->bev)) {
527
            return false;
528
        }
529
        if ($this->bev->input->length <= $o) {
530
            return '';
531
        }
532
        return $this->bev->input->substr($o, $n);
533
    }
534
535
    /**
536
     * Read from buffer without draining
537
     * @param  integer $o Offset
538
     * @param  integer $n Number of bytes to read
539
     * @return string|false
540
     */
541
    public function substr($o, $n = -1)
542
    {
543
        if (!isset($this->bev)) {
544
            return false;
545
        }
546
        return $this->bev->input->substr($o, $n);
547
    }
548
549
    /**
550
     * Searches first occurence of the string in input buffer
551
     * @param  string $what Needle
552
     * @param  integer $start Offset start
553
     * @param  integer $end Offset end
554
     * @return integer        Position
555
     */
556
    public function search($what, $start = 0, $end = -1)
557
    {
558
        return $this->bev->input->search($what, $start, $end);
559
    }
560
561
    /**
562
     * Reads exact $n bytes from buffer
563
     * @param  integer $n Number of bytes to read
564
     * @return string|false
565
     */
566 View Code Duplication
    public function readExact($n)
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...
567
    {
568
        if ($n === 0) {
569
            return '';
570
        }
571
        if (!$this->bev || $this->bev->input->length < $n) {
572
            return false;
573
        }
574
        return $this->read($n);
575
    }
576
577
    /**
578
     * Returns length of input buffer
579
     * @return integer
580
     */
581
    public function getInputLength()
582
    {
583
        return $this->bev->input->length;
584
    }
585
586
    /**
587
     * Called when the worker is going to shutdown
588
     * @return boolean Ready to shutdown?
589
     */
590
    public function gracefulShutdown()
591
    {
592
        $this->finish();
593
        return true;
594
    }
595
596
    /**
597
     * Freeze input
598
     * @param  boolean $at_front At front. Default is true. If the front of a buffer is frozen, operations that drain data from the front of the buffer, or that prepend data to the buffer, will fail until it is unfrozen. If the back a buffer is frozen, operations that append data from the buffer will fail until it is unfrozen
599
     * @return boolean           Success
600
     */
601
    public function freezeInput($at_front = true)
602
    {
603
        if (isset($this->bev)) {
604
            return $this->bev->input->freeze($at_front);
605
        }
606
        return false;
607
    }
608
609
    /**
610
     * Unfreeze input
611
     * @param  boolean $at_front At front. Default is true. If the front of a buffer is frozen, operations that drain data from the front of the buffer, or that prepend data to the buffer, will fail until it is unfrozen. If the back a buffer is frozen, operations that append data from the buffer will fail until it is unfrozen
612
     * @return boolean           Success
613
     */
614
    public function unfreezeInput($at_front = true)
615
    {
616
        if (isset($this->bev)) {
617
            return $this->bev->input->unfreeze($at_front);
618
        }
619
        return false;
620
    }
621
622
    /**
623
     * Freeze output
624
     * @param  boolean $at_front At front. Default is true. If the front of a buffer is frozen, operations that drain data from the front of the buffer, or that prepend data to the buffer, will fail until it is unfrozen. If the back a buffer is frozen, operations that append data from the buffer will fail until it is unfrozen
625
     * @return boolean           Success
626
     */
627
    public function freezeOutput($at_front = true)
628
    {
629
        if (isset($this->bev)) {
630
            return $this->bev->output->unfreeze($at_front);
631
        }
632
        return false;
633
    }
634
635
    /**
636
     * Unfreeze output
637
     * @param  boolean $at_front At front. Default is true. If the front of a buffer is frozen, operations that drain data from the front of the buffer, or that prepend data to the buffer, will fail until it is unfrozen. If the back a buffer is frozen, operations that append data from the buffer will fail until it is unfrozen
638
     * @return boolean           Success
639
     */
640
    public function unfreezeOutput($at_front = true)
641
    {
642
        if (isset($this->bev)) {
643
            return $this->bev->output->unfreeze($at_front);
644
        }
645
        return false;
646
    }
647
648
    /**
649
     * Called when the connection is ready to accept new data
650
     * @return void
651
     */
652
    public function onWrite()
653
    {
654
    }
655
656
    /**
657
     * Send data to the connection. Note that it just writes to buffer that flushes at every baseloop
658
     * @param  string $data Data to send
659
     * @return boolean       Success
660
     */
661 View Code Duplication
    public function write($data)
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...
662
    {
663
        if (!$this->alive) {
664
            Daemon::log('Attempt to write to dead IOStream (' . get_class($this) . ')');
665
            return false;
666
        }
667
        if (!isset($this->bev)) {
668
            return false;
669
        }
670
        if (!mb_orig_strlen($data)) {
671
            return true;
672
        }
673
        $this->writing = true;
674
        Daemon::$noError = true;
675
        if (!$this->bev->write($data) || !Daemon::$noError) {
676
            $this->close();
677
            return false;
678
        }
679
        return true;
680
    }
681
682
    /**
683
     * Send data and appending \n to connection. Note that it just writes to buffer flushed at every baseloop
684
     * @param  string $data Data to send
685
     * @return boolean       Success
686
     */
687 View Code Duplication
    public function writeln($data)
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...
688
    {
689
        if (!$this->alive) {
690
            Daemon::log('Attempt to write to dead IOStream (' . get_class($this) . ')');
691
            return false;
692
        }
693
        if (!isset($this->bev)) {
694
            return false;
695
        }
696
        if (!mb_orig_strlen($data) && !mb_orig_strlen($this->EOL)) {
697
            return true;
698
        }
699
        $this->writing = true;
700
        $this->bev->write($data);
701
        $this->bev->write($this->EOL);
702
        return true;
703
    }
704
705
    /**
706
     * Finish the session. You should not worry about buffers, they are going to be flushed properly
707
     * @return void
708
     */
709
    public function finish()
710
    {
711
        if ($this->finished) {
712
            return;
713
        }
714
        $this->finished = true;
715
        $this->eventLoop->interrupt();
716
        $this->onFinish();
717
        if (!$this->writing) {
718
            $this->close();
719
        }
720
    }
721
722
    /**
723
     * Called when the session finished
724
     * @return void
725
     */
726
    protected function onFinish()
727
    {
728
    }
729
730
    /**
731
     * Close the connection
732
     * @return void
733
     */
734
    public function close()
735
    {
736
        if (!$this->freed) {
737
            $this->freed = true;
738
            if (isset($this->bev)) {
739
                $this->bev->free();
740
            }
741
            $this->bev = null;
742
            //$this->eventLoop->interrupt();
743
        }
744
        if ($this->pool) {
745
            $this->pool->detach($this);
746
        }
747
    }
748
749
    /**
750
     * Unsets pointers of associated EventBufferEvent and File descriptr
751
     * @return void
752
     */
753
    public function unsetFd()
754
    {
755
        $this->bev = null;
756
        $this->fd = null;
757
    }
758
759
    /**
760
     * Send message to log
761
     * @param  string $m Message
762
     * @return void
763
     */
764
    protected function log($m)
765
    {
766
        Daemon::log(get_class($this) . ': ' . $m);
767
    }
768
769
    /**
770
     * Called when the connection has got new data
771
     * @param  object $bev EventBufferEvent
772
     * @return void
773
     */
774
    public function onReadEv($bev)
0 ignored issues
show
Unused Code introduced by
The parameter $bev 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...
775
    {
776
        if (!$this->ready) {
777
            $this->wRead = true;
778
            return;
779
        }
780
        if ($this->finished) {
781
            return;
782
        }
783
        try {
784
            $this->onRead();
785
        } catch (\Throwable $e) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
786
            Daemon::uncaughtExceptionHandler($e);
787
        }
788
    }
789
790
    /**
791
     * Called when new data received
792
     * @return void
793
     */
794
    protected function onRead()
795
    {
796
    }
797
798
    /**
799
     * Called when the stream is handshaked (at low-level), and peer is ready to recv. data
800
     * @return void
801
     */
802
    protected function onReady()
803
    {
804
    }
805
806
    /**
807
     * Push callback which will be called only once, when writing is available next time
808
     * @param  callable $cb Callback
809
     * @return void
810
     */
811
    public function onWriteOnce($cb)
812
    {
813
        if (!$this->writing) {
814
            $cb($this);
815
            return;
816
        }
817
        $this->onWriteOnce->push($cb);
818
    }
819
820
    /**
821
     * Called when the connection is ready to accept new data
822
     * @param  object $bev EventBufferEvent
823
     * @return void
824
     */
825
    public function onWriteEv($bev)
0 ignored issues
show
Unused Code introduced by
The parameter $bev 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...
826
    {
827
        $this->writing = false;
828
        if ($this->finished) {
829
            if ($this->bev->output->length === 0) {
830
                $this->close();
831
            }
832
            return;
833
        }
834
        if (!$this->ready) {
835
            $this->ready = true;
836
            while (!$this->onWriteOnce->isEmpty()) {
837
                try {
838
                    $this->onWriteOnce->executeOne($this);
839
                } catch (\Throwable $e) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
840
                    Daemon::uncaughtExceptionHandler($e);
841
                }
842
                if (!$this->ready) {
843
                    return;
844
                }
845
            }
846
            $this->alive = true;
847
            try {
848
                $this->onReady();
849
                if ($this->wRead) {
850
                    $this->wRead = false;
851
                    $this->onRead();
852
                }
853
            } catch (\Throwable $e) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
854
                Daemon::uncaughtExceptionHandler($e);
855
            }
856
        } else {
857
            $this->onWriteOnce->executeAll($this);
858
        }
859
        try {
860
            $this->onWrite();
861
        } catch (\Throwable $e) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
862
            Daemon::uncaughtExceptionHandler($e);
863
        }
864
    }
865
866
    /**
867
     * Called when the connection state changed
868
     * @param  object $bev EventBufferEvent
869
     * @param  integer $events Events
870
     * @return void
871
     */
872
    public function onStateEv($bev, $events)
873
    {
874
        if ($events & \EventBufferEvent::CONNECTED) {
875
            $this->onWriteEv($bev);
876
        } elseif ($events & \EventBufferEvent::TIMEOUT) {
877
            $this->timedout = true;
878
            $this->finish();
879
        } elseif ($events & (\EventBufferEvent::ERROR | \EventBufferEvent::EOF)) {
880
            try {
881
                if ($this->finished) {
882
                    return;
883
                }
884
                if ($events & \EventBufferEvent::ERROR) {
885
                    $errno = \EventUtil::getLastSocketErrno();
886
                    if ($errno !== 0 && $errno !== 104) {
887
                        $this->log('Socket error #' . $errno . ':' . \EventUtil::getLastSocketError());
888
                    }
889
                    if ($this->ssl && $this->bev) {
890
                        while ($err = $this->bev->sslError()) {
891
                            $this->log('EventBufferEvent SSL error: ' . $err);
892
                        }
893
                    }
894
                }
895
                $this->finished = true;
896
                $this->onFinish();
897
                $this->close();
898
            } catch (\Throwable $e) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
899
                Daemon::uncaughtExceptionHandler($e);
900
            }
901
        }
902
    }
903
904
    /**
905
     * Moves arbitrary number of bytes from input buffer to given buffer
906
     * @param  \EventBuffer $dest Destination nuffer
907
     * @param  integer $n Max. number of bytes to move
908
     * @return integer|false
909
     */
910 View Code Duplication
    public function moveToBuffer(\EventBuffer $dest, $n)
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...
911
    {
912
        if (!isset($this->bev)) {
913
            return false;
914
        }
915
        return $dest->appendFrom($this->bev->input, $n);
916
    }
917
918
    /**
919
     * Moves arbitrary number of bytes from given buffer to output buffer
920
     * @param  \EventBuffer $src Source buffer
921
     * @param  integer $n Max. number of bytes to move
922
     * @return integer|false
923
     */
924 View Code Duplication
    public function writeFromBuffer(\EventBuffer $src, $n)
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...
925
    {
926
        if (!isset($this->bev)) {
927
            return false;
928
        }
929
        $this->writing = true;
930
        return $this->bev->output->appendFrom($src, $n);
931
    }
932
933
    /**
934
     * Read data from the connection's buffer
935
     * @param  integer $n Max. number of bytes to read
936
     * @return string|false    Readed data
937
     */
938
    public function read($n)
939
    {
940
        if ($n <= 0) {
941
            return '';
942
        }
943
        if (!isset($this->bev)) {
944
            return false;
945
        }
946
        $read = $this->bev->read($n);
947
        if ($read === null) {
948
            return false;
949
        }
950
        return $read;
951
    }
952
953
    /**
954
     * Reads all data from the connection's buffer
955
     * @return string Readed data
956
     */
957
    public function readUnlimited()
958
    {
959
        if (!isset($this->bev)) {
960
            return false;
961
        }
962
        $read = $this->bev->read($this->bev->input->length);
963
        if ($read === null) {
964
            return false;
965
        }
966
        return $read;
967
    }
968
}
969