Completed
Pull Request — master (#243)
by Дмитрий
05:05
created

IOStream::setFd()   D

Complexity

Conditions 20
Paths 178

Size

Total Lines 79
Code Lines 59

Duplication

Lines 4
Ratio 5.06 %

Importance

Changes 2
Bugs 2 Features 0
Metric Value
cc 20
eloc 59
c 2
b 2
f 0
nc 178
nop 2
dl 4
loc 79
rs 4.7291

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\Network;
3
4
use PHPDaemon\Core\Daemon;
5
use PHPDaemon\Core\Debug;
6
use PHPDaemon\FS\File;
7
use PHPDaemon\Structures\StackCallbacks;
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
20
    /**
21
     * @var object Associated pool
22
     */
23
    public $pool;
24
25
    /**
26
     * @var string EOL
27
     */
28
    protected $EOL = "\n";
29
30
    /**
31
     * @var integer EOLS_* switch
32
     */
33
    protected $EOLS;
34
35
    /**
36
     * @var object EventBufferEvent
37
     */
38
    protected $bev;
39
40
    /**
41
     * @var resource File descriptor
42
     */
43
    protected $fd;
44
45
    /**
46
     * @var boolean Finished?
47
     */
48
    protected $finished = false;
49
50
    /**
51
     * @var boolean Ready?
52
     */
53
    protected $ready = false;
54
55
    /**
56
     * @var boolean Writing?
57
     */
58
    protected $writing = true;
59
60
    /**
61
     * @var boolean Timeout?
62
     */
63
    protected $timedout = false;
64
65
    /**
66
     * @var integer Default low mark. Minimum number of bytes in buffer
67
     */
68
    protected $lowMark = 1;
69
70
    /**
71
     * @var integer Default high mark. Maximum number of bytes in buffer
72
     */
73
    protected $highMark = 0xFFFF; // initial value of the maximum amout of bytes in buffer
74
75
    /**
76
     * @var integer Priority
77
     */
78
    protected $priority;
79
80
    /**
81
     * @var boolean Initialized?
82
     */
83
    protected $inited = false;
84
85
    /**
86
     * @var integer Current state
87
     */
88
    protected $state = 0; // stream state of the connection (application protocol level)
89
90
    /**
91
     * Alias of STATE_STANDBY
92
     */
93
    const STATE_ROOT = 0;
94
95
    /**
96
     * Standby state (default state)
97
     */
98
    const STATE_STANDBY = 0;
99
100
    /**
101
     * @var object Stack of callbacks called when writing is done
102
     */
103
    protected $onWriteOnce;
104
105
    /**
106
     * @var integer Timeout
107
     */
108
    protected $timeout = null;
109
110
    /**
111
     * @var string URL
112
     */
113
    protected $url;
114
115
    /**
116
     * @var boolean Alive?
117
     */
118
    protected $alive = false;
119
120
    /**
121
     * @var boolean Is bevConnect used?
122
     */
123
    protected $bevConnect = false;
124
125
    /**
126
     * @var boolean Should we can onReadEv() in next onWriteEv()?
127
     */
128
    protected $wRead = false;
129
130
    /**
131
     * @var boolean Freed?
132
     */
133
    protected $freed = false;
134
135
    /**
136
     * @var object Context
137
     */
138
    protected $ctx;
139
140
    /**
141
     * @var object Context name
142
     */
143
    protected $ctxname;
144
145
    /**
146
     * @var integer Defines context-related flag
147
     */
148
    protected $ctxMode;
149
150
    /**
151
     * @var boolean SSL?
152
     */
153
    protected $ssl = false;
154
155
    /**
156
     * @var float Read timeout
157
     */
158
    protected $timeoutRead;
159
160
    /**
161
     * @var float Write timeout
162
     */
163
    protected $timeoutWrite;
164
165
    /**
166
     * IOStream constructor
167
     * @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...
168
     * @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...
169
     */
170
    public function __construct($fd = null, $pool = null)
171
    {
172
        if ($pool) {
173
            $this->pool = $pool;
174
            $this->pool->attach($this);
175
            if (isset($this->pool->config->timeout->value)) {
176
                $this->timeout = $this->pool->config->timeout->value;
177
            }
178
            if (isset($this->pool->config->timeoutread->value)) {
179
                $this->timeoutRead = $this->pool->config->timeoutread->value;
180
            }
181
            if (isset($this->pool->config->timeoutwrite->value)) {
182
                $this->timeoutWrite = $this->pool->config->timeoutwrite->value;
183
            }
184
        }
185
186
        if ($fd !== null) {
187
            $this->setFd($fd);
188
        }
189
190
        if ($this->EOL === "\n") {
191
            $this->EOLS = \EventBuffer::EOL_LF;
192
        } elseif ($this->EOL === "\r\n") {
193
            $this->EOLS = \EventBuffer::EOL_CRLF;
194
        } else {
195
            $this->EOLS = \EventBuffer::EOL_ANY;
196
        }
197
198
        $this->onWriteOnce = new StackCallbacks;
199
    }
200
201
    /**
202
     * Getter
203
     * @param  string $name Name
204
     * @return mixed
205
     */
206
    public function __get($name)
207
    {
208
        if ($name === 'finished'
209
            || $name === 'alive'
210
            || $name === 'freed'
211
            || $name === 'url'
212
        ) {
213
            return $this->{$name};
214
        }
215
        return null;
216
    }
217
218
219
    /**
220
     * Freed?
221
     * @return boolean
222
     */
223
    public function isFreed()
224
    {
225
        return $this->freed;
226
    }
227
228
    /**
229
     * Finished?
230
     * @return boolean
231
     */
232
    public function isFinished()
233
    {
234
        return $this->finished;
235
    }
236
237
    /**
238
     * Get EventBufferEvent
239
     * @return EventBufferEvent
240
     */
241
    public function getBev()
242
    {
243
        return $this->bev;
244
    }
245
246
    /**
247
     * Get file descriptor
248
     * @return resource File descriptor
249
     */
250
    public function getFd()
251
    {
252
        return $this->fd;
253
    }
254
255
    /**
256
     * Sets context mode
257
     * @param  object $ctx Context
258
     * @param  integer $mode Mode
259
     * @return void
260
     */
261
262
    public function setContext($ctx, $mode)
263
    {
264
        $this->ctx = $ctx;
265
        $this->ctxMode = $mode;
266
    }
267
268
    /**
269
     * Sets fd
270
     * @param  resource $fd File descriptor
271
     * @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...
272
     * @return void
273
     */
274
    public function setFd($fd, $bev = null)
275
    {
276
        $this->fd = $fd;
277
        if ($this->fd === false) {
278
            $this->finish();
279
            return;
280
        }
281
        if ($bev !== null) {
282
            $this->bev = $bev;
283
            $this->bev->setCallbacks([$this, 'onReadEv'], [$this, 'onWriteEv'], [$this, 'onStateEv']);
284
            if (!$this->bev) {
285
                return;
286
            }
287
            $this->ready = true;
288
            $this->alive = true;
289
        } else {
290
            $flags = !is_resource($this->fd) ? \EventBufferEvent::OPT_CLOSE_ON_FREE : 0;
291
            $flags |= \EventBufferEvent::OPT_DEFER_CALLBACKS; /* buggy option */
292
            if ($this->ctx) {
293
                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...
294
                    $this->bev = \EventBufferEvent::sslSocket(
295
                        Daemon::$process->eventBase,
296
                        $this->fd,
297
                        $this->ctx,
298
                        $this->ctxMode,
299
                        $flags
300
                    );
301
302
                    if ($this->bev) {
303
                        $this->bev->setCallbacks([$this, 'onReadEv'], [$this, 'onWriteEv'], [$this, 'onStateEv']);
304
                    }
305
                    $this->ssl = true;
306
                } else {
307
                    $this->log('unsupported type of context: ' . ($this->ctx ? get_class($this->ctx) : 'undefined'));
308
                    return;
309
                }
310
            } else {
311
                $this->bev = new \EventBufferEvent(
312
                    Daemon::$process->eventBase,
313
                    $this->fd,
314
                    $flags,
315
                    [$this, 'onReadEv'],
316
                    [$this, 'onWriteEv'],
317
                    [$this, 'onStateEv']
318
                );
319
            }
320
            if (!$this->bev) {
321
                return;
322
            }
323
        }
324
        if ($this->priority !== null) {
325
            $this->bev->priority = $this->priority;
326
        }
327
        $this->setTimeouts(
328
            $this->timeoutRead !== null ? $this->timeoutRead : $this->timeout,
329
            $this->timeoutWrite !== null ? $this->timeoutWrite : $this->timeout
330
        );
331
        if ($this->bevConnect && ($this->fd === null)) {
332
            //$this->bev->connectHost(Daemon::$process->dnsBase, $this->hostReal, $this->port);
0 ignored issues
show
Unused Code Comprehensibility introduced by
66% 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...
333
            $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...
334
        }
335
        if (!$this->bev) {
336
            $this->finish();
337
            return;
338
        }
339 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...
340
            $this->finish();
341
            return;
342
        }
