Completed
Push — master ( 02259a...c531bb )
by Vasily
03:54
created

Connection::sendFrame()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
dl 0
loc 3
rs 10
c 3
b 0
f 0
cc 1
eloc 2
nc 1
nop 3
1
<?php
2
namespace PHPDaemon\Servers\WebSocket;
3
4
use PHPDaemon\Core\Daemon;
5
use PHPDaemon\HTTPRequest\Generic;
6
use PHPDaemon\WebSocket\Route;
7
use PHPDaemon\Request\RequestHeadersAlreadySent;
8
9
class Connection extends \PHPDaemon\Network\Connection {
10
	use \PHPDaemon\Traits\DeferredEventHandlers;
11
	use \PHPDaemon\Traits\Sessions;
12
13
	/**
14
	 * @var integer Timeout
15
	 */
16
	protected $timeout = 120;
17
18
	protected $handshaked = false;
19
	protected $route;
20
	protected $writeReady = true;
21
	protected $extensions = [];
22
	protected $extensionsCleanRegex = '/(?:^|\W)x-webkit-/iS';
23
	
24
	
25
	protected $headers = [];
26
	protected $headers_sent = false;
27
	
28
	/**
29
	 * @var array _SERVER
30
	 */
31
	public $server = [];
32
33
	/**
34
	 * @var array _COOKIE
35
	 */
36
	public $cookie = [];
37
38
	/**
39
	 * @var array _GET
40
	 */
41
	public $get = [];
42
	
43
44
	protected $policyReqNotFound = false;
45
	protected $currentHeader;
46
	protected $EOL = "\r\n";
47
48
	/**
49
	 * @var boolean Is this connection running right now?
50
	 */
51
	protected $running = false;
52
53
	/**
54
	 * State: first line
55
	 */
56
	const STATE_FIRSTLINE  = 1;
57
58
	/**
59
	 * State: headers
60
	 */
61
	const STATE_HEADERS    = 2;
62
63
	/**
64
	 * State: content
65
	 */
66
	const STATE_CONTENT    = 3;
67
68
	/**
69
	 * State: prehandshake
70
	 */
71
	const STATE_PREHANDSHAKE = 5;
72
73
	/**
74
	 * State: handshaked
75
	 */
76
	const STATE_HANDSHAKED = 6;
77
78
	const STRING = NULL;
79
80
	const BINARY = NULL;
81
82
83
	/**
84
	 * @var integer Content length from header() method
85
	 */
86
	protected $contentLength;
87
88
	/**
89
	 * @var integer Number of outgoing cookie-headers
90
	 */
91
	protected $cookieNum = 0;
92
93
	/**
94
	 * @var array Replacement pairs for processing some header values in parse_str()
95
	 */
96
	public static $hvaltr = ['; ' => '&', ';' => '&', ' ' => '%20'];
97
98
	/**
99
	 * Called when the stream is handshaked (at low-level), and peer is ready to recv. data
100
	 * @return void
101
	 */
102
	public function onReady() {
103
		$this->setWatermark(null, $this->pool->maxAllowedPacket + 100);
104
	}
105
106
	/**
107
	 * Get real frame type identificator
108
	 * @param $type
109
	 * @return integer
110
	 */
111
	public function getFrameType($type) {
112
		if (is_int($type)) {
113
			return $type;
114
		}
115
		if ($type === null) {
116
			$type = 'STRING';
117
		}
118
		$frametype = @constant(get_class($this) . '::' . $type);
119
		if ($frametype === null) {
120
			Daemon::log(__METHOD__ . ' : Undefined frametype "' . $type . '"');
121
		}
122
		return $frametype;
123
	}
124
125
126
	/**
127
	 * Called when connection is inherited from HTTP request
128
	 * @param  object $req
129
	 * @return void
130
	 */
131 View Code Duplication
	public function onInheritanceFromRequest($req) {
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...
132
		$this->state  = self::STATE_HEADERS;
133
		$this->addr   = $req->attrs->server['REMOTE_ADDR'];
134
		$this->server = $req->attrs->server;
135
		$this->get = $req->attrs->get;
136
		$this->prependInput("\r\n");
137
		$this->onRead();
138
	}
139
140
	/**
141
	 * Sends a frame.
142
	 * @param  string   $data  Frame's data.
143
	 * @param  string   $type  Frame's type. ("STRING" OR "BINARY")
0 ignored issues
show
Documentation introduced by
Should the type for parameter $type not be string|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...
144
	 * @param  callable $cb    Optional. Callback called when the frame is received by client.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
145
	 * @callback $cb ( )
146
	 * @return boolean         Success.
147
	 */
148
	public function sendFrame($data, $type = null, $cb = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $data 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...
Unused Code introduced by
The parameter $type 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...
Unused Code introduced by
The parameter $cb 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...
149
		return false;
150
	}
151
152
	/**
153
	 * Event of Connection.
154
	 * @return void
155
	 */
156
	public function onFinish() {
157
158
		$this->sendFrame('', 'CONNCLOSE');
0 ignored issues
show
Unused Code introduced by
The call to the method PHPDaemon\Servers\WebSoc...Connection::sendFrame() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
159
		
160
		if ($this->route) {
161
			$this->route->onFinish();
162
		}
163
		$this->route = null;
164
	}
165
166
	/**
167
	 * Uncaught exception handler
168
	 * @param  Exception $e
169
	 * @return boolean      Handled?
170
	 */
171
	public function handleException($e) {
172
		if (!isset($this->route)) {
173
			return false;
174
		}
175
		return $this->route->handleException($e);
176
	}
177
178
	/**
179
	 * Called when new frame received.
180
	 * @param  string $data Frame's data.
181
	 * @param  string $type Frame's type ("STRING" OR "BINARY").
182
	 * @return boolean      Success.
183
	 */
184
	public function onFrame($data, $type) {
185
		if (!isset($this->route)) {
186
			return false;
187
		}
188
		try {
189
			$this->route->onWakeup();
190
			$this->route->onFrame($data, $type);
191
		} catch (\Exception $e) {
192
			Daemon::uncaughtExceptionHandler($e);
193
		}
194
		$this->route->onSleep();
195
		return true;
196
	}
197
198
	/**
199
	 * Called when the worker is going to shutdown.
200
	 * @return boolean Ready to shutdown ?
201
	 */
202
	public function gracefulShutdown() {
203
		if ((!$this->route) || $this->route->gracefulShutdown()) {
204
			$this->finish();
205
			return true;
206
		}
207
		return FALSE;
208
	}
209
210
211
	/**
212
	 * Called when we're going to handshake.
213
	 * @return boolean               Handshake status
0 ignored issues
show
Documentation introduced by
Should the return type not be null|boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
214
	 */
215
	public function handshake() {
216
217
		$this->route = $this->pool->getRoute($this->server['DOCUMENT_URI'], $this);
218 View Code Duplication
		if (!$this->route) {
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...
219
			Daemon::$process->log(get_class($this) . '::' . __METHOD__ . ' : Cannot handshake session for client "' . $this->addr . '"');
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 128 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...
220
			$this->finish();
221
			return false;
222
		}
223
		if (method_exists($this->route, 'onBeforeHandshake')) {
224
			$this->route->onWakeup();
225
			$ret = $this->route->onBeforeHandshake(function($cb) {
0 ignored issues
show
Unused Code introduced by
The parameter $cb 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...
226 View Code Duplication
				if (!$this->sendHandshakeReply()) {
0 ignored issues
show
Bug introduced by
The method sendHandshakeReply() does not exist on PHPDaemon\Servers\WebSocket\Connection. Did you maybe mean handshake()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
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...
227
					Daemon::$process->log(get_class($this) . '::' . __METHOD__ . ' : Handshake protocol failure for client "' . $this->addr . '"');
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 132 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...
228
					$this->finish();
229
					return false;
230
				}
231
			});
232
			$this->route->onSleep();
233
			if ($ret !== false) {
234
				return;
235
			}
236
		}
237 View Code Duplication
		if (!$this->sendHandshakeReply()) {
0 ignored issues
show
Bug introduced by
The method sendHandshakeReply() does not exist on PHPDaemon\Servers\WebSocket\Connection. Did you maybe mean handshake()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
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...
238
			Daemon::$process->log(get_class($this) . '::' . __METHOD__ . ' : Handshake protocol failure for client "' . $this->addr . '"');
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 130 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...
239
			$this->finish();
240
			return false;
241
		}
242
		$this->handshaked = true;
243
		$this->headers_sent = true;
244
		$this->state = static::STATE_HANDSHAKED;
245
		if (is_callable([$this->route, 'onHandshake'])) {
246
			$this->route->onWakeup();
247
			$this->route->onHandshake();
248
			$this->route->onSleep();
249
		}
250
		return true;
251
	}
252
253
	/**
254
	 * @TODO
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
255
	 * @param  string $s Data
256
	 */
257
	public function write($s) {
258
		parent::write($s);
259
	}
260
	
261
	/**
262
	 * Send Bad request
263
	 * @return void
264
	 */
265
	public function badRequest() {
266
		$this->state = self::STATE_STANDBY;
267
		$this->write("400 Bad Request\r\n\r\n<html><head><title>400 Bad Request</title></head><body bgcolor=\"white\"><center><h1>400 Bad Request</h1></center></body></html>");
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 170 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...
268
		$this->finish();
269
	}
270
271
	/**
272
	 * Read first line of HTTP request
273
	 * @return boolean|null Success
274
	 */
275
	protected function httpReadFirstline() {
276
		if (($l = $this->readline()) === null) {
277
			return null;
278
		}
279
		$e = explode(' ', $l);
280
		$u = isset($e[1]) ? parse_url($e[1]) : false;
281
		if ($u === false) {
282
			$this->badRequest();
283
			return false;
284
		}
285
		if (!isset($u['path'])) {
286
			$u['path'] = null;
287
		}
288
		if (isset($u['host'])) {
289
			$this->server['HTTP_HOST'] = $u['host'];
290
		}
291
		$srv                       = & $this->server;
292
		$srv['REQUEST_METHOD']     = $e[0];
293
		$srv['REQUEST_TIME']       = time();
294
		$srv['REQUEST_TIME_FLOAT'] = microtime(true);
295
		$srv['REQUEST_URI']        = $u['path'] . (isset($u['query']) ? '?' . $u['query'] : '');
296
		$srv['DOCUMENT_URI']       = $u['path'];
297
		$srv['PHP_SELF']           = $u['path'];
298
		$srv['QUERY_STRING']       = isset($u['query']) ? $u['query'] : null;
299
		$srv['SCRIPT_NAME']        = $srv['DOCUMENT_URI'] = isset($u['path']) ? $u['path'] : '/';
300
		$srv['SERVER_PROTOCOL']    = isset($e[2]) ? $e[2] : 'HTTP/1.1';
301
		$srv['REMOTE_ADDR']        = $this->addr;
302
		$srv['REMOTE_PORT']        = $this->port;
303
		return true;
304
	}
305
306
	/**
307
	 * Read headers line-by-line
308
	 * @return boolean|null Success
309
	 */
310
	protected function httpReadHeaders() {
311
		while (($l = $this->readLine()) !== null) {
312
			if ($l === '') {
313
				return true;
314
			}
315
			$e = explode(': ', $l);
316
			if (isset($e[1])) {
317
				$this->currentHeader                = 'HTTP_' . strtoupper(strtr($e[0], Generic::$htr));
318
				$this->server[$this->currentHeader] = $e[1];
319
			}
320 View Code Duplication
			elseif (($e[0][0] === "\t" || $e[0][0] === "\x20") && $this->currentHeader) {
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...
321
				// multiline header continued
322
				$this->server[$this->currentHeader] .= $e[0];
323
			}
324
			else {
325
				// whatever client speaks is not HTTP anymore
326
				$this->badRequest();
327
				return false;
328
			}
329
		}
330
		return null;
331
	}
332
333
	/**
334
	 * Called when new data received.
335
	 * @return void
336
	 */
337
	protected function onRead() {
338 View Code Duplication
		if (!$this->policyReqNotFound) {
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...
339
			$d = $this->drainIfMatch("<policy-file-request/>\x00");
340
			if ($d === null) { // partially match
341
				return;
342
			}
343
			if ($d) {
344
				if (($FP = \PHPDaemon\Servers\FlashPolicy\Pool::getInstance($this->pool->config->fpsname->value, false)) && $FP->policyData) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 130 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...
345
					$this->write($FP->policyData . "\x00");
346
				}
347
				$this->finish();
348
				return;
349
			}
350
			else {
351
				$this->policyReqNotFound = true;
352
			}
353
		}
354
		start:
355
		if ($this->finished) {
356
			return;
357
		}
358
		if ($this->state === self::STATE_STANDBY) {
359
			$this->state = self::STATE_FIRSTLINE;
360
		}
361
		if ($this->state === self::STATE_FIRSTLINE) {
362
			if (!$this->httpReadFirstline()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->httpReadFirstline() of type null|boolean is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
363
				return;
364
			}
365
			$this->state = self::STATE_HEADERS;
366
		}
367
368 View Code Duplication
		if ($this->state === self::STATE_HEADERS) {
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...
369
			if (!$this->httpReadHeaders()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->httpReadHeaders() of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
370
				return;
371
			}
372
			if (!$this->httpProcessHeaders()) {
373
				$this->finish();
374
				return;
375
			}
376
			$this->state = self::STATE_CONTENT;
377
		}
378
		if ($this->state === self::STATE_CONTENT) {
379
			$this->state = self::STATE_PREHANDSHAKE;
380
		}
381
	}
