Completed
Push — EventLoopContainer ( c9a84d )
by Vasily
04:35
created

IOStream::onReadEv()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 4
dl 0
loc 15
rs 9.2
c 2
b 1
f 0
eloc 10
nc 4
nop 1
1
<?php
2
namespace PHPDaemon\Network;
3
4
use PHPDaemon\Core\Daemon;
5
use PHPDaemon\Structures\StackCallbacks;
6
use PHPDaemon\Traits\EventLoopContainer;
7
8
/**
9
 * IOStream
10
 * @package PHPDaemon\Network
11
 * @author  Vasily Zorin <[email protected]>
12
 */
13
abstract class IOStream
14
{
15
    use \PHPDaemon\Traits\ClassWatchdog;
16
    use \PHPDaemon\Traits\StaticObjectWatchdog;
17
    use \PHPDaemon\Traits\EventHandlers;
18
    use EventLoopContainer;
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->eventLoop = $pool->eventLoop;
175
            var_dump(['iostream pool' => md5(spl_object_hash($pool)), 'eventLoop' => $this->eventLoop]);
0 ignored issues
show
Security Debugging Code introduced by
var_dump(array('iostream... => $this->eventLoop)); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
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
        $this->fd = $fd;
279
        if ($this->fd === false) {
280
            $this->finish();
281
            return;
282
        }
283
        if ($bev !== null) {
284
            $this->bev = $bev;
285
            $this->bev->setCallbacks([$this, 'onReadEv'], [$this, 'onWriteEv'], [$this, 'onStateEv']);
286
            if (!$this->bev) {
287
                return;
288
            }
289
            $this->ready = true;
290
            $this->alive = true;
291
        } else {
292
            $flags = !is_resource($this->fd) ? \EventBufferEvent::OPT_CLOSE_ON_FREE : 0;
293
            $flags |= \EventBufferEvent::OPT_DEFER_CALLBACKS; /* buggy option */
294
            if ($this->ctx) {
295
                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...
296
                    $this->bev = $this->eventLoop->bufferEventSsl(
297
                        $this->fd,
298
                        $this->ctx,
299
                        $this->ctxMode,
300
                        $flags
301
                    );
302
303 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...
304
                        $this->bev->setCallbacks([$this, 'onReadEv'], [$this, 'onWriteEv'], [$this, 'onStateEv']);
305
                    }
306
                    $this->ssl = true;
307
                } else {
308
                    $this->log('unsupported type of context: ' . ($this->ctx ? get_class($this->ctx) : 'undefined'));
309
                    return;
310
                }
311 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...
312
                $this->bev = $this->eventLoop->bufferEvent(
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
     * Constructor
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
    public function lookExact($n, $o = 0)
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
        $this->eventLoop->interrupt();
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
            //$this->eventLoop->interrupt();
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% 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 (!$this->ready) {
774
            $this->wRead = true;
775
            return;
776
        }
777
        if ($this->finished) {
778
            return;
779
        }
780
        try {
781
            $this->onRead();
782
        } catch (\Exception $e) {
783
            Daemon::uncaughtExceptionHandler($e);
784
        }
785
    }
786
787
    /**
788
     * Called when new data received
789
     * @return void
790
     */
791
    protected function onRead()
792
    {
793
    }
794
795
    /**
796
     * Called when the stream is handshaked (at low-level), and peer is ready to recv. data
797
     * @return void
798
     */
799
    protected function onReady()
800
    {
801
    }
802
803
    /**
804
     * Push callback which will be called only once, when writing is available next time
805
     * @param  callable $cb Callback
806
     * @return void
807
     */
808
    public function onWriteOnce($cb)
809
    {
810
        if (!$this->writing) {
811
            $cb($this);
812
            return;
813
        }
814
        $this->onWriteOnce->push($cb);
815
    }
816
817
    /**
818
     * Called when the connection is ready to accept new data
819
     * @param  object $bev EventBufferEvent
820
     * @return void
821
     */
822
    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...
823
    {
824
        $this->writing = false;
825
        if ($this->finished) {
826
            if ($this->bev->output->length === 0) {
827
                $this->close();
828
            }
829
            return;
830
        }
831
        if (!$this->ready) {
832
            $this->ready = true;
833
            while (!$this->onWriteOnce->isEmpty()) {
834
                try {
835
                    $this->onWriteOnce->executeOne($this);
836
                } catch (\Exception $e) {
837
                    Daemon::uncaughtExceptionHandler($e);
838
                }
839
                if (!$this->ready) {
840
                    return;
841
                }
842
            }
843
            $this->alive = true;
844
            try {
845
                $this->onReady();
846
                if ($this->wRead) {
847
                    $this->wRead = false;
848
                    $this->onRead();
849
                }
850
            } catch (\Exception $e) {
851
                Daemon::uncaughtExceptionHandler($e);
852
            }
853
        } else {
854
            $this->onWriteOnce->executeAll($this);
855
        }
856
        try {
857
            $this->onWrite();
858
        } catch (\Exception $e) {
859
            Daemon::uncaughtExceptionHandler($e);
860
        }
861
    }
862
863
    /**
864
     * Called when the connection state changed
865
     * @param  object $bev EventBufferEvent
866
     * @param  integer $events Events
867
     * @return void
868
     */
869
    public function onStateEv($bev, $events)
870
    {
871
        if ($events & \EventBufferEvent::CONNECTED) {
872
            $this->onWriteEv($bev);
873
        } elseif ($events & \EventBufferEvent::TIMEOUT) {
874
            $this->timedout = true;
875
            $this->finish();
876
        } elseif ($events & (\EventBufferEvent::ERROR | \EventBufferEvent::EOF)) {
877
            try {
878
                if ($this->finished) {
879
                    return;
880
                }
881
                if ($events & \EventBufferEvent::ERROR) {
882
                    $errno = \EventUtil::getLastSocketErrno();
883
                    if ($errno !== 0 && $errno !== 104) {
884
                        $this->log('Socket error #' . $errno . ':' . \EventUtil::getLastSocketError());
885
                    }
886
                    if ($this->ssl && $this->bev) {
887
                        while ($err = $this->bev->sslError()) {
888
                            $this->log('EventBufferEvent SSL error: ' . $err);
889
                        }
890
                    }
891
                }
892
                $this->finished = true;
893
                $this->onFinish();
894
                $this->close();
895
            } catch (\Exception $e) {
896
                Daemon::uncaughtExceptionHandler($e);
897
            }
898
        }
899
    }
900
901
    /**
902
     * Moves arbitrary number of bytes from input buffer to given buffer
903
     * @param  \EventBuffer $dest Destination nuffer
904
     * @param  integer $n Max. number of bytes to move
905
     * @return integer|false
906
     */
907 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...
908
    {
909
        if (!isset($this->bev)) {
910
            return false;
911
        }
912
        return $dest->appendFrom($this->bev->input, $n);
913
    }
914
915
    /**
916
     * Moves arbitrary number of bytes from given buffer to output buffer
917
     * @param  \EventBuffer $src Source buffer
918
     * @param  integer $n Max. number of bytes to move
919
     * @return integer|false
920
     */
921 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...
922
    {
923
        if (!isset($this->bev)) {
924
            return false;
925
        }
926
        $this->writing = true;
927
        return $this->bev->output->appendFrom($src, $n);
928
    }
929
930
    /**
931
     * Read data from the connection's buffer
932
     * @param  integer $n Max. number of bytes to read
933
     * @return string|false    Readed data
934
     */
935
    public function read($n)
936
    {
937
        if ($n <= 0) {
938
            return '';
939
        }
940
        if (!isset($this->bev)) {
941
            return false;
942
        }
943
        $read = $this->bev->read($n);
944
        if ($read === null) {
945
            return false;
946
        }
947
        return $read;
948
    }
949
950
    /**
951
     * Reads all data from the connection's buffer
952
     * @return string Readed data
953
     */
954
    public function readUnlimited()
955
    {
956
        if (!isset($this->bev)) {
957
            return false;
958
        }
959
        $read = $this->bev->read($this->bev->input->length);
960
        if ($read === null) {
961
            return false;
962
        }
963
        return $read;
964
    }
965
}
966