1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the php-gelf package. |
5
|
|
|
* |
6
|
|
|
* (c) Benjamin Zikarsky <http://benjamin-zikarsky.de> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Gelf\Transport; |
13
|
|
|
|
14
|
|
|
use RuntimeException; |
15
|
|
|
use ParagonIE\ConstantTime\Binary; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* StreamSocketClient is a very simple OO-Wrapper around the PHP |
19
|
|
|
* stream_socket-library and some specific stream-functions like |
20
|
|
|
* fwrite, etc. |
21
|
|
|
* |
22
|
|
|
* @author Benjamin Zikarsky <[email protected]> |
23
|
|
|
*/ |
24
|
|
|
class StreamSocketClient |
25
|
|
|
{ |
26
|
|
|
/** |
27
|
|
|
* @deprecated deprecated since v1.4.0 |
28
|
|
|
*/ |
29
|
|
|
const SOCKET_TIMEOUT = 30; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @var string |
33
|
|
|
*/ |
34
|
|
|
protected $host; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var integer |
38
|
|
|
*/ |
39
|
|
|
protected $port; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @var string |
43
|
|
|
*/ |
44
|
|
|
protected $scheme; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @var array |
48
|
|
|
*/ |
49
|
|
|
protected $context; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @var resource |
53
|
|
|
*/ |
54
|
|
|
protected $socket; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @var int |
58
|
|
|
*/ |
59
|
|
|
protected $connectTimeout = self::SOCKET_TIMEOUT; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* @param string $scheme |
63
|
|
|
* @param string $host |
64
|
|
|
* @param integer $port |
65
|
|
|
* @param array $context |
66
|
|
|
*/ |
67
|
45 |
|
public function __construct($scheme, $host, $port, array $context = array()) |
68
|
|
|
{ |
69
|
45 |
|
$this->scheme = $scheme; |
70
|
45 |
|
$this->host = $host; |
71
|
45 |
|
$this->port = $port; |
72
|
45 |
|
$this->context = $context; |
73
|
45 |
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Destructor, closes socket if possible |
77
|
|
|
*/ |
78
|
33 |
|
public function __destruct() |
79
|
|
|
{ |
80
|
33 |
|
$this->close(); |
81
|
33 |
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Initializes socket-client |
85
|
|
|
* |
86
|
|
|
* @deprecated deprecated since v1.4.0 |
87
|
|
|
* |
88
|
|
|
* @param string $scheme like "udp" or "tcp" |
89
|
|
|
* @param string $host |
90
|
|
|
* @param integer $port |
91
|
|
|
* @param array $context |
92
|
|
|
* |
93
|
|
|
* @return resource |
94
|
|
|
* |
95
|
|
|
* @throws RuntimeException on connection-failure |
96
|
|
|
*/ |
97
|
|
|
protected static function initSocket($scheme, $host, $port, array $context) |
98
|
|
|
{ |
99
|
|
|
$socketDescriptor = sprintf("%s://%s:%d", $scheme, $host, $port); |
100
|
|
|
$socket = @stream_socket_client( |
101
|
|
|
$socketDescriptor, |
102
|
|
|
$errNo, |
103
|
|
|
$errStr, |
104
|
|
|
static::SOCKET_TIMEOUT, |
|
|
|
|
105
|
|
|
\STREAM_CLIENT_CONNECT, |
106
|
|
|
stream_context_create($context) |
107
|
|
|
); |
108
|
|
|
|
109
|
|
|
if ($socket === false) { |
110
|
|
|
throw new RuntimeException( |
111
|
|
|
sprintf( |
112
|
|
|
"Failed to create socket-client for %s: %s (%s)", |
113
|
|
|
$socketDescriptor, |
114
|
|
|
$errStr, |
115
|
|
|
$errNo |
116
|
|
|
) |
117
|
|
|
); |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
// set non-blocking for UDP |
121
|
|
|
if (strcasecmp("udp", $scheme) == 0) { |
122
|
|
|
stream_set_blocking($socket, 0); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
return $socket; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Internal function mimicking the behaviour of static::initSocket |
131
|
|
|
* which will get new functionality instead of the deprecated |
132
|
|
|
* "factory" |
133
|
|
|
* |
134
|
|
|
* @return resource |
135
|
|
|
* |
136
|
|
|
* @throws RuntimeException on connection-failure |
137
|
|
|
*/ |
138
|
15 |
|
private function buildSocket() |
139
|
|
|
{ |
140
|
15 |
|
$socketDescriptor = sprintf( |
141
|
15 |
|
"%s://%s:%d", |
142
|
15 |
|
$this->scheme, |
143
|
15 |
|
$this->host, |
144
|
15 |
|
$this->port |
145
|
|
|
); |
146
|
|
|
|
147
|
15 |
|
$socket = @stream_socket_client( |
148
|
15 |
|
$socketDescriptor, |
149
|
|
|
$errNo, |
150
|
|
|
$errStr, |
151
|
15 |
|
$this->connectTimeout, |
152
|
15 |
|
\STREAM_CLIENT_CONNECT, |
153
|
15 |
|
stream_context_create($this->context) |
154
|
|
|
); |
155
|
|
|
|
156
|
15 |
|
if ($socket === false) { |
157
|
1 |
|
throw new RuntimeException( |
158
|
|
|
sprintf( |
159
|
1 |
|
"Failed to create socket-client for %s: %s (%s)", |
160
|
|
|
$socketDescriptor, |
161
|
|
|
$errStr, |
162
|
|
|
$errNo |
163
|
|
|
) |
164
|
|
|
); |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
// set non-blocking for UDP |
168
|
14 |
|
if (strcasecmp("udp", $this->scheme) == 0) { |
169
|
2 |
|
stream_set_blocking($socket, 0); |
170
|
|
|
} |
171
|
|
|
|
172
|
14 |
|
return $socket; |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* Returns raw-socket-resource |
177
|
|
|
* |
178
|
|
|
* @return resource |
179
|
|
|
*/ |
180
|
15 |
|
public function getSocket() |
181
|
|
|
{ |
182
|
|
|
// lazy initializing of socket-descriptor |
183
|
15 |
|
if (!$this->socket) { |
184
|
15 |
|
$this->socket = $this->buildSocket(); |
185
|
|
|
} |
186
|
|
|
|
187
|
14 |
|
return $this->socket; |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* Writes a given string to the socket and returns the |
192
|
|
|
* number of written bytes |
193
|
|
|
* |
194
|
|
|
* @param string $buffer |
195
|
|
|
* |
196
|
|
|
* @return int |
197
|
|
|
* |
198
|
|
|
* @throws RuntimeException on write-failure |
199
|
|
|
*/ |
200
|
8 |
|
public function write($buffer) |
201
|
|
|
{ |
202
|
8 |
|
$buffer = (string) $buffer; |
203
|
8 |
|
$bufLen = Binary::safeStrlen($buffer); |
204
|
|
|
|
205
|
8 |
|
$socket = $this->getSocket(); |
206
|
8 |
|
$written = 0; |
207
|
|
|
|
208
|
8 |
|
while ($written < $bufLen) { |
209
|
|
|
// PHP's fwrite does not behave nice in regards to errors, so we wrap |
210
|
|
|
// it with a temporary error handler and treat every warning/notice as |
211
|
|
|
// a error |
212
|
8 |
|
$failed = false; |
213
|
8 |
|
$errorMessage = "Failed to write to socket"; |
214
|
|
|
set_error_handler(function ($errno, $errstr) use (&$failed, &$errorMessage) { |
215
|
1 |
|
$failed = true; |
216
|
1 |
|
$errorMessage .= ": $errstr ($errno)"; |
217
|
8 |
|
}); |
218
|
8 |
|
$byteCount = fwrite($socket, Binary::safeSubstr($buffer, $written)); |
219
|
8 |
|
restore_error_handler(); |
220
|
|
|
|
221
|
8 |
|
if ($byteCount === 0 && defined('HHVM_VERSION')) { |
222
|
|
|
$failed = true; |
223
|
|
|
} |
224
|
|
|
|
225
|
8 |
|
if ($failed || $byteCount === false) { |
226
|
1 |
|
throw new \RuntimeException($errorMessage); |
227
|
|
|
} |
228
|
|
|
|
229
|
8 |
|
$written += $byteCount; |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
|
233
|
8 |
|
return $written; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* Reads a given number of bytes from the socket |
238
|
|
|
* |
239
|
|
|
* @param integer $byteCount |
240
|
|
|
* |
241
|
|
|
* @return string |
242
|
|
|
*/ |
243
|
2 |
|
public function read($byteCount) |
244
|
|
|
{ |
245
|
2 |
|
return fread($this->getSocket(), $byteCount); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* Closes underlying socket explicitly |
250
|
|
|
*/ |
251
|
33 |
|
public function close() |
252
|
|
|
{ |
253
|
33 |
|
if (!is_resource($this->socket)) { |
254
|
20 |
|
return; |
255
|
|
|
} |
256
|
|
|
|
257
|
14 |
|
fclose($this->socket); |
258
|
14 |
|
$this->socket = null; |
259
|
14 |
|
} |
260
|
|
|
|
261
|
|
|
/** |
262
|
|
|
* Checks if the socket is closed |
263
|
|
|
* |
264
|
|
|
* @return bool |
265
|
|
|
*/ |
266
|
6 |
|
public function isClosed() |
267
|
|
|
{ |
268
|
6 |
|
return $this->socket === null; |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
/** |
272
|
|
|
* Returns the current connect-timeout |
273
|
|
|
* |
274
|
|
|
* @return int |
275
|
|
|
*/ |
276
|
1 |
|
public function getConnectTimeout() |
277
|
|
|
{ |
278
|
1 |
|
return $this->connectTimeout; |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* Sets the connect-timeout |
283
|
|
|
* |
284
|
|
|
* @param int $timeout |
285
|
|
|
*/ |
286
|
2 |
|
public function setConnectTimeout($timeout) |
287
|
|
|
{ |
288
|
2 |
|
if (!$this->isClosed()) { |
289
|
1 |
|
throw new \LogicException("Cannot change socket properties with an open connection"); |
290
|
|
|
} |
291
|
|
|
|
292
|
1 |
|
$this->connectTimeout = $timeout; |
293
|
1 |
|
} |
294
|
|
|
|
295
|
|
|
/** |
296
|
|
|
* Returns the stream context |
297
|
|
|
* |
298
|
|
|
* @return array |
299
|
|
|
*/ |
300
|
2 |
|
public function getContext() |
301
|
|
|
{ |
302
|
2 |
|
return $this->context; |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
/** |
306
|
|
|
* Sets the stream context |
307
|
|
|
* |
308
|
|
|
* @param array $context |
309
|
|
|
*/ |
310
|
2 |
|
public function setContext(array $context) |
311
|
|
|
{ |
312
|
2 |
|
if (!$this->isClosed()) { |
313
|
1 |
|
throw new \LogicException("Cannot change socket properties with an open connection"); |
314
|
|
|
} |
315
|
|
|
|
316
|
1 |
|
$this->context = $context; |
317
|
1 |
|
} |
318
|
|
|
} |
319
|
|
|
|
This class constant has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.