ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
SocketHandler.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the Monolog package.
5  *
6  * (c) Jordi Boggiano <j.boggiano@seld.be>
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 Monolog\Handler;
13 
14 use Monolog\Logger;
15 
23 {
26  private $resource;
27  private $timeout = 0;
28  private $writingTimeout = 10;
29  private $lastSentBytes = null;
30  private $chunkSize = null;
31  private $persistent = false;
32  private $errno;
33  private $errstr;
34  private $lastWritingAt;
35 
42  {
43  parent::__construct($level, $bubble);
44  $this->connectionString = $connectionString;
45  $this->connectionTimeout = (float) ini_get('default_socket_timeout');
46  }
47 
56  protected function write(array $record)
57  {
58  $this->connectIfNotConnected();
59  $data = $this->generateDataStream($record);
60  $this->writeToSocket($data);
61  }
62 
66  public function close()
67  {
68  if (!$this->isPersistent()) {
69  $this->closeSocket();
70  }
71  }
72 
76  public function closeSocket()
77  {
78  if (is_resource($this->resource)) {
79  fclose($this->resource);
80  $this->resource = null;
81  }
82  }
83 
89  public function setPersistent($persistent)
90  {
91  $this->persistent = (bool) $persistent;
92  }
93 
101  public function setConnectionTimeout($seconds)
102  {
103  $this->validateTimeout($seconds);
104  $this->connectionTimeout = (float) $seconds;
105  }
106 
114  public function setTimeout($seconds)
115  {
116  $this->validateTimeout($seconds);
117  $this->timeout = (float) $seconds;
118  }
119 
125  public function setWritingTimeout($seconds)
126  {
127  $this->validateTimeout($seconds);
128  $this->writingTimeout = (float) $seconds;
129  }
130 
136  public function setChunkSize($bytes)
137  {
138  $this->chunkSize = $bytes;
139  }
140 
146  public function getConnectionString()
147  {
149  }
150 
156  public function isPersistent()
157  {
158  return $this->persistent;
159  }
160 
166  public function getConnectionTimeout()
167  {
169  }
170 
176  public function getTimeout()
177  {
178  return $this->timeout;
179  }
180 
186  public function getWritingTimeout()
187  {
188  return $this->writingTimeout;
189  }
190 
196  public function getChunkSize()
197  {
198  return $this->chunkSize;
199  }
200 
208  public function isConnected()
209  {
210  return is_resource($this->resource)
211  && !feof($this->resource); // on TCP - other party can close connection.
212  }
213 
217  protected function pfsockopen()
218  {
219  return @pfsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout);
220  }
221 
225  protected function fsockopen()
226  {
227  return @fsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout);
228  }
229 
235  protected function streamSetTimeout()
236  {
237  $seconds = floor($this->timeout);
238  $microseconds = round(($this->timeout - $seconds) * 1e6);
239 
240  return stream_set_timeout($this->resource, $seconds, $microseconds);
241  }
242 
248  protected function streamSetChunkSize()
249  {
250  return stream_set_chunk_size($this->resource, $this->chunkSize);
251  }
252 
256  protected function fwrite($data)
257  {
258  return @fwrite($this->resource, $data);
259  }
260 
264  protected function streamGetMetadata()
265  {
266  return stream_get_meta_data($this->resource);
267  }
268 
269  private function validateTimeout($value)
270  {
271  $ok = filter_var($value, FILTER_VALIDATE_FLOAT);
272  if ($ok === false || $value < 0) {
273  throw new \InvalidArgumentException("Timeout must be 0 or a positive float (got $value)");
274  }
275  }
276 
277  private function connectIfNotConnected()
278  {
279  if ($this->isConnected()) {
280  return;
281  }
282  $this->connect();
283  }
284 
285  protected function generateDataStream($record)
286  {
287  return (string) $record['formatted'];
288  }
289 
293  protected function getResource()
294  {
295  return $this->resource;
296  }
297 
298  private function connect()
299  {
300  $this->createSocketResource();
301  $this->setSocketTimeout();
302  $this->setStreamChunkSize();
303  }
304 
305  private function createSocketResource()
306  {
307  if ($this->isPersistent()) {
308  $resource = $this->pfsockopen();
309  } else {
310  $resource = $this->fsockopen();
311  }
312  if (!$resource) {
313  throw new \UnexpectedValueException("Failed connecting to $this->connectionString ($this->errno: $this->errstr)");
314  }
315  $this->resource = $resource;
316  }
317 
318  private function setSocketTimeout()
319  {
320  if (!$this->streamSetTimeout()) {
321  throw new \UnexpectedValueException("Failed setting timeout with stream_set_timeout()");
322  }
323  }
324 
325  private function setStreamChunkSize()
326  {
327  if ($this->chunkSize && !$this->streamSetChunkSize()) {
328  throw new \UnexpectedValueException("Failed setting chunk size with stream_set_chunk_size()");
329  }
330  }
331 
332  private function writeToSocket($data)
333  {
334  $length = strlen($data);
335  $sent = 0;
336  $this->lastSentBytes = $sent;
337  while ($this->isConnected() && $sent < $length) {
338  if (0 == $sent) {
339  $chunk = $this->fwrite($data);
340  } else {
341  $chunk = $this->fwrite(substr($data, $sent));
342  }
343  if ($chunk === false) {
344  throw new \RuntimeException("Could not write to socket");
345  }
346  $sent += $chunk;
347  $socketInfo = $this->streamGetMetadata();
348  if ($socketInfo['timed_out']) {
349  throw new \RuntimeException("Write timed-out");
350  }
351 
352  if ($this->writingIsTimedOut($sent)) {
353  throw new \RuntimeException("Write timed-out, no data sent for `{$this->writingTimeout}` seconds, probably we got disconnected (sent $sent of $length)");
354  }
355  }
356  if (!$this->isConnected() && $sent < $length) {
357  throw new \RuntimeException("End-of-file reached, probably we got disconnected (sent $sent of $length)");
358  }
359  }
360 
361  private function writingIsTimedOut($sent)
362  {
363  $writingTimeout = (int) floor($this->writingTimeout);
364  if (0 === $writingTimeout) {
365  return false;
366  }
367 
368  if ($sent !== $this->lastSentBytes) {
369  $this->lastWritingAt = time();
370  $this->lastSentBytes = $sent;
371 
372  return false;
373  } else {
374  usleep(100);
375  }
376 
377  if ((time() - $this->lastWritingAt) >= $writingTimeout) {
378  $this->closeSocket();
379 
380  return true;
381  }
382 
383  return false;
384  }
385 }
getChunkSize()
Get current chunk size.
getConnectionString()
Get current connection string.
const DEBUG
Detailed debug information.
Definition: Logger.php:33
setTimeout($seconds)
Set write timeout.
Stores to any socket - uses fsockopen() or pfsockopen().
setWritingTimeout($seconds)
Set writing timeout.
Base Handler class providing the Handler structure.
__construct($connectionString, $level=Logger::DEBUG, $bubble=true)
fwrite($data)
Wrapper to allow mocking.
isConnected()
Check to see if the socket is currently available.
write(array $record)
Connect (if necessary) and write to the socket.
fsockopen()
Wrapper to allow mocking.
close()
We will not close a PersistentSocket instance so it can be reused in other requests.
streamSetTimeout()
Wrapper to allow mocking.
isPersistent()
Get persistent setting.
setConnectionTimeout($seconds)
Set connection timeout.
pfsockopen()
Wrapper to allow mocking.
getWritingTimeout()
Get current local writing timeout.
setChunkSize($bytes)
Set chunk size.
getConnectionTimeout()
Get current connection timeout setting.
setPersistent($persistent)
Set socket connection to nbe persistent.
getTimeout()
Get current in-transfer timeout.
closeSocket()
Close socket, if open.
streamSetChunkSize()
Wrapper to allow mocking.
streamGetMetadata()
Wrapper to allow mocking.
$data
Definition: bench.php:6