Plop
A simple logging library for PHP
Socket.php
1 <?php
2 /*
3  This file is part of Plop, a simple logging library for PHP.
4 
5  Copyright © 2010-2014 François Poirotte
6 
7  Plop is free software: you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  Plop is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with Plop. If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 namespace Plop\Handler;
22 
29 {
31  protected $host;
32 
34  protected $port;
35 
37  protected $socket;
38 
40  protected $closeOnError;
41 
43  protected $retryTime;
44 
46  protected $retryStart;
47 
49  protected $retryMax;
50 
52  protected $retryFactor;
53 
55  protected $retryPeriod;
56 
68  public function __construct($host, $port)
69  {
70  parent::__construct();
71 
72  if (strpos($host, ':') !== false) {
73  // IPv6 addresses must be enclosed in brackets.
74  $host = "[$host]";
75  }
76 
77  $this->host = $host;
78  $this->port = $port;
79  $this->socket = false;
80  $this->closeOnError = false;
81  $this->retryTime = null;
82  $this->retryStart = 1.0;
83  $this->retryMax = 30.0;
84  $this->retryFactor = 2.0;
85  $this->retryPeriod = 0;
86  }
87 
89  public function __destruct()
90  {
91  $this->close();
92  }
93 
103  public function getCloseOnError()
104  {
105  return $this->closeOnError;
106  }
107 
119  public function setCloseOnError($close)
120  {
121  if (!is_bool($close)) {
122  throw new \Plop\Exception('Invalid value');
123  }
124  $this->closeOnError = $close;
125  return $this;
126  }
127 
135  public function getInitialRetryDelay()
136  {
137  return $this->retryStart;
138  }
139 
151  public function setInitialRetryDelay($delay)
152  {
153  if (!(is_int($delay) || is_float($delay)) || $delay < 0) {
154  throw new \Plop\Exception('Invalid value');
155  }
156  $this->retryStart = $delay;
157  return $this;
158  }
159 
167  public function getRetryFactor()
168  {
169  return $this->retryFactor;
170  }
171 
183  public function setRetryFactor($factor)
184  {
185  if (!(is_int($factor) || is_float($factor)) || $factor < 1) {
186  throw new \Plop\Exception('Invalid value');
187  }
188  $this->retryFactor = $factor;
189  return $this;
190  }
191 
199  public function getMaximumRetryDelay()
200  {
201  return $this->retryMax;
202  }
203 
215  public function setMaximumRetryDelay($max)
216  {
217  if (!(is_int($max) || is_float($max)) || $max < 0) {
218  throw new \Plop\Exception('Invalid value');
219  }
220  $this->retryMax = $max;
221  return $this;
222  }
223 
236  protected function makeSocket($timeout = 1)
237  {
238  return fsockopen(
239  'tcp://' . $this->host,
240  $this->port,
241  $errno,
242  $errstr,
243  $timeout
244  );
245  }
246 
255  protected function getCurrentTime()
256  {
257  return time();
258  }
259 
267  protected function createSocket()
268  {
269  $now = $this->getCurrentTime();
270  if ($this->retryTime === null) {
271  $attempt = true;
272  } else {
273  $attempt = ($now >= $this->retryTime);
274  }
275 
276  if (!$attempt) {
277  return;
278  }
279 
280  $this->socket = $this->makeSocket();
281  if ($this->socket !== false) {
282  $this->retryTime = null;
283  return;
284  }
285 
286  if ($this->retryTime === null) {
287  $this->retryPeriod = $this->retryStart;
288  } else {
289  $this->retryPeriod *= $this->retryFactor;
290  if ($this->retryPeriod > $this->retryMax) {
291  $this->retryPeriod = $this->retryMax;
292  }
293  }
294  $this->retryTime = $now + $this->retryPeriod;
295  }
296 
310  protected function send($s)
311  {
312  if (!$this->socket) {
313  $this->createSocket();
314  }
315 
316  if (!$this->socket) {
317  return false;
318  }
319 
320  $written = 0;
321  while ($s != '') {
322  $written = $this->write($s);
323  if ($written === false) {
324  throw new \Plop\Exception('Connection lost');
325  }
326  $s = (string) substr($s, $written);
327  }
328  return true;
329  }
330 
347  protected function write($s)
348  {
349  return @fwrite($this->socket, $s);
350  }
351 
363  protected function makePickle(\Plop\RecordInterface $record)
364  {
365  // To maintain full compatibility with Python,
366  // we should emulate pickle here, but it seems
367  // to be quite some work and PHP already has
368  // it's own serialization mechanism anyway.
369  $s = serialize($record);
370  $slen = pack('N', strlen($s));
371  return $slen.$s;
372  }
373 
375  public function handleError(
376  \Plop\RecordInterface $record,
377  \Exception $exception
378  ) {
379  if ($this->closeOnError) {
380  $this->close();
381  }
382  return parent::handleError($record, $exception);
383  }
384 
386  protected function emit(\Plop\RecordInterface $record)
387  {
388  try {
389  $s = $this->makePickle($record);
390  $this->send($s);
391  } catch (\Exception $e) {
392  $this->handleError($record, $e);
393  }
394  }
395 
402  protected function close()
403  {
404  if (is_resource($this->socket)) {
405  fclose($this->socket);
406  }
407  $this->socket = false;
408  }
409 }
handleError(\Plop\RecordInterface $record,\Exception $exception)
Definition: Socket.php:375
An abstract class that can be used as a base to create new log records handlers.
$socket
The socket that will be used to send the logs.
Definition: Socket.php:37
$retryMax
Maximum delay between reconnection attempts.
Definition: Socket.php:49
setCloseOnError($close)
Definition: Socket.php:119
setInitialRetryDelay($delay)
Definition: Socket.php:151
$retryPeriod
The delay that will apply to the next reconnection attempt.
Definition: Socket.php:55
Interface for a log record.
An handler that sends log messages to a remote host over a TCP socket.
Definition: Socket.php:28
setMaximumRetryDelay($max)
Definition: Socket.php:215
emit(\Plop\RecordInterface $record)
Definition: Socket.php:386
$port
Remote port where the logs will be sent.
Definition: Socket.php:34
makeSocket($timeout=1)
Definition: Socket.php:236
makePickle(\Plop\RecordInterface $record)
Definition: Socket.php:363
$retryTime
UNIX timestamp of the next connection attempt, if any.
Definition: Socket.php:43
__construct($host, $port)
Definition: Socket.php:68
$host
Remote host where the logs will be sent.
Definition: Socket.php:31
$retryStart
Initial delay for reconnection attempts.
Definition: Socket.php:46
This exception is thrown by Plop whenever a problem is detected.
Definition: Exception.php:30
setRetryFactor($factor)
Definition: Socket.php:183
__destruct()
Free the resources used by this handler.
Definition: Socket.php:89
$retryFactor
Factor applied to the reconnection delay after a reconnection failure.
Definition: Socket.php:52
$closeOnError
Whether to close the socket automatically on error.
Definition: Socket.php:40