1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Helix\Socket; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Full duplex connection. |
7
|
|
|
*/ |
8
|
|
|
class StreamClient extends AbstractClient { |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
* `SOCK_STREAM` |
12
|
|
|
* |
13
|
|
|
* @return int |
14
|
|
|
*/ |
15
|
|
|
final public static function getType (): int { |
16
|
|
|
return SOCK_STREAM; |
17
|
|
|
} |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* Creates a pair of interconnected Unix instances that can be used for IPC. |
21
|
|
|
* |
22
|
|
|
* @see https://php.net/socket_create_pair |
23
|
|
|
* |
24
|
|
|
* @param array $extra Variadic constructor arguments. |
25
|
|
|
* @return static[] Two instances at indices `0` and `1`. |
26
|
|
|
* @throws SocketError |
27
|
|
|
*/ |
28
|
|
|
public static function newUnixPair (...$extra) { |
29
|
|
|
if (!@socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $fd)) { |
30
|
|
|
throw new SocketError; // reliable errno |
31
|
|
|
} |
32
|
|
|
return [ |
33
|
|
|
new static($fd[0], ...$extra), |
|
|
|
|
34
|
|
|
new static($fd[1], ...$extra) |
|
|
|
|
35
|
|
|
]; |
36
|
|
|
} |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* Reads the specified length from the peer (forced blocking). |
40
|
|
|
* |
41
|
|
|
* May return less than the desired length if the peer shut down writing or closed. |
42
|
|
|
* |
43
|
|
|
* @param int $length |
44
|
|
|
* @return string |
45
|
|
|
* @throws SocketError The partially read data is attached. |
46
|
|
|
*/ |
47
|
|
|
public function read (int $length): string { |
48
|
|
|
try { |
49
|
|
|
$data = ''; |
50
|
|
|
do { |
51
|
|
|
$data .= $chunk = $this->awaitReadable()->recv($length); |
52
|
|
|
$length -= $chunkSize = strlen($chunk); |
53
|
|
|
} while ($chunkSize and $length); |
54
|
|
|
return $data; |
55
|
|
|
} |
56
|
|
|
catch (SocketError $e) { |
57
|
|
|
$e->setExtra($data); |
58
|
|
|
throw $e; |
59
|
|
|
} |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Receives up to a specified length from the peer. |
64
|
|
|
* |
65
|
|
|
* Returns an empty string if the peer shut down writing or closed. |
66
|
|
|
* |
67
|
|
|
* @see https://php.net/socket_recv |
68
|
|
|
* |
69
|
|
|
* @param int $maxLength |
70
|
|
|
* @param int $flags `MSG_*` |
71
|
|
|
* @return string |
72
|
|
|
* @throws SocketError |
73
|
|
|
*/ |
74
|
|
|
public function recv (int $maxLength, int $flags = 0): string { |
75
|
|
|
if (false === @socket_recv($this->resource, $data, $maxLength, $flags)) { |
76
|
|
|
throw new SocketError($this->resource, SOCKET_EINVAL); |
77
|
|
|
} |
78
|
|
|
return (string)$data; // cast needed, will be null if 0 bytes are read. |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* All available data in the system buffer without blocking. |
83
|
|
|
* |
84
|
|
|
* @param int $flags `MSG_*` |
85
|
|
|
* @return string |
86
|
|
|
* @throws SocketError |
87
|
|
|
*/ |
88
|
|
|
public function recvAll (int $flags = 0): string { |
89
|
|
|
$flags = ($flags & ~MSG_WAITALL) | MSG_DONTWAIT; |
90
|
|
|
$length = $this->getOption(SO_RCVBUF); |
91
|
|
|
try { |
92
|
|
|
return $this->recv($length, $flags); |
93
|
|
|
} |
94
|
|
|
catch (SocketError $e) { |
95
|
|
|
if ($e->getCode() === SOCKET_EAGAIN) { // would block |
96
|
|
|
return ''; |
97
|
|
|
} |
98
|
|
|
throw $e; |
99
|
|
|
} |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
} |
103
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.