/blog/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php

https://gitlab.com/zan_zan/laravel_sample · PHP · 284 lines · 159 code · 29 blank · 96 comment · 19 complexity · ad245f0583e47beac39e08813f5e7522 MD5 · raw file

  1. <?php
  2. /*
  3. * This file is part of the Monolog package.
  4. *
  5. * (c) Jordi Boggiano <j.boggiano@seld.be>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Monolog\Handler;
  11. use Monolog\Logger;
  12. /**
  13. * Stores to any socket - uses fsockopen() or pfsockopen().
  14. *
  15. * @author Pablo de Leon Belloc <pablolb@gmail.com>
  16. * @see http://php.net/manual/en/function.fsockopen.php
  17. */
  18. class SocketHandler extends AbstractProcessingHandler
  19. {
  20. private $connectionString;
  21. private $connectionTimeout;
  22. private $resource;
  23. private $timeout = 0;
  24. private $persistent = false;
  25. private $errno;
  26. private $errstr;
  27. /**
  28. * @param string $connectionString Socket connection string
  29. * @param integer $level The minimum logging level at which this handler will be triggered
  30. * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
  31. */
  32. public function __construct($connectionString, $level = Logger::DEBUG, $bubble = true)
  33. {
  34. parent::__construct($level, $bubble);
  35. $this->connectionString = $connectionString;
  36. $this->connectionTimeout = (float) ini_get('default_socket_timeout');
  37. }
  38. /**
  39. * Connect (if necessary) and write to the socket
  40. *
  41. * @param array $record
  42. *
  43. * @throws \UnexpectedValueException
  44. * @throws \RuntimeException
  45. */
  46. protected function write(array $record)
  47. {
  48. $this->connectIfNotConnected();
  49. $data = $this->generateDataStream($record);
  50. $this->writeToSocket($data);
  51. }
  52. /**
  53. * We will not close a PersistentSocket instance so it can be reused in other requests.
  54. */
  55. public function close()
  56. {
  57. if (!$this->isPersistent()) {
  58. $this->closeSocket();
  59. }
  60. }
  61. /**
  62. * Close socket, if open
  63. */
  64. public function closeSocket()
  65. {
  66. if (is_resource($this->resource)) {
  67. fclose($this->resource);
  68. $this->resource = null;
  69. }
  70. }
  71. /**
  72. * Set socket connection to nbe persistent. It only has effect before the connection is initiated.
  73. *
  74. * @param type $boolean
  75. */
  76. public function setPersistent($boolean)
  77. {
  78. $this->persistent = (boolean) $boolean;
  79. }
  80. /**
  81. * Set connection timeout. Only has effect before we connect.
  82. *
  83. * @param float $seconds
  84. *
  85. * @see http://php.net/manual/en/function.fsockopen.php
  86. */
  87. public function setConnectionTimeout($seconds)
  88. {
  89. $this->validateTimeout($seconds);
  90. $this->connectionTimeout = (float) $seconds;
  91. }
  92. /**
  93. * Set write timeout. Only has effect before we connect.
  94. *
  95. * @param float $seconds
  96. *
  97. * @see http://php.net/manual/en/function.stream-set-timeout.php
  98. */
  99. public function setTimeout($seconds)
  100. {
  101. $this->validateTimeout($seconds);
  102. $this->timeout = (float) $seconds;
  103. }
  104. /**
  105. * Get current connection string
  106. *
  107. * @return string
  108. */
  109. public function getConnectionString()
  110. {
  111. return $this->connectionString;
  112. }
  113. /**
  114. * Get persistent setting
  115. *
  116. * @return boolean
  117. */
  118. public function isPersistent()
  119. {
  120. return $this->persistent;
  121. }
  122. /**
  123. * Get current connection timeout setting
  124. *
  125. * @return float
  126. */
  127. public function getConnectionTimeout()
  128. {
  129. return $this->connectionTimeout;
  130. }
  131. /**
  132. * Get current in-transfer timeout
  133. *
  134. * @return float
  135. */
  136. public function getTimeout()
  137. {
  138. return $this->timeout;
  139. }
  140. /**
  141. * Check to see if the socket is currently available.
  142. *
  143. * UDP might appear to be connected but might fail when writing. See http://php.net/fsockopen for details.
  144. *
  145. * @return boolean
  146. */
  147. public function isConnected()
  148. {
  149. return is_resource($this->resource)
  150. && !feof($this->resource); // on TCP - other party can close connection.
  151. }
  152. /**
  153. * Wrapper to allow mocking
  154. */
  155. protected function pfsockopen()
  156. {
  157. return @pfsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout);
  158. }
  159. /**
  160. * Wrapper to allow mocking
  161. */
  162. protected function fsockopen()
  163. {
  164. return @fsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout);
  165. }
  166. /**
  167. * Wrapper to allow mocking
  168. *
  169. * @see http://php.net/manual/en/function.stream-set-timeout.php
  170. */
  171. protected function streamSetTimeout()
  172. {
  173. $seconds = floor($this->timeout);
  174. $microseconds = round(($this->timeout - $seconds)*1e6);
  175. return stream_set_timeout($this->resource, $seconds, $microseconds);
  176. }
  177. /**
  178. * Wrapper to allow mocking
  179. */
  180. protected function fwrite($data)
  181. {
  182. return @fwrite($this->resource, $data);
  183. }
  184. /**
  185. * Wrapper to allow mocking
  186. */
  187. protected function streamGetMetadata()
  188. {
  189. return stream_get_meta_data($this->resource);
  190. }
  191. private function validateTimeout($value)
  192. {
  193. $ok = filter_var($value, FILTER_VALIDATE_FLOAT);
  194. if ($ok === false || $value < 0) {
  195. throw new \InvalidArgumentException("Timeout must be 0 or a positive float (got $value)");
  196. }
  197. }
  198. private function connectIfNotConnected()
  199. {
  200. if ($this->isConnected()) {
  201. return;
  202. }
  203. $this->connect();
  204. }
  205. protected function generateDataStream($record)
  206. {
  207. return (string) $record['formatted'];
  208. }
  209. private function connect()
  210. {
  211. $this->createSocketResource();
  212. $this->setSocketTimeout();
  213. }
  214. private function createSocketResource()
  215. {
  216. if ($this->isPersistent()) {
  217. $resource = $this->pfsockopen();
  218. } else {
  219. $resource = $this->fsockopen();
  220. }
  221. if (!$resource) {
  222. throw new \UnexpectedValueException("Failed connecting to $this->connectionString ($this->errno: $this->errstr)");
  223. }
  224. $this->resource = $resource;
  225. }
  226. private function setSocketTimeout()
  227. {
  228. if (!$this->streamSetTimeout()) {
  229. throw new \UnexpectedValueException("Failed setting timeout with stream_set_timeout()");
  230. }
  231. }
  232. private function writeToSocket($data)
  233. {
  234. $length = strlen($data);
  235. $sent = 0;
  236. while ($this->isConnected() && $sent < $length) {
  237. if (0 == $sent) {
  238. $chunk = $this->fwrite($data);
  239. } else {
  240. $chunk = $this->fwrite(substr($data, $sent));
  241. }
  242. if ($chunk === false) {
  243. throw new \RuntimeException("Could not write to socket");
  244. }
  245. $sent += $chunk;
  246. $socketInfo = $this->streamGetMetadata();
  247. if ($socketInfo['timed_out']) {
  248. throw new \RuntimeException("Write timed-out");
  249. }
  250. }
  251. if (!$this->isConnected() && $sent < $length) {
  252. throw new \RuntimeException("End-of-file reached, probably we got disconnected (sent $sent of $length)");
  253. }
  254. }
  255. }