Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

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