Completed
Push — master ( 8561f8...0f383b )
by Vasily
03:40
created

IOStream::write()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 19
Code Lines 14

Duplication

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