382
383
	/**
384
	 * Process headers
385
	 * @return bool
386
	 */
387
	protected function httpProcessHeaders() {
388
		$this->state = self::STATE_PREHANDSHAKE;
389
		if (isset($this->server['HTTP_SEC_WEBSOCKET_EXTENSIONS'])) {
390
			$str              = strtolower($this->server['HTTP_SEC_WEBSOCKET_EXTENSIONS']);
391
			$str              = preg_replace($this->extensionsCleanRegex, '', $str);
392
			$this->extensions = explode(', ', $str);
393
		}
394
		if (!isset($this->server['HTTP_CONNECTION'])
395
				|| (!preg_match('~(?:^|\W)Upgrade(?:\W|$)~i', $this->server['HTTP_CONNECTION'])) // "Upgrade" is not always alone (ie. "Connection: Keep-alive, Upgrade")
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 157 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...
396
				|| !isset($this->server['HTTP_UPGRADE'])
397
				|| (strtolower($this->server['HTTP_UPGRADE']) !== 'websocket') // Lowercase compare important
398
		) {
399
			$this->finish();
400
			return false;
401
		}
402 View Code Duplication
		if (isset($this->server['HTTP_COOKIE'])) {
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...
403
			Generic::parse_str(strtr($this->server['HTTP_COOKIE'], Generic::$hvaltr), $this->cookie);
404
		}
405
		if (isset($this->server['QUERY_STRING'])) {
406
			Generic::parse_str($this->server['QUERY_STRING'], $this->get);
407
		}
408
		// ----------------------------------------------------------
409
		// Protocol discovery, based on HTTP headers...
410
		// ----------------------------------------------------------
411
		if (isset($this->server['HTTP_SEC_WEBSOCKET_VERSION'])) { // HYBI
412
			if ($this->server['HTTP_SEC_WEBSOCKET_VERSION'] === '8') { // Version 8 (FF7, Chrome14)
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% 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...
413
				$this->switchToProtocol('V13');
414
			}
415
			elseif ($this->server['HTTP_SEC_WEBSOCKET_VERSION'] === '13') { // newest protocol
416
				$this->switchToProtocol('V13');
417
			}
418
			else {
419
				Daemon::$process->log(get_class($this) . '::' . __METHOD__ . " : Websocket protocol version " . $this->server['HTTP_SEC_WEBSOCKET_VERSION'] . ' is not yet supported for client "' . $this->addr . '"');
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 204 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...
420
				$this->finish();
421
				return false;
422
			}
423
		}
424
		elseif (!isset($this->server['HTTP_SEC_WEBSOCKET_KEY1']) || !isset($this->server['HTTP_SEC_WEBSOCKET_KEY2'])) {
425
			$this->switchToProtocol('VE');
426
		}
427
		else { // Defaulting to HIXIE (Safari5 and many non-browser clients...)
428
			$this->switchToProtocol('V0');
429
		}
430
		// ----------------------------------------------------------
431
		// End of protocol discovery
432
		// ----------------------------------------------------------
433
		return true;
434
	}
