PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/NetSocket.php

https://github.com/jszobody/talker.php
PHP | 597 lines | 278 code | 61 blank | 258 comment | 63 complexity | 9889f58c28c1195b751e0e5829e596f2 MD5 | raw file
  1. <?php
  2. /*
  3. * This class has been modified from PEAR's Net_Socket class (http://pear.php.net/Net_Socket)
  4. * to remove the dependency on PEAR. The changes mostly involved changing all occurrences
  5. * of "return $this->raiseError(...)" to * "throw new Exception(...)". Now this is a
  6. * standalone socket class!
  7. */
  8. //
  9. // +----------------------------------------------------------------------+
  10. // | PHP Version 4 |
  11. // +----------------------------------------------------------------------+
  12. // | Copyright (c) 1997-2003 The PHP Group |
  13. // +----------------------------------------------------------------------+
  14. // | This source file is subject to version 2.0 of the PHP license, |
  15. // | that is bundled with this package in the file LICENSE, and is |
  16. // | available at through the world-wide-web at |
  17. // | http://www.php.net/license/2_02.txt. |
  18. // | If you did not receive a copy of the PHP license and are unable to |
  19. // | obtain it through the world-wide-web, please send a note to |
  20. // | license@php.net so we can mail you a copy immediately. |
  21. // +----------------------------------------------------------------------+
  22. // | Authors: Stig Bakken <ssb@php.net> |
  23. // | Chuck Hagenbuch <chuck@horde.org> |
  24. // +----------------------------------------------------------------------+
  25. //
  26. // $Id: Socket.php,v 1.38 2008/02/15 18:24:17 chagenbu Exp $
  27. define('NET_SOCKET_READ', 1);
  28. define('NET_SOCKET_WRITE', 2);
  29. define('NET_SOCKET_ERROR', 4);
  30. /**
  31. * Generalized Socket class.
  32. *
  33. * @version 1.1
  34. * @author Stig Bakken <ssb@php.net>
  35. * @author Chuck Hagenbuch <chuck@horde.org>
  36. */
  37. class NetSocket {
  38. /**
  39. * Socket file pointer.
  40. * @var resource $fp
  41. */
  42. var $fp = null;
  43. /**
  44. * Whether the socket is blocking. Defaults to true.
  45. * @var boolean $blocking
  46. */
  47. var $blocking = true;
  48. /**
  49. * Whether the socket is persistent. Defaults to false.
  50. * @var boolean $persistent
  51. */
  52. var $persistent = false;
  53. /**
  54. * The IP address to connect to.
  55. * @var string $addr
  56. */
  57. var $addr = '';
  58. /**
  59. * The port number to connect to.
  60. * @var integer $port
  61. */
  62. var $port = 0;
  63. /**
  64. * Number of seconds to wait on socket connections before assuming
  65. * there's no more data. Defaults to no timeout.
  66. * @var integer $timeout
  67. */
  68. var $timeout = false;
  69. /**
  70. * Number of bytes to read at a time in readLine() and
  71. * readAll(). Defaults to 2048.
  72. * @var integer $lineLength
  73. */
  74. var $lineLength = 2048;
  75. /**
  76. * Connect to the specified port. If called when the socket is
  77. * already connected, it disconnects and connects again.
  78. *
  79. * @param string $addr IP address or host name.
  80. * @param integer $port TCP port number.
  81. * @param boolean $persistent (optional) Whether the connection is
  82. * persistent (kept open between requests
  83. * by the web server).
  84. * @param integer $timeout (optional) How long to wait for data.
  85. * @param array $options See options for stream_context_create.
  86. *
  87. * @access public
  88. *
  89. * @return boolean | PEAR_Error True on success or a PEAR_Error on failure.
  90. */
  91. function connect($addr, $port = 0, $persistent = null, $timeout = null, $options = null)
  92. {
  93. if (is_resource($this->fp)) {
  94. @fclose($this->fp);
  95. $this->fp = null;
  96. }
  97. if (!$addr) {
  98. throw new Exception('$addr cannot be empty');
  99. } elseif (strspn($addr, '.0123456789') == strlen($addr) ||
  100. strstr($addr, '/') !== false) {
  101. $this->addr = $addr;
  102. } else {
  103. $this->addr = @gethostbyname($addr);
  104. }
  105. $this->port = $port % 65536;
  106. if ($persistent !== null) {
  107. $this->persistent = $persistent;
  108. }
  109. if ($timeout !== null) {
  110. $this->timeout = $timeout;
  111. }
  112. $openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen';
  113. $errno = 0;
  114. $errstr = '';
  115. $old_track_errors = @ini_set('track_errors', 1);
  116. if ($options && function_exists('stream_context_create')) {
  117. if ($this->timeout) {
  118. $timeout = $this->timeout;
  119. } else {
  120. $timeout = 0;
  121. }
  122. $context = stream_context_create($options);
  123. // Since PHP 5 fsockopen doesn't allow context specification
  124. if (function_exists('stream_socket_client')) {
  125. $flags = $this->persistent ? STREAM_CLIENT_PERSISTENT : STREAM_CLIENT_CONNECT;
  126. $addr = $this->addr . ':' . $this->port;
  127. $fp = stream_socket_client($addr, $errno, $errstr, $timeout, $flags, $context);
  128. } else {
  129. $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $timeout, $context);
  130. }
  131. } else {
  132. if ($this->timeout) {
  133. $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $this->timeout);
  134. } else {
  135. $fp = @$openfunc($this->addr, $this->port, $errno, $errstr);
  136. }
  137. }
  138. if (!$fp) {
  139. if ($errno == 0 && isset($php_errormsg)) {
  140. $errstr = $php_errormsg;
  141. }
  142. @ini_set('track_errors', $old_track_errors);
  143. throw new Exception($errstr, $errno);
  144. }
  145. @ini_set('track_errors', $old_track_errors);
  146. $this->fp = $fp;
  147. return $this->setBlocking($this->blocking);
  148. }
  149. /**
  150. * Disconnects from the peer, closes the socket.
  151. *
  152. * @access public
  153. * @return mixed true on success or a PEAR_Error instance otherwise
  154. */
  155. function disconnect()
  156. {
  157. if (!is_resource($this->fp)) {
  158. throw new Exception('not connected');
  159. }
  160. @fclose($this->fp);
  161. $this->fp = null;
  162. return true;
  163. }
  164. /**
  165. * Find out if the socket is in blocking mode.
  166. *
  167. * @access public
  168. * @return boolean The current blocking mode.
  169. */
  170. function isBlocking()
  171. {
  172. return $this->blocking;
  173. }
  174. /**
  175. * Sets whether the socket connection should be blocking or
  176. * not. A read call to a non-blocking socket will return immediately
  177. * if there is no data available, whereas it will block until there
  178. * is data for blocking sockets.
  179. *
  180. * @param boolean $mode True for blocking sockets, false for nonblocking.
  181. * @access public
  182. * @return mixed true on success or a PEAR_Error instance otherwise
  183. */
  184. function setBlocking($mode)
  185. {
  186. if (!is_resource($this->fp)) {
  187. throw new Exception('not connected');
  188. }
  189. $this->blocking = $mode;
  190. socket_set_blocking($this->fp, $this->blocking);
  191. return true;
  192. }
  193. /**
  194. * Sets the timeout value on socket descriptor,
  195. * expressed in the sum of seconds and microseconds
  196. *
  197. * @param integer $seconds Seconds.
  198. * @param integer $microseconds Microseconds.
  199. * @access public
  200. * @return mixed true on success or a PEAR_Error instance otherwise
  201. */
  202. function setTimeout($seconds, $microseconds)
  203. {
  204. if (!is_resource($this->fp)) {
  205. throw new Exception('not connected');
  206. }
  207. return socket_set_timeout($this->fp, $seconds, $microseconds);
  208. }
  209. /**
  210. * Sets the file buffering size on the stream.
  211. * See php's stream_set_write_buffer for more information.
  212. *
  213. * @param integer $size Write buffer size.
  214. * @access public
  215. * @return mixed on success or an PEAR_Error object otherwise
  216. */
  217. function setWriteBuffer($size)
  218. {
  219. if (!is_resource($this->fp)) {
  220. throw new Exception('not connected');
  221. }
  222. $returned = stream_set_write_buffer($this->fp, $size);
  223. if ($returned == 0) {
  224. return true;
  225. }
  226. throw new Exception('Cannot set write buffer.');
  227. }
  228. /**
  229. * Returns information about an existing socket resource.
  230. * Currently returns four entries in the result array:
  231. *
  232. * <p>
  233. * timed_out (bool) - The socket timed out waiting for data<br>
  234. * blocked (bool) - The socket was blocked<br>
  235. * eof (bool) - Indicates EOF event<br>
  236. * unread_bytes (int) - Number of bytes left in the socket buffer<br>
  237. * </p>
  238. *
  239. * @access public
  240. * @return mixed Array containing information about existing socket resource or a PEAR_Error instance otherwise
  241. */
  242. function getStatus()
  243. {
  244. if (!is_resource($this->fp)) {
  245. throw new Exception('not connected');
  246. }
  247. return socket_get_status($this->fp);
  248. }
  249. /**
  250. * Get a specified line of data
  251. *
  252. * @access public
  253. * @return $size bytes of data from the socket, or a PEAR_Error if
  254. * not connected.
  255. */
  256. function gets($size)
  257. {
  258. if (!is_resource($this->fp)) {
  259. throw new Exception('not connected');
  260. }
  261. return @fgets($this->fp, $size);
  262. }
  263. /**
  264. * Read a specified amount of data. This is guaranteed to return,
  265. * and has the added benefit of getting everything in one fread()
  266. * chunk; if you know the size of the data you're getting
  267. * beforehand, this is definitely the way to go.
  268. *
  269. * @param integer $size The number of bytes to read from the socket.
  270. * @access public
  271. * @return $size bytes of data from the socket, or a PEAR_Error if
  272. * not connected.
  273. */
  274. function read($size)
  275. {
  276. if (!is_resource($this->fp)) {
  277. throw new Exception('not connected');
  278. }
  279. return @fread($this->fp, $size);
  280. }
  281. /**
  282. * Write a specified amount of data.
  283. *
  284. * @param string $data Data to write.
  285. * @param integer $blocksize Amount of data to write at once.
  286. * NULL means all at once.
  287. *
  288. * @access public
  289. * @return mixed If the socket is not connected, returns an instance of PEAR_Error
  290. * If the write succeeds, returns the number of bytes written
  291. * If the write fails, returns false.
  292. */
  293. function write($data, $blocksize = null)
  294. {
  295. if (!is_resource($this->fp)) {
  296. throw new Exception('not connected');
  297. }
  298. if (is_null($blocksize) && !OS_WINDOWS) {
  299. return @fwrite($this->fp, $data);
  300. } else {
  301. if (is_null($blocksize)) {
  302. $blocksize = 1024;
  303. }
  304. $pos = 0;
  305. $size = strlen($data);
  306. while ($pos < $size) {
  307. $written = @fwrite($this->fp, substr($data, $pos, $blocksize));
  308. if ($written === false) {
  309. return false;
  310. }
  311. $pos += $written;
  312. }
  313. return $pos;
  314. }
  315. }
  316. /**
  317. * Write a line of data to the socket, followed by a trailing "\r\n".
  318. *
  319. * @access public
  320. * @return mixed fputs result, or an error
  321. */
  322. function writeLine($data)
  323. {
  324. if (!is_resource($this->fp)) {
  325. throw new Exception('not connected');
  326. }
  327. return fwrite($this->fp, $data . "\r\n");
  328. }
  329. /**
  330. * Tests for end-of-file on a socket descriptor.
  331. *
  332. * Also returns true if the socket is disconnected.
  333. *
  334. * @access public
  335. * @return bool
  336. */
  337. function eof()
  338. {
  339. return (!is_resource($this->fp) || feof($this->fp));
  340. }
  341. /**
  342. * Reads a byte of data
  343. *
  344. * @access public
  345. * @return 1 byte of data from the socket, or a PEAR_Error if
  346. * not connected.
  347. */
  348. function readByte()
  349. {
  350. if (!is_resource($this->fp)) {
  351. throw new Exception('not connected');
  352. }
  353. return ord(@fread($this->fp, 1));
  354. }
  355. /**
  356. * Reads a word of data
  357. *
  358. * @access public
  359. * @return 1 word of data from the socket, or a PEAR_Error if
  360. * not connected.
  361. */
  362. function readWord()
  363. {
  364. if (!is_resource($this->fp)) {
  365. throw new Exception('not connected');
  366. }
  367. $buf = @fread($this->fp, 2);
  368. return (ord($buf[0]) + (ord($buf[1]) << 8));
  369. }
  370. /**
  371. * Reads an int of data
  372. *
  373. * @access public
  374. * @return integer 1 int of data from the socket, or a PEAR_Error if
  375. * not connected.
  376. */
  377. function readInt()
  378. {
  379. if (!is_resource($this->fp)) {
  380. throw new Exception('not connected');
  381. }
  382. $buf = @fread($this->fp, 4);
  383. return (ord($buf[0]) + (ord($buf[1]) << 8) +
  384. (ord($buf[2]) << 16) + (ord($buf[3]) << 24));
  385. }
  386. /**
  387. * Reads a zero-terminated string of data
  388. *
  389. * @access public
  390. * @return string, or a PEAR_Error if
  391. * not connected.
  392. */
  393. function readString()
  394. {
  395. if (!is_resource($this->fp)) {
  396. throw new Exception('not connected');
  397. }
  398. $string = '';
  399. while (($char = @fread($this->fp, 1)) != "\x00") {
  400. $string .= $char;
  401. }
  402. return $string;
  403. }
  404. /**
  405. * Reads an IP Address and returns it in a dot formatted string
  406. *
  407. * @access public
  408. * @return Dot formatted string, or a PEAR_Error if
  409. * not connected.
  410. */
  411. function readIPAddress()
  412. {
  413. if (!is_resource($this->fp)) {
  414. throw new Exception('not connected');
  415. }
  416. $buf = @fread($this->fp, 4);
  417. return sprintf('%d.%d.%d.%d', ord($buf[0]), ord($buf[1]),
  418. ord($buf[2]), ord($buf[3]));
  419. }
  420. /**
  421. * Read until either the end of the socket or a newline, whichever
  422. * comes first. Strips the trailing newline from the returned data.
  423. *
  424. * @access public
  425. * @return All available data up to a newline, without that
  426. * newline, or until the end of the socket, or a PEAR_Error if
  427. * not connected.
  428. */
  429. function readLine()
  430. {
  431. if (!is_resource($this->fp)) {
  432. throw new Exception('not connected');
  433. }
  434. $line = '';
  435. $timeout = time() + $this->timeout;
  436. while (!feof($this->fp) && (!$this->timeout || time() < $timeout)) {
  437. $line .= @fgets($this->fp, $this->lineLength);
  438. if (substr($line, -1) == "\n") {
  439. return rtrim($line, "\r\n");
  440. }
  441. }
  442. return $line;
  443. }
  444. /**
  445. * Read until the socket closes, or until there is no more data in
  446. * the inner PHP buffer. If the inner buffer is empty, in blocking
  447. * mode we wait for at least 1 byte of data. Therefore, in
  448. * blocking mode, if there is no data at all to be read, this
  449. * function will never exit (unless the socket is closed on the
  450. * remote end).
  451. *
  452. * @access public
  453. *
  454. * @return string All data until the socket closes, or a PEAR_Error if
  455. * not connected.
  456. */
  457. function readAll()
  458. {
  459. if (!is_resource($this->fp)) {
  460. throw new Exception('not connected');
  461. }
  462. $data = '';
  463. while (!feof($this->fp)) {
  464. $data .= @fread($this->fp, $this->lineLength);
  465. }
  466. return $data;
  467. }
  468. /**
  469. * Runs the equivalent of the select() system call on the socket
  470. * with a timeout specified by tv_sec and tv_usec.
  471. *
  472. * @param integer $state Which of read/write/error to check for.
  473. * @param integer $tv_sec Number of seconds for timeout.
  474. * @param integer $tv_usec Number of microseconds for timeout.
  475. *
  476. * @access public
  477. * @return False if select fails, integer describing which of read/write/error
  478. * are ready, or PEAR_Error if not connected.
  479. */
  480. function select($state, $tv_sec, $tv_usec = 0)
  481. {
  482. if (!is_resource($this->fp)) {
  483. throw new Exception('not connected');
  484. }
  485. $read = null;
  486. $write = null;
  487. $except = null;
  488. if ($state & NET_SOCKET_READ) {
  489. $read[] = $this->fp;
  490. }
  491. if ($state & NET_SOCKET_WRITE) {
  492. $write[] = $this->fp;
  493. }
  494. if ($state & NET_SOCKET_ERROR) {
  495. $except[] = $this->fp;
  496. }
  497. if (false === ($sr = stream_select($read, $write, $except, $tv_sec, $tv_usec))) {
  498. return false;
  499. }
  500. $result = 0;
  501. if (count($read)) {
  502. $result |= NET_SOCKET_READ;
  503. }
  504. if (count($write)) {
  505. $result |= NET_SOCKET_WRITE;
  506. }
  507. if (count($except)) {
  508. $result |= NET_SOCKET_ERROR;
  509. }
  510. return $result;
  511. }
  512. /**
  513. * Turns encryption on/off on a connected socket.
  514. *
  515. * @param bool $enabled Set this parameter to true to enable encryption
  516. * and false to disable encryption.
  517. * @param integer $type Type of encryption. See
  518. * http://se.php.net/manual/en/function.stream-socket-enable-crypto.php for values.
  519. *
  520. * @access public
  521. * @return false on error, true on success and 0 if there isn't enough data and the
  522. * user should try again (non-blocking sockets only). A PEAR_Error object
  523. * is returned if the socket is not connected
  524. */
  525. function enableCrypto($enabled, $type)
  526. {
  527. if (version_compare(phpversion(), "5.1.0", ">=")) {
  528. if (!is_resource($this->fp)) {
  529. throw new Exception('not connected');
  530. }
  531. return @stream_socket_enable_crypto($this->fp, $enabled, $type);
  532. } else {
  533. throw new Exception('Net_Socket::enableCrypto() requires php version >= 5.1.0');
  534. }
  535. }
  536. }