343
        $this->bev->setWatermark(\Event::READ, $this->lowMark, $this->highMark);
344
        init:
345
        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...
346
            $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...
347
        }
348
        if (!$this->inited) {
349
            $this->inited = true;
350
            $this->init();
351
        }
352
    }
353
354
    /**
355
     * Set timeout
356
     * @param  integer $rw Timeout
357
     * @return void
358
     */
359
    public function setTimeout($rw)
360
    {
361
        $this->setTimeouts($rw, $rw);
362
    }
363
364
    /**
365
     * Set timeouts
366
     * @param  integer $read Read timeout in seconds
367
     * @param  integer $write Write timeout in seconds
368
     * @return void
369
     */
370
    public function setTimeouts($read, $write)
371
    {
372
        $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...
373
        $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...
374
        if ($this->bev) {
375
            $this->bev->setTimeouts($this->timeoutRead, $this->timeoutWrite);
376
        }
377
    }
378
379
    /**
380
     * Sets priority
381
     * @param  integer $p Priority
382
     * @return void
383
     */
384
    public function setPriority($p)
385
    {
386
        $this->priority = $p;
387
        $this->bev->priority = $p;
388
    }
389
390
    /**
391
     * Sets watermark
392
     * @param  integer|null $low Low
393
     * @param  integer|null $high High
394
     * @return void
395
     */