435
436
	protected function switchToProtocol($proto) {
437
		$class = '\\PHPDaemon\\Servers\\WebSocket\\Protocols\\' . $proto;
438
		$conn  = new $class(null, $this->pool);
439
		$this->pool->attach($conn);
440
		$conn->setFd($this->getFd(), $this->getBev());
441
		$this->unsetFd();
442
		$this->pool->detach($this);
443
		$conn->onInheritance($this);
444
	}
445
446 View Code Duplication
	public function onInheritance($conn) {
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...
447
		$this->server = $conn->server;
448
		$this->cookie = $conn->cookie;
449
		$this->get = $conn->get;
450
		$this->state = self::STATE_PREHANDSHAKE;
451
		$this->addr = $conn->addr;
452
		$this->onRead();
453
	}
454
455
456
	/**
457
	 * Send HTTP-status
458
	 * @throws RequestHeadersAlreadySent
459
	 * @param  integer $code Code
460
	 * @return boolean       Success
461
	 */
462
	public function status($code = 200) {
0 ignored issues
show
Unused Code introduced by
The parameter $code 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...
463
		return false;
464
	}
465
466
	/**
467
	 * Send the header
468
	 * @param  string  $s       Header. Example: 'Location: http://php.net/'
469
	 * @param  boolean $replace Optional. Replace?
470
	 * @param  boolean $code    Optional. HTTP response code
471
	 * @throws \PHPDaemon\Request\RequestHeadersAlreadySent
472
	 * @return boolean          Success
473
	 */
