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