396
    public function setWatermark($low = null, $high = null)
397
    {
398
        if ($low !== null) {
399
            $this->lowMark = $low;
400
        }
401
        if ($high !== null) {
402
            $this->highMark = $high;
403
        }
404
        if ($this->highMark > 0) {
405
            $this->highMark = max($this->lowMark, $this->highMark);
406
        }
407
        $this->bev->setWatermark(\Event::READ, $this->lowMark, $this->highMark);
408
    }
409
410
    /**
411
     * Called when the session constructed
412
     * @return void
413
     */
414
    protected function init()
415
    {
416
    }
417
418
    /**
419
     * Reads line from buffer
420
     * @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...
421
     * @return string|null
422
     */
423
    public function readLine($eol = null)
424
    {
425
        if (!isset($this->bev)) {
426
            return null;
427
        }
428
        return $this->bev->input->readLine($eol ?: $this->EOLS);
429
    }
430
431
    /**
432
     * Drains buffer
433
     * @param  integer $n Numbers of bytes to drain
434
     * @return boolean    Success
435
     */
436
    public function drain($n)
437
    {
438
        return $this->bev->input->drain($n);
439
    }
440
441
    /**
442
     * Drains buffer it matches the string
443
     * @param  string $str Data
444
     * @return boolean|null      Success
445
     */
446
    public function drainIfMatch($str)
447
    {
448
        if (!isset($this->bev)) {
449
            return false;
450
        }
451
        $in = $this->bev->input;
452
        $l = mb_orig_strlen($str);
453
        $ll = $in->length;
454
        if ($ll === 0) {
455
            return $l === 0 ? true : null;
456
        }
457
        if ($ll < $l) {
458
            return $in->search(substr($str, 0, $ll)) === 0 ? null : false;
459
        }
460
        if ($ll === $l) {
461
            if ($in->search($str) === 0) {
462
                $in->drain($l);
463
                return true;
464
            }
465
        } elseif ($in->search($str, 0, $l) === 0) {
466
            $in->drain($l);
467
            return true;
468
        }
469
        return false;
470
    }