474 View Code Duplication
	public function header($s, $replace = true, $code = false) {
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...
475
		if ($code) {
476
			$this->status($code);
0 ignored issues
show
Documentation introduced by
$code is of type boolean, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Unused Code introduced by
The call to the method PHPDaemon\Servers\WebSocket\Connection::status() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
477
		}
478
479
		if ($this->headers_sent) {
480
			throw new RequestHeadersAlreadySent;
481
		}
482
		$s = strtr($s, "\r\n", '  ');
483
484
		$e = explode(':', $s, 2);
485
486
		if (!isset($e[1])) {
487
			$e[0] = 'STATUS';
488
489
			if (strncmp($s, 'HTTP/', 5) === 0) {
490
				$s = substr($s, 9);
491
			}
492
		}
493
494
		$k = strtr(strtoupper($e[0]), Generic::$htr);
495
496
		if ($k === 'CONTENT_TYPE') {
497
			self::parse_str(strtolower($e[1]), $ctype, true);
0 ignored issues
show
Bug introduced by
The variable $ctype seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
498
			if (!isset($ctype['charset'])) {
0 ignored issues
show
Bug introduced by
The variable $ctype seems only to be defined at a later point. As such the call to isset() seems to always evaluate to false.

This check marks calls to isset(...) or empty(...) that are found before the variable itself is defined. These will always have the same result.

This is likely the result of code being shifted around. Consider removing these calls.

Loading history...
499
				$ctype['charset'] = $this->upstream->pool->config->defaultcharset->value;
0 ignored issues
show
Documentation introduced by
The property upstream does not exist on object<PHPDaemon\Servers\WebSocket\Connection>. Since you implemented __get, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
Coding Style Comprehensibility introduced by
$ctype was never initialized. Although not strictly required by PHP, it is generally a good practice to add $ctype = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
500
501
				$s = $e[0] . ': ';
502
				$i = 0;
503
				foreach ($ctype as $k => $v) {
504
					$s .= ($i > 0 ? '; ' : '') . $k . ($v !== '' ? '=' . $v : '');
505
					++$i;
506
				}
507
			}
508
		}
509
		if ($k === 'SET_COOKIE') {
510
			$k .= '_' . ++$this->cookieNum;
511
		}
512
		elseif (!$replace && isset($this->headers[$k])) {
513
			return false;
514
		}
515
516
		$this->headers[$k] = $s;
517
518
		if ($k === 'CONTENT_LENGTH') {
519
			$this->contentLength = (int)$e[1];
520
		}
521
		elseif ($k === 'LOCATION') {
522
			$this->status(301);
0 ignored issues
show
Unused Code introduced by
The call to the method PHPDaemon\Servers\WebSocket\Connection::status() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
523
		}
524
525
		if (Daemon::$compatMode) {
526
			is_callable('header_native') ? header_native($s) : header($s);
527
		}
528
529
		return true;
530
	}
531
}
532