blob: e1bd3bd68159fba48f58e69ad515747a3a362658 [file] [log] [blame]
Mark Slee6e536442006-06-30 18:28:50 +00001<?php
2
3/**
Mark Slee4902c052007-03-01 00:31:30 +00004 * Copyright (c) 2006- Facebook
5 * Distributed under the Thrift Software License
6 *
7 * See accompanying file LICENSE or visit the Thrift site at:
8 * http://developers.facebook.com/thrift/
9 *
10 * @package thrift.transport
11 * @author Mark Slee <mcslee@facebook.com>
12 */
13
14/**
Mark Slee6e536442006-06-30 18:28:50 +000015 * Sockets implementation of the TTransport interface.
16 *
17 * @package thrift.transport
18 * @author Mark Slee <mcslee@facebook.com>
19 */
20class TSocket extends TTransport {
21
22 /**
23 * Handle to PHP socket
24 *
25 * @var resource
26 */
27 private $handle_ = null;
28
29 /**
30 * Remote hostname
Mark Slee0cdc6c82007-11-13 10:19:08 +000031 *
Mark Slee6e536442006-06-30 18:28:50 +000032 * @var string
33 */
Mark Sleeade2c832006-09-08 03:41:50 +000034 protected $host_ = 'localhost';
Mark Slee6e536442006-06-30 18:28:50 +000035
36 /**
37 * Remote port
38 *
39 * @var int
40 */
Mark Sleeade2c832006-09-08 03:41:50 +000041 protected $port_ = '9090';
42
43 /**
44 * Send timeout in milliseconds
45 *
46 * @var int
47 */
48 private $sendTimeout_ = 100;
49
50 /**
51 * Recv timeout in milliseconds
52 *
53 * @var int
54 */
55 private $recvTimeout_ = 750;
56
57 /**
58 * Is send timeout set?
59 *
60 * @var bool
61 */
62 private $sendTimeoutSet_ = FALSE;
Mark Slee6e536442006-06-30 18:28:50 +000063
64 /**
65 * Persistent socket or plain?
66 *
67 * @var bool
68 */
Mark Sleeade2c832006-09-08 03:41:50 +000069 private $persist_ = FALSE;
70
71 /**
72 * Debugging on?
73 *
74 * @var bool
75 */
robertb0fac3e2007-01-15 23:53:25 +000076 protected $debug_ = FALSE;
Mark Slee6e536442006-06-30 18:28:50 +000077
78 /**
Mark Sleead58f952007-01-03 19:23:50 +000079 * Debug handler
80 *
81 * @var mixed
82 */
robertb0fac3e2007-01-15 23:53:25 +000083 protected $debugHandler_ = null;
Mark Sleead58f952007-01-03 19:23:50 +000084
85 /**
Mark Slee6e536442006-06-30 18:28:50 +000086 * Socket constructor
87 *
Mark Sleead58f952007-01-03 19:23:50 +000088 * @param string $host Remote hostname
89 * @param int $port Remote port
90 * @param bool $persist Whether to use a persistent socket
91 * @param string $debugHandler Function to call for error logging
Mark Slee6e536442006-06-30 18:28:50 +000092 */
Mark Sleead58f952007-01-03 19:23:50 +000093 public function __construct($host='localhost',
94 $port=9090,
95 $persist=FALSE,
96 $debugHandler=null) {
Mark Slee6e536442006-06-30 18:28:50 +000097 $this->host_ = $host;
98 $this->port_ = $port;
99 $this->persist_ = $persist;
Mark Sleead58f952007-01-03 19:23:50 +0000100 $this->debugHandler_ = $debugHandler ? $debugHandler : 'error_log';
Mark Slee6e536442006-06-30 18:28:50 +0000101 }
102
103 /**
Mark Sleeade2c832006-09-08 03:41:50 +0000104 * Sets the send timeout.
105 *
106 * @param int $timeout
107 */
108 public function setSendTimeout($timeout) {
109 $this->sendTimeout_ = $timeout;
110 }
111
112 /**
113 * Sets the receive timeout.
114 *
115 * @param int $timeout
116 */
117 public function setRecvTimeout($timeout) {
118 $this->recvTimeout_ = $timeout;
119 }
120
121 /**
122 * Sets debugging output on or off
123 *
124 * @param bool $debug
125 */
126 public function setDebug($debug) {
127 $this->debug_ = $debug;
128 }
129
130 /**
Mark Slee0cdc6c82007-11-13 10:19:08 +0000131 * Get the host that this socket is connected to
132 *
133 * @return string host
134 */
135 public function getHost() {
136 return $this->host_;
137 }
138
139 /**
140 * Get the remote port that this socket is connected to
141 *
142 * @return int port
143 */
144 public function getPort() {
145 return $this->port_;
146 }
147
148 /**
Mark Slee6e536442006-06-30 18:28:50 +0000149 * Tests whether this is open
150 *
151 * @return bool true if the socket is open
152 */
153 public function isOpen() {
154 return is_resource($this->handle_);
155 }
156
157 /**
158 * Connects the socket.
159 */
160 public function open() {
Mark Slee0cdc6c82007-11-13 10:19:08 +0000161
Mark Slee6e536442006-06-30 18:28:50 +0000162 if ($this->persist_) {
Mark Sleed7cc1c42006-10-04 16:49:07 +0000163 $this->handle_ = @pfsockopen($this->host_,
164 $this->port_,
165 $errno,
166 $errstr,
167 $this->sendTimeout_/1000.0);
168 } else {
169 $this->handle_ = @fsockopen($this->host_,
Mark Sleeade2c832006-09-08 03:41:50 +0000170 $this->port_,
171 $errno,
172 $errstr,
173 $this->sendTimeout_/1000.0);
Mark Slee6e536442006-06-30 18:28:50 +0000174 }
Mark Sleeade2c832006-09-08 03:41:50 +0000175
176 // Connect failed?
177 if ($this->handle_ === FALSE) {
Martin Kraemer0b64e772007-02-07 22:39:58 +0000178 $error = 'TSocket: Could not connect to '.$this->host_.':'.$this->port_.' ('.$errstr.' ['.$errno.'])';
Mark Sleeade2c832006-09-08 03:41:50 +0000179 if ($this->debug_) {
Mark Sleee7714a62007-01-11 01:26:00 +0000180 call_user_func($this->debugHandler_, $error);
Mark Sleeade2c832006-09-08 03:41:50 +0000181 }
Mark Slee76791962007-03-14 02:47:35 +0000182 throw new TException($error);
Mark Slee6e536442006-06-30 18:28:50 +0000183 }
Mark Slee0cdc6c82007-11-13 10:19:08 +0000184
Mark Sleeade2c832006-09-08 03:41:50 +0000185 stream_set_timeout($this->handle_, 0, $this->sendTimeout_*1000);
186 $this->sendTimeoutSet_ = TRUE;
Mark Slee6e536442006-06-30 18:28:50 +0000187 }
188
189 /**
Mark Sleeade2c832006-09-08 03:41:50 +0000190 * Closes the socket.
Mark Slee6e536442006-06-30 18:28:50 +0000191 */
192 public function close() {
193 if (!$this->persist_) {
Mark Sleeade2c832006-09-08 03:41:50 +0000194 @fclose($this->handle_);
195 $this->handle_ = null;
Mark Slee6e536442006-06-30 18:28:50 +0000196 }
197 }
Mark Slee0cdc6c82007-11-13 10:19:08 +0000198
Mark Slee6e536442006-06-30 18:28:50 +0000199 /**
200 * Uses stream get contents to do the reading
Mark Sleeade2c832006-09-08 03:41:50 +0000201 *
202 * @param int $len How many bytes
203 * @return string Binary data
Mark Slee6e536442006-06-30 18:28:50 +0000204 */
205 public function readAll($len) {
Mark Sleeade2c832006-09-08 03:41:50 +0000206 if ($this->sendTimeoutSet_) {
207 stream_set_timeout($this->handle_, 0, $this->recvTimeout_*1000);
208 $this->sendTimeoutSet_ = FALSE;
209 }
Mark Slee794993d2006-09-20 01:56:10 +0000210 // This call does not obey stream_set_timeout values!
211 // $buf = @stream_get_contents($this->handle_, $len);
212
213 $pre = null;
Mark Slee29f5f672006-09-28 03:19:03 +0000214 while (TRUE) {
Mark Slee794993d2006-09-20 01:56:10 +0000215 $buf = @fread($this->handle_, $len);
Mark Sleee598d072006-11-21 02:01:22 +0000216 if ($buf === FALSE || $buf === '') {
Martin Kraemer0b64e772007-02-07 22:39:58 +0000217 $md = stream_get_meta_data($this->handle_);
218 if ($md['timed_out']) {
Mark Slee76791962007-03-14 02:47:35 +0000219 throw new TException('TSocket: timed out reading '.$len.' bytes from '.
220 $this->host_.':'.$this->port_);
Martin Kraemer0b64e772007-02-07 22:39:58 +0000221 } else {
Mark Slee76791962007-03-14 02:47:35 +0000222 throw new TException('TSocket: Could not read '.$len.' bytes from '.
223 $this->host_.':'.$this->port_);
Martin Kraemer0b64e772007-02-07 22:39:58 +0000224 }
Mark Slee794993d2006-09-20 01:56:10 +0000225 } else if (($sz = strlen($buf)) < $len) {
226 $md = stream_get_meta_data($this->handle_);
227 if ($md['timed_out']) {
Mark Slee76791962007-03-14 02:47:35 +0000228 throw new TException('TSocket: timed out reading '.$len.' bytes from '.
229 $this->host_.':'.$this->port_);
Mark Slee794993d2006-09-20 01:56:10 +0000230 } else {
231 $pre .= $buf;
232 $len -= $sz;
233 }
234 } else {
235 return $pre.$buf;
236 }
Mark Sleeade2c832006-09-08 03:41:50 +0000237 }
Mark Slee6e536442006-06-30 18:28:50 +0000238 }
239
240 /**
241 * Read from the socket
Mark Sleeade2c832006-09-08 03:41:50 +0000242 *
243 * @param int $len How many bytes
244 * @return string Binary data
Mark Slee6e536442006-06-30 18:28:50 +0000245 */
246 public function read($len) {
Mark Sleeade2c832006-09-08 03:41:50 +0000247 if ($this->sendTimeoutSet_) {
248 stream_set_timeout($this->handle_, 0, $this->recvTimeout_*1000);
249 $this->sendTimeoutSet_ = FALSE;
250 }
Mark Slee794993d2006-09-20 01:56:10 +0000251 $data = @fread($this->handle_, $len);
Mark Sleee598d072006-11-21 02:01:22 +0000252 if ($data === FALSE || $data === '') {
Martin Kraemer0b64e772007-02-07 22:39:58 +0000253 $md = stream_get_meta_data($this->handle_);
254 if ($md['timed_out']) {
Mark Slee76791962007-03-14 02:47:35 +0000255 throw new TException('TSocket: timed out reading '.$len.' bytes from '.
256 $this->host_.':'.$this->port_);
Martin Kraemer0b64e772007-02-07 22:39:58 +0000257 } else {
Mark Slee76791962007-03-14 02:47:35 +0000258 throw new TException('TSocket: Could not read '.$len.' bytes from '.
259 $this->host_.':'.$this->port_);
Martin Kraemer0b64e772007-02-07 22:39:58 +0000260 }
Mark Slee6e536442006-06-30 18:28:50 +0000261 }
262 return $data;
263 }
264
265 /**
266 * Write to the socket.
Mark Sleeade2c832006-09-08 03:41:50 +0000267 *
268 * @param string $buf The data to write
Mark Slee6e536442006-06-30 18:28:50 +0000269 */
270 public function write($buf) {
Mark Sleeade2c832006-09-08 03:41:50 +0000271 if (!$this->sendTimeoutSet_) {
272 stream_set_timeout($this->handle_, 0, $this->sendTimeout_*1000);
273 $this->sendTimeoutSet_ = TRUE;
274 }
Mark Sleed395d572007-02-27 01:16:55 +0000275 while (strlen($buf) > 0) {
Mark Sleeade2c832006-09-08 03:41:50 +0000276 $got = @fwrite($this->handle_, $buf);
277 if ($got === 0 || $got === FALSE) {
Martin Kraemer0b64e772007-02-07 22:39:58 +0000278 $md = stream_get_meta_data($this->handle_);
279 if ($md['timed_out']) {
Mark Slee76791962007-03-14 02:47:35 +0000280 throw new TException('TSocket: timed out writing '.$len.' bytes from '.
281 $this->host_.':'.$this->port_);
Martin Kraemer0b64e772007-02-07 22:39:58 +0000282 } else {
Mark Slee76791962007-03-14 02:47:35 +0000283 throw new TException('TSocket: Could not write '.strlen($buf).' bytes '.
284 $this->host_.':'.$this->port_);
Martin Kraemer0b64e772007-02-07 22:39:58 +0000285 }
Mark Slee6e536442006-06-30 18:28:50 +0000286 }
287 $buf = substr($buf, $got);
288 }
289 }
290
291 /**
292 * Flush output to the socket.
293 */
294 public function flush() {
Mark Sleeade2c832006-09-08 03:41:50 +0000295 $ret = fflush($this->handle_);
296 if ($ret === FALSE) {
Mark Slee76791962007-03-14 02:47:35 +0000297 throw new TException('TSocket: Could not flush: '.
298 $this->host_.':'.$this->port_);
Mark Sleeade2c832006-09-08 03:41:50 +0000299 }
Mark Slee6e536442006-06-30 18:28:50 +0000300 }
301}
302
303?>