471
472
    /**
473
     * Reads exact $n bytes of buffer without draining
474
     * @param  integer $n Number of bytes to read
475
     * @param  integer $o Offset
476
     * @return string|false
477
     */
478 View Code Duplication
    public function lookExact($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...
479
    {
480
        if (!isset($this->bev)) {
481
            return false;
482
        }
483
        if ($o + $n > $this->bev->input->length) {
484
            return false;
485
        }
486
        return $this->bev->input->substr($o, $n);
487
    }
488
489
    /**
490
     * Prepends data to input buffer
491
     * @param  string $str Data
492
     * @return boolean      Success
493
     */
494
    public function prependInput($str)
495
    {
496
        if (!isset($this->bev)) {
497
            return false;
498
        }
499
        return $this->bev->input->prepend($str);
500
    }
501
502
    /**
503
     * Prepends data to output buffer
504
     * @param  string $str Data
505
     * @return boolean      Success
506
     */
507
    public function prependOutput($str)
508
    {
509
        if (!isset($this->bev)) {
510
            return false;
511
        }
512
        return $this->bev->output->prepend($str);
513
    }
514
515
    /**
516
     * Read from buffer without draining
517
     * @param integer $n Number of bytes to read
518
     * @param integer $o Offset
519
     * @return string|false
520
     */
521 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...
522
    {
523
        if (!isset($this->bev)) {
524
            return false;
525
        }
526
        if ($this->bev->input->length <= $o) {
527
            return '';
528
        }
529
        return $this->bev->input->substr($o, $n);
530
    }
531
532
    /**
533
     * Read from buffer without draining
534
     * @param  integer $o Offset
535
     * @param  integer $n Number of bytes to read
536
     * @return string|false
537
     */
538
    public function substr($o, $n = -1)
539
    {
540
        if (!isset($this->bev)) {
541
            return false;
542
        }
543
        return $this->bev->input->substr($o, $n);
544
    }
545
546
    /**
547
     * Searches first occurence of the string in input buffer
548
     * @param  string $what Needle
549
     * @param  integer $start Offset start
550
     * @param  integer $end Offset end
551
     * @return integer        Position
552
     */
553
    public function search($what, $start = 0, $end = -1)
554
    {
555
        return $this->bev->input->search($what, $start, $end);
556
    }
557
558
    /**
559
     * Reads exact $n bytes from buffer
560
     * @param  integer $n Number of bytes to read
561
     * @return string|false
562
     */
563 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...
564
    {
565
        if ($n === 0) {
566
            return '';
567
        }
568
        if (!$this->bev || $this->bev->input->length < $n) {
569
            return false;
570
        }
571
        return $this->read($n);
572
    }
573
574
    /**
575
     * Returns length of input buffer
576
     * @return integer
577
     */
578
    public function getInputLength()
579
    {
580
        return $this->bev->input->length;
581
    }
582
583
    /**
584
     * Called when the worker is going to shutdown
585
     * @return boolean Ready to shutdown?
586
     */
587
    public function gracefulShutdown()
588
    {
589
        $this->finish();
590
        return true;
591
    }
592
593
    /**
594
     * Freeze input
595
     * @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
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 327 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...
596
     * @return boolean           Success
597
     */
598
    public function freezeInput($at_front = true)
599
    {
600
        if (isset($this->bev)) {
601
            return $this->bev->input->freeze($at_front);
602
        }
603
        return false;
604
    }
605
606
    /**
607
     * Unfreeze input
608
     * @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
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 327 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...
609
     * @return boolean           Success
610
     */
611
    public function unfreezeInput($at_front = true)
612
    {
613
        if (isset($this->bev)) {
614
            return $this->bev->input->unfreeze($at_front);
615
        }
616
        return false;
617
    }
618
619
    /**
620
     * Freeze output
621
     * @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
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 327 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...
622
     * @return boolean           Success
623
     */
624
    public function freezeOutput($at_front = true)
625
    {
626
        if (isset($this->bev)) {
627
            return $this->bev->output->unfreeze($at_front);
628
        }
629
        return false;
630
    }
631
632
    /**
633
     * Unfreeze output
634
     * @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
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 327 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...
635
     * @return boolean           Success
636
     */
637
    public function unfreezeOutput($at_front = true)
638
    {
639
        if (isset($this->bev)) {
640
            return $this->bev->output->unfreeze($at_front);
641
        }
642
        return false;
643
    }
644
645
    /**
646
     * Called when the connection is ready to accept new data
647
     * @return void
648
     */
649
    public function onWrite()
650
    {
651
    }
652
653
    /**
654
     * Send data to the connection. Note that it just writes to buffer that flushes at every baseloop
655
     * @param  string $data Data to send
656
     * @return boolean       Success
657
     */
658 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...
659
    {
660
        if (!$this->alive) {
661
            Daemon::log('Attempt to write to dead IOStream (' . get_class($this) . ')');
662
            return false;
663
        }
664
        if (!isset($this->bev)) {
665
            return false;
666
        }
667
        if (!mb_orig_strlen($data)) {
668
            return true;
669
        }
670
        $this->writing = true;
671
        Daemon::$noError = true;
672
        if (!$this->bev->write($data) || !Daemon::$noError) {
673
            $this->close();
674
            return false;
675
        }
676
        return true;
677
    }
678
679
    /**
680
     * Send data and appending \n to connection. Note that it just writes to buffer flushed at every baseloop
681
     * @param  string $data Data to send
682
     * @return boolean       Success
683
     */
684 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...
685
    {
686
        if (!$this->alive) {
687
            Daemon::log('Attempt to write to dead IOStream (' . get_class($this) . ')');
688
            return false;
689
        }
690
        if (!isset($this->bev)) {
691
            return false;
692
        }
693
        if (!mb_orig_strlen($data) && !mb_orig_strlen($this->EOL)) {
694
            return true;
695
        }
696
        $this->writing = true;
697
        $this->bev->write($data);
698
        $this->bev->write($this->EOL);
699
        return true;
700
    }
701
702
    /**
703
     * Finish the session. You should not worry about buffers, they are going to be flushed properly
704
     * @return void
705
     */
706
    public function finish()
707
    {
708
        if ($this->finished) {
709
            return;
710
        }
711
        $this->finished = true;
712
        Daemon::$process->eventBase->stop();
713
        $this->onFinish();
714
        if (!$this->writing) {
715
            $this->close();
716
        }
717
    }
718
719
    /**
720
     * Called when the session finished
721
     * @return void
722
     */
723
    protected function onFinish()
724
    {
725
    }
726
727
    /**
728
     * Close the connection
729
     * @return void
730
     */
731
    public function close()
732
    {
733
        if (!$this->freed) {
734
            $this->freed = true;
735
            if (isset($this->bev)) {
736
                $this->bev->free();
737
            }
738
            $this->bev = null;
739
            //Daemon::$process->eventBase->stop();
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% 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...
740
        }
741
        if ($this->pool) {
742
            $this->pool->detach($this);
743
        }
744
    }
745
746
    /**
747
     * Unsets pointers of associated EventBufferEvent and File descriptr
748
     * @return void
749
     */
750
    public function unsetFd()
751
    {
752
        $this->bev = null;
753
        $this->fd = null;
754
    }
755
756
    /**
757
     * Send message to log
758
     * @param  string $m Message
759
     * @return void
760
     */
761
    protected function log($m)
762
    {
763
        Daemon::log(get_class($this) . ': ' . $m);
764
    }
765
766
    /**
767
     * Called when the connection has got new data
768
     * @param  object $bev EventBufferEvent
769
     * @return void
770
     */
771
    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...
772
    {
773
        if (Daemon::$config->logevents->value) {
774
            $this->log(' onReadEv called');
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 (\Exception $e) {
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
        if (Daemon::$config->logevents->value) {
828
            Daemon::log(get_class() . ' onWriteEv called');
829
        }
830
        $this->writing = false;
831
        if ($this->finished) {
832
            if ($this->bev->output->length === 0) {
833
                $this->close();
834
            }
835
            return;
836
        }
837
        if (!$this->ready) {
838
            $this->ready = true;
839
            while (!$this->onWriteOnce->isEmpty()) {
840
                try {
841
                    $this->onWriteOnce->executeOne($this);
842
                } catch (\Exception $e) {
843
                    Daemon::uncaughtExceptionHandler($e);
844
                }
845
                if (!$this->ready) {
846
                    return;
847
                }
848
            }
849
            $this->alive = true;
850
            try {
851
                $this->onReady();
852
                if ($this->wRead) {
853
                    $this->wRead = false;
854
                    $this->onRead();
855
                }
856
            } catch (\Exception $e) {
857
                Daemon::uncaughtExceptionHandler($e);
858
            }
859
        } else {
860
            $this->onWriteOnce->executeAll($this);
861
        }
862
        try {
863
            $this->onWrite();
864
        } catch (\Exception $e) {
865
            Daemon::uncaughtExceptionHandler($e);
866
        }
867
    }
868
869
    /**
870
     * Called when the connection state changed
871
     * @param  object $bev EventBufferEvent
872
     * @param  integer $events Events
873
     * @return void
874
     */
875
    public function onStateEv($bev, $events)
876
    {
877
        if ($events & \EventBufferEvent::CONNECTED) {
878
            $this->onWriteEv($bev);
879
        } elseif ($events & \EventBufferEvent::TIMEOUT) {
880
            $this->timedout = true;
881
            $this->finish();
882
        } elseif ($events & (\EventBufferEvent::ERROR | \EventBufferEvent::EOF)) {
883
            try {
884
                if ($this->finished) {
885
                    return;
886
                }
887
                if ($events & \EventBufferEvent::ERROR) {
888
                    $errno = \EventUtil::getLastSocketErrno();
889
                    if ($errno !== 0 && $errno !== 104) {
890
                        $this->log('Socket error #' . $errno . ':' . \EventUtil::getLastSocketError());
891
                    }
892
                    if ($this->ssl && $this->bev) {
893
                        while ($err = $this->bev->sslError()) {
894
                            $this->log('EventBufferEvent SSL error: ' . $err);
895
                        }
896
                    }
897
                }
898
                $this->finished = true;
899
                $this->onFinish();
900
                $this->close();
901
            } catch (\Exception $e) {
902
                Daemon::uncaughtExceptionHandler($e);
903
            }
904
        }
905
    }
906
907
    /**
908
     * Moves arbitrary number of bytes from input buffer to given buffer
909
     * @param  \EventBuffer $dest Destination nuffer
910
     * @param  integer $n Max. number of bytes to move
911
     * @return integer|false
912
     */
913 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...
914
    {
915
        if (!isset($this->bev)) {
916
            return false;
917
        }
918
        return $dest->appendFrom($this->bev->input, $n);
919
    }
920
921
    /**
922
     * Moves arbitrary number of bytes from given buffer to output buffer
923
     * @param  \EventBuffer $src Source buffer
924
     * @param  integer $n Max. number of bytes to move
925
     * @return integer|false
926
     */
927 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...
928
    {
929
        if (!isset($this->bev)) {
930
            return false;
931
        }
932
        $this->writing = true;
933
        return $this->bev->output->appendFrom($src, $n);
934
    }
935
936
    /**
937
     * Read data from the connection's buffer
938
     * @param  integer $n Max. number of bytes to read
939
     * @return string|false    Readed data
940
     */
941
    public function read($n)
942
    {
943
        if ($n <= 0) {
944
            return '';
945
        }
946
        if (!isset($this->bev)) {
947
            return false;
948
        }
949
        $read = $this->bev->read($n);
950
        if ($read === null) {
951
            return false;
952
        }
953
        return $read;
954
    }
955
956
    /**
957
     * Reads all data from the connection's buffer
958
     * @return string Readed data
959
     */
960 View Code Duplication
    public function readUnlimited()
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...
961
    {
962
        if (!isset($this->bev)) {
963
            return false;
964
        }
965
        $read = $this->bev->read($this->bev->input->length);
966
        if ($read === null) {
967
            return false;
968
        }
969
        return $read;
970
    }
971
}
972