GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 64c21d...65951c )
by Navarr
11:16 queued 09:25
created

Socket::__destruct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
ccs 0
cts 4
cp 0
rs 9.4285
cc 1
eloc 3
nc 1
nop 0
crap 2
1
<?php
2
3
namespace Navarr\Socket;
4
5
use Navarr\Socket\Exception\SocketException;
6
7
/**
8
 * Class Socket.
9
 *
10
 * <p>A simple wrapper for PHP's socket functions.</p>
11
 */
12
class Socket
13
{
14
    /**
15
     * @var resource Will store a reference to the php socket object.
16
     */
17
    protected $resource = null;
18
    /**
19
     * @var int Should be set to one of the php predefined constants for Sockets - AF_UNIX, AF_INET, or AF_INET6
20
     */
21
    protected $domain = null;
22
    /**
23
     * @var int Should be set to one of the php predefined constants for Sockets - SOCK_STREAM, SOCK_DGRAM,
24
     *          SOCK_SEQPACKET, SOCK_RAW, SOCK_RDM
25
     */
26
    protected $type = null;
27
    /**
28
     * @var int Should be set to the protocol number to be used. Can use getprotobyname to get the value.
29
     *          Alternatively, there are two predefined constants for Sockets that could be used - SOL_TCP, SOL_UDP
30
     */
31
    protected $protocol = null;
32
    /**
33
     * @var array An internal storage of php socket resources and their associated Socket object.
34
     */
35
    protected static $map = [];
36
37
    /**
38
     * Sets up the Socket Resource and stores it in the local map.
39
     *
40
     * <p>This class uses the <a href="https://en.wikipedia.org/wiki/Factory_(object-oriented_programming)">
41
     * Factory pattern</a> to create instances. Please use the <code>create</code> method to create new instances
42
     * of this class.
43
     *
44
     * @see Socket::create()
45
     *
46
     * @param resource $resource The php socket resource. This is just a reference to the socket object created using
47
     *                           the <code>socket_create</code> method.
48
     */
49 5
    protected function __construct($resource)
50
    {
51 5
        $this->resource = $resource;
52 5
        self::$map[(string) $resource] = $this;
53 5
    }
54
55
    /**
56
     * Cleans up the Socket and dereferences the internal resource.
57
     */
58
    public function __destruct()
59
    {
60
        $this->close();
61
        $this->resource = null;
62
    }
63
64
    /**
65
     * Return the php socket resource name.
66
     *
67
     * <p>Resources are always converted to strings with the structure "Resource id#1", where 1 is the resource number
68
     * assigned to the resource by PHP at runtime. While the exact structure of this string should not be relied on and
69
     * is subject to change, it will always be unique for a given resource within the lifetime of the script execution
70
     * and won't be reused.</p>
71
     *
72
     * <p>If the resource object has been dereferrenced (set to <code>null</code>), this will return an empty
73
     * string.</p>
74
     *
75
     * @return string The string representation of the resource or an empty string if the resource was null.
76
     */
77
    public function __toString()
78
    {
79
        return (string) $this->resource;
80
    }
81
82
    /**
83
     * Accept a connection.
84
     *
85
     * <p>After the socket socket has been created using <code>create()</code>, bound to a name with
86
     * <code>bind()</code>, and told to listen for connections with <code>listen()</code>, this function will accept
87
     * incoming connections on that socket. Once a successful connection is made, a new Socket resource is returned,
88
     * which may be used for communication. If there are multiple connections queued on the socket, the first will be
89
     * used. If there are no pending connections, this will block until a connection becomes present. If socket has
90
     * been made non-blocking using <code>setBlocking()</code>, a <code>SocketException</code> will be thrown.</p>
91
     *
92
     * <p>The Socket returned by this method may not be used to accept new connections. The original listening Socket,
93
     * however, remains open and may be reused.</p>
94
     *
95
     * @throws Exception\SocketException If the Socket is set as non-blocking and there are no pending connections.
96
     *
97
     * @see Socket::create()
98
     * @see Socket::bind()
99
     * @see Socket::listen()
100
     * @see Socket::setBlocking()
101
     *
102
     * @return Socket A new Socket representation of the accepted socket.
103
     */
104
    public function accept()
105
    {
106
        $return = @socket_accept($this->resource);
107
108
        if ($return === false) {
109
            throw new SocketException($this->resource);
110
        }
111
112
        return new self($return);
113
    }
114
115
    /**
116
     * Binds a name to a socket.
117
     *
118
     * <p>Binds the name given in address to the php socket resource currently in use. This has to be done before a
119
     * connection is established using <code>connect()</code> or <code>listen()</code>.</p>
120
     *
121
     * @param string $address <p>If the socket is of the AF_INET family, the address is an IP in dotted-quad
122
     *                        notation (e.g. <code>127.0.0.1</code>).</p> <p>If the socket is of the AF_UNIX family, the address is the path
123
     *                        of the Unix-domain socket (e.g. <code>/tmp/my.sock</code>).</p>
124
     * @param int    $port    <p>(Optional) The port parameter is only used when binding an AF_INET socket, and designates the port
125
     *                        on which to listen for connections.</p>
126
     *
127
     * @throws Exception\SocketException If the bind was unsuccessful.
128
     *
129
     * @return bool <p>Returns <code>true</code> if the bind was successful.</p>
130
     */
131 1 View Code Duplication
    public function bind($address, $port = 0)
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
132
    {
133 1
        return static::exceptionOnFalse(
134
            $this->resource,
135 1
            function ($resource) use ($address, $port) {
136
                return @socket_bind($resource, $address, $port);
137
            }
138
        );
139 1
    }
140
141
    /**
142
     * Close the socket.
143
     *
144
     * <p>Closes the php socket resource currently in use and removes the reference to it in the internal map.</p>
145
     *
146
     * @return void
147
     */
148
    public function close()
149
    {
150
        unset(self::$map[(string) $this->resource]);
151
        @socket_close($this->resource);
152
    }
153
154
    /**
155
     * Connect to a socket.
156
     *
157
     * <p>Initiate a connection to the address given using the current php socket resource, which must be a valid
158
     * socket resource created with <code>create()</code>.
159
     *
160
     * @param string $address <p>The address parameter is either an IPv4 address in dotted-quad notation (e.g.
161
     *                        <code>127.0.0.1</code>) if the socket is AF_INET, a valid IPv6 address (e.g. <code>::1</code>) if IPv6 support
162
     *                        is enabled and the socket is AF_INET6, or the pathname of a Unix domain socket, if the socket family is AF_UNIX.
163
     *                        </p>
164
     * @param int    $port    <p>(Optional) The port parameter is only used and is mandatory when connecting to an AF_INET or
165
     *                        an AF_INET6 socket, and designates the port on the remote host to which a connection should be made.</p>
166
     *
167
     * @throws Exception\SocketException If the connect was unsuccessful or if the socket is non-blocking.
168
     *
169
     * @see Socket::bind()
170
     * @see Socket::listen()
171
     * @see Socket::create()
172
     *
173
     * @return bool <p>Returns <code>true</code> if the connect was successful.
174
     */
175 View Code Duplication
    public function connect($address, $port = 0)
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
176
    {
177
        return static::exceptionOnFalse(
178
            $this->resource,
179
            function ($resource) use ($address, $port) {
180
                return @socket_connect($resource, $address, $port);
181
            }
182
        );
183
    }
184
185
    /**
186
     * Build Socket objects based on an array of php socket resources.
187
     *
188
     * @param array $resources The resources parameter is a list of php socket resource objects.
189
     *
190
     * @return Socket[] <p>Returns an array of Socket objects built from the given php socket resources.</p>
191
     */
192
    protected static function constructFromResources(array $resources)
193
    {
194 2
        return array_map(function ($resource) {
195
            return new self($resource);
196 2
        }, $resources);
197 2
    }
198 2
199
    /**
200
     * Create a socket.
201
     *
202
     * <p>Creates and returns a Socket. A typical network connection is made up of two sockets, one performing the role
203
     * of the client, and another performing the role of the server.</p>
204
     *
205
     * @param int $domain   <p>The domain parameter specifies the protocol family to be used by the socket.</p><p>
206
     *                      <code>AF_INET</code> - IPv4 Internet based protocols. TCP and UDP are common protocols of this protocol family.
207
     *                      </p><p><code>AF_INET6</code> - IPv6 Internet based protocols. TCP and UDP are common protocols of this protocol
208
     *                      family.</p><p><code>AF_UNIX</code> - Local communication protocol family. High efficiency and low overhead make
209
     *                      it a great form of IPC (Interprocess Communication).</p>
210
     * @param int $type     <p>The type parameter selects the type of communication to be used by the socket.</p><p>
211
     *                      <code>SOCK_STREAM</code> - Provides sequenced, reliable, full-duplex, connection-based byte streams. An
212
     *                      out-of-band data transmission mechanism may be supported. The TCP protocol is based on this socket type.</p><p>
213
     *                      <code>SOCK_DGRAM</code> - Supports datagrams (connectionless, unreliable messages of a fixed maximum length).
214
     *                      The UDP protocol is based on this socket type.</p><p><code>SOCK_SEQPACKET</code> - Provides a sequenced,
215
     *                      reliable, two-way connection-based data transmission path for datagrams of fixed maximum length; a consumer is
216
     *                      required to read an entire packet with each read call.</p><p><code>SOCK_RAW</code> - Provides raw network
217
     *                      protocol access. This special type of socket can be used to manually construct any type of protocol. A common
218
     *                      use for this socket type is to perform ICMP requests (like ping).</p><p><code>SOCK_RDM</code> - Provides a
219
     *                      reliable datagram layer that does not guarantee ordering. This is most likely not implemented on your operating
220
     *                      system.</p>
221
     * @param int $protocol <p>The protocol parameter sets the specific protocol within the specified domain to be used
222
     *                      when communicating on the returned socket. The proper value can be retrieved by name by using
223
     *                      <code>getprotobyname()</code>. If the desired protocol is TCP, or UDP the corresponding constants
224
     *                      <code>SOL_TCP</code>, and <code>SOL_UDP</code> can also be used.<p><p>Some of the common protocol types</p><p>
225
     *                      icmp - The Internet Control Message Protocol is used primarily by gateways and hosts to report errors in
226
     *                      datagram communication. The "ping" command (present in most modern operating systems) is an example application
227
     *                      of ICMP.</p><p>udp - The User Datagram Protocol is a connectionless, unreliable, protocol with fixed record
228
     *                      lengths. Due to these aspects, UDP requires a minimum amount of protocol overhead.</p><p>tcp - The Transmission
229
     *                      Control Protocol is a reliable, connection based, stream oriented, full duplex protocol. TCP guarantees that all
230
     *                      data packets will be received in the order in which they were sent. If any packet is somehow lost during
231
     *                      communication, TCP will automatically retransmit the packet until the destination host acknowledges that packet.
232
     *                      For reliability and performance reasons, the TCP implementation itself decides the appropriate octet boundaries
233
     *                      of the underlying datagram communication layer. Therefore, TCP applications must allow for the possibility of
234
     *                      partial record transmission.</p>
235
     *
236
     * @throws Exception\SocketException If there is an error creating the php socket.
237
     *
238
     * @return Socket Returns a Socket object based on the successful creation of the php socket.
239
     */
240
    public static function create($domain, $type, $protocol)
241
    {
242 4
        $return = @socket_create($domain, $type, $protocol);
243
244 4
        if ($return === false) {
245
            throw new SocketException();
246 4
        }
247 1
248
        $socket = new self($return);
249
        $socket->domain = $domain;
250 3
        $socket->type = $type;
251 3
        $socket->protocol = $protocol;
252 3
253 3
        return $socket;
254
    }
255 3
256
    /**
257
     * Opens a socket on port to accept connections.
258
     *
259
     * <p>Creates a new socket resource of type <code>AF_INET</code> listening on all local interfaces on the given
260
     * port waiting for new connections.</p>
261
     *
262
     * @param int $port    The port on which to listen on all interfaces.
263
     * @param int $backlog <p>The backlog parameter defines the maximum length the queue of pending connections may
264
     *                     grow to. <code>SOMAXCONN</code> may be passed as the backlog parameter.</p>
265
     *
266
     * @throws Exception\SocketException If the socket is not successfully created.
267
     *
268
     * @see Socket::create()
269
     * @see Socket::bind()
270
     * @see Socket::listen()
271
     *
272
     * @return Socket Returns a Socket object based on the successful creation of the php socket.
273
     */
274
    public static function createListen($port, $backlog = 128)
275
    {
276
        $return = @socket_create_listen($port, $backlog);
277
278
        if ($return === false) {
279
            throw new SocketException();
280
        }
281
282
        $socket = new self($return);
283
        $socket->domain = AF_INET;
284
285
        return $socket;
286
    }
287
288
    /**
289
     * Creates a pair of indistinguishable sockets and stores them in an array.
290
     *
291
     * <p>Creates two connected and indistinguishable sockets. This function is commonly used in IPC (InterProcess
292
     * Communication).</p>
293
     *
294
     * @param int $domain   <p>The domain parameter specifies the protocol family to be used by the socket. See
295
     *                      <code>create()</code> for the full list.</p>
296
     * @param int $type     <p>The type parameter selects the type of communication to be used by the socket. See
297
     *                      <code>create()</code> for the full list.</p>
298
     * @param int $protocol <p>The protocol parameter sets the specific protocol within the specified domain to be used
299
     *                      when communicating on the returned socket. The proper value can be retrieved by name by using
300
     *                      <code>getprotobyname()</code>. If the desired protocol is TCP, or UDP the corresponding constants
301
     *                      <code>SOL_TCP</code>, and <code>SOL_UDP</code> can also be used. See <code>create()</code> for the full list of
302
     *                      supported protocols.
303
     *
304
     * @throws Exception\SocketException If the creation of the php sockets is not successful.
305
     *
306
     * @see Socket::create()
307
     *
308
     * @return Socket[] An array of Socket objects containing identical sockets.
309
     */
310
    public static function createPair($domain, $type, $protocol)
311
    {
312
        $array = [];
313
        $return = @socket_create_pair($domain, $type, $protocol, $array);
314
315
        if ($return === false) {
316
            throw new SocketException();
317
        }
318
319
        $sockets = self::constructFromResources($array);
320
321
        foreach ($sockets as $socket) {
322
            $socket->domain = $domain;
323
            $socket->type = $type;
324
            $socket->protocol = $protocol;
325
        }
326
327
        return $sockets;
328
    }
329
330
    /**
331
     * Gets socket options.
332
     *
333
     * <p>Retrieves the value for the option specified by the optname parameter for the current socket.</p>
334
     *
335
     * @param int $level   <p>The level parameter specifies the protocol level at which the option resides. For example,
336
     *                     to retrieve options at the socket level, a level parameter of <code>SOL_SOCKET</code> would be used. Other
337
     *                     levels, such as <code>TCP</code>, can be used by specifying the protocol number of that level. Protocol numbers
338
     *                     can be found by using the <code>getprotobyname()</code> function.
339
     * @param int $optname <p><b>Available Socket Options</b></p><p><code>SO_DEBUG</code> - Reports whether debugging
340
     *                     information is being recorded. Returns int.</p><p><code>SO_BROADCAST</code> - Reports whether transmission of
341
     *                     broadcast messages is supported. Returns int.</p><p><code>SO_REUSERADDR</code> - Reports whether local addresses
342
     *                     can be reused. Returns int.</p><p><code>SO_KEEPALIVE</code> - Reports whether connections are kept active with
343
     *                     periodic transmission of messages. If the connected socket fails to respond to these messages, the connection is
344
     *                     broken and processes writing to that socket are notified with a SIGPIPE signal. Returns int.</p><p>
345
     *                     <code>SO_LINGER</code> - Reports whether the socket lingers on <code>close()</code> if data is present. By
346
     *                     default, when the socket is closed, it attempts to send all unsent data. In the case of a connection-oriented
347
     *                     socket, <code>close()</code> will wait for its peer to acknowledge the data. If <code>l_onoff</code> is non-zero
348
     *                     and <code>l_linger</code> is zero, all the unsent data will be discarded and RST (reset) is sent to the peer in
349
     *                     the case of a connection-oriented socket. On the other hand, if <code>l_onoff</code> is non-zero and
350
     *                     <code>l_linger</code> is non-zero, <code>close()</code> will block until all the data is sent or the time
351
     *                     specified in <code>l_linger</code> elapses. If the socket is non-blocking, <code>close()</code> will fail and
352
     *                     return an error. Returns an array with two keps: <code>l_onoff</code> and <code>l_linger</code>.</p><p>
353
     *                     <code>SO_OOBINLINE</code> - Reports whether the socket leaves out-of-band data inline. Returns int.</p><p>
354
     *                     <code>SO_SNDBUF</code> - Reports the size of the send buffer. Returns int.</p><p><code>SO_RCVBUF</code> -
355
     *                     Reports the size of the receive buffer. Returns int.</p><p><code>SO_ERROR</code> - Reports information about
356
     *                     error status and clears it. Returns int.</p><p><code>SO_TYPE</code> - Reports the socket type (e.g.
357
     *                     <code>SOCK_STREAM</code>). Returns int.</p><p><code>SO_DONTROUTE</code> - Reports whether outgoing messages
358
     *                     bypass the standard routing facilities. Returns int.</p><p><code>SO_RCVLOWAT</code> - Reports the minimum number
359
     *                     of bytes to process for socket input operations. Returns int.</p><p><code>SO_RCVTIMEO</code> - Reports the
360
     *                     timeout value for input operations. Returns an array with two keys: <code>sec</code> which is the seconds part
361
     *                     on the timeout value and <code>usec</code> which is the microsecond part of the timeout value.</p><p>
362
     *                     <code>SO_SNDTIMEO</code> - Reports the timeout value specifying the amount of time that an output function
363
     *                     blocks because flow control prevents data from being sent. Returns an array with two keys: <code>sec</code>
364
     *                     which is the seconds part on the timeout value and <code>usec</code> which is the microsecond part of the
365
     *                     timeout value.</p><p><code>SO_SNDLOWAT</code> - Reports the minimum number of bytes to process for socket output
366
     *                     operations. Returns int.</p><p><code>TCP_NODELAY</code> - Reports whether the Nagle TCP algorithm is disabled.
367
     *                     Returns int.</p><p><code>IP_MULTICAST_IF</code> - The outgoing interface for IPv4 multicast packets. Returns the
368
     *                     index of the interface (int).</p><p><code>IPV6_MULTICAST_IF</code> - The outgoing interface for IPv6 multicast
369
     *                     packets. Returns the same thing as <code>IP_MULTICAST_IF</code>.</p><p><code>IP_MULTICAST_LOOP</code> - The
370
     *                     multicast loopback policy for IPv4 packets, which determines whether multicast packets sent by this socket also
371
     *                     reach receivers in the same host that have joined the same multicast group on the outgoing interface used by
372
     *                     this socket. This is the case by default. Returns int.</p><p><code>IPV6_MULTICAST_LOOP</code> - Analogous to
373
     *                     <code>IP_MULTICAST_LOOP</code>, but for IPv6. Returns int.</p><p><code>IP_MULTICAST_TTL</code> - The
374
     *                     time-to-live of outgoing IPv4 multicast packets. This should be a value between 0 (don't leave the interface)
375
     *                     and 255. The default value is 1 (only the local network is reached). Returns int.</p><p>
376
     *                     <code>IPV6_MULTICAST_HOPS</code> - Analogous to <code>IP_MULTICAST_TTL</code>, but for IPv6 packets. The value
377
     *                     -1 is also accepted, meaning the route default should be used. Returns int.</p>
378
     *
379
     * @throws Exception\SocketException If there was an error retrieving the option.
380
     *
381
     * @return mixed See the descriptions based on the option being requested above.
382
     */
383
    public function getOption($level, $optname)
384
    {
385
        return static::exceptionOnFalse(
386
            $this->resource,
387
            function ($resource) use ($level, $optname) {
388
                return @socket_get_option($resource, $level, $optname);
389
            }
390
        );
391
    }
392
393
    /**
394
     * Queries the remote side of the given socket which may either result in host/port or in a Unix filesystem
395
     * path, dependent on its type.
396
     *
397
     * @param string $address <p>If the given socket is of type <code>AF_INET</code> or <code>AF_INET6</code>,
398
     *                        <code>getPeerName()</code> will return the peers (remote) IP address in appropriate notation (e.g.
399
     *                        <code>127.0.0.1</code> or <code>fe80::1</code>) in the address parameter and, if the optional port parameter is
400
     *                        present, also the associated port.</p><p>If the given socket is of type <code>AF_UNIX</code>,
401
     *                        <code>getPeerName()</code> will return the Unix filesystem path (e.g. <code>/var/run/daemon.sock</cod>) in the
402
     *                        address parameter.</p>
403
     * @param int    $port    (Optional) If given, this will hold the port associated to the address.
404
     *
405
     * @throws Exception\SocketException <p>If the retrieval of the peer name fails or if the socket type is not
406
     *                                   <code>AF_INET</code>, <code>AF_INET6</code>, or <code>AF_UNIX</code>.</p>
407
     *
408
     * @return bool <p>Returns <code>true</code> if the retrieval of the peer name was successful.</p>
409
     */
410
    public function getPeerName(&$address, &$port)
411
    {
412
        return static::exceptionOnFalse(
413
            $this->resource,
414
            function ($resource) use (&$address, &$port) {
415
                return @socket_getpeername($resource, $address, $port);
416
            }
417
        );
418
    }
419
420
    /**
421
     * Queries the local side of the given socket which may either result in host/port or in a Unix filesystem path,
422
     * dependent on its type.
423
     *
424
     * <p><b>Note:</b> <code>getSockName()</code> should not be used with <code>AF_UNIX</code> sockets created with
425
     * <code>connect()</code>. Only sockets created with <code>accept()</code> or a primary server socket following a
426
     * call to <code>bind()</code> will return meaningful values.</p>
427
     *
428
     * @param string $address <p>If the given socket is of type <code>AF_INET</code> or <code>AF_INET6</code>,
429
     *                        <code>getSockName()</code> will return the local IP address in appropriate notation (e.g.
430
     *                        <code>127.0.0.1</code> or <code>fe80::1</code>) in the address parameter and, if the optional port parameter is
431
     *                        present, also the associated port.</p><p>If the given socket is of type <code>AF_UNIX</code>,
432
     *                        <code>getSockName()</code> will return the Unix filesystem path (e.g. <code>/var/run/daemon.sock</cod>) in the
433
     *                        address parameter.</p>
434
     * @param int    $port    If provided, this will hold the associated port.
435
     *
436
     * @throws Exception\SocketException <p>If the retrieval of the socket name fails or if the socket type is not
437
     *                                   <code>AF_INET</code>, <code>AF_INET6</code>, or <code>AF_UNIX</code>.</p>
438
     *
439
     * @return bool <p>Returns <code>true</code> if the retrieval of the socket name was successful.</p>
440
     */
441
    public function getSockName(&$address, &$port)
442
    {
443
        if (!in_array($this->domain, [AF_UNIX, AF_INET, AF_INET6])) {
444
            return false;
445 1
        }
446
447 1
        return static::exceptionOnFalse(
448
            $this->resource,
449
            function ($resource) use (&$address, &$port) {
450
                return @socket_getsockname($resource, $address, $port);
451 1
            }
452
        );
453 1
    }
454
455
    /**
456
     * Imports a stream.
457 1
     *
458
     * <p>Imports a stream that encapsulates a socket into a socket extension resource.</p>
459
     *
460
     * @param resource $stream The stream resource to import.
461
     *
462
     * @throws Exception\SocketException If the import of the stream is not successful.
463
     *
464
     * @return Socket Returns a Socket object based on the stream.
465
     */
466
    public static function importStream($stream)
467
    {
468
        $return = @socket_import_stream($stream);
469
470
        if ($return === false || is_null($return)) {
471
            throw new SocketException($stream);
472
        }
473
474
        return new self($return);
475
    }
476
477
    /**
478
     * Listens for a connection on a socket.
479
     *
480
     * <p>After the socket has been created using <code>create()</code> and bound to a name with <code>bind()</code>,
481
     * it may be told to listen for incoming connections on socket.</p>
482
     *
483
     * @param int $backlog <p>A maximum of backlog incoming connections will be queued for processing. If a connection
484
     *                     request arrives with the queue full the client may receive an error with an indication of ECONNREFUSED, or, if
485
     *                     the underlying protocol supports retransmission, the request may be ignored so that retries may succeed.</p><p>
486
     *                     <b>Note:</b> The maximum number passed to the backlog parameter highly depends on the underlying platform. On
487
     *                     Linux, it is silently truncated to <code>SOMAXCONN</code>. On win32, if passed <code>SOMAXCONN</code>, the
488
     *                     underlying service provider responsible for the socket will set the backlog to a maximum reasonable value. There
489
     *                     is no standard provision to find out the actual backlog value on this platform.</p>
490
     *
491
     * @throws Exception\SocketException If the listen fails.
492
     *
493
     * @return bool <p>Returns <code>true</code> on success.
494
     */
495
    public function listen($backlog = 0)
496
    {
497
        return static::exceptionOnFalse(
498
            $this->resource,
499
            function ($resource) use ($backlog) {
500 1
                return @socket_listen($resource, $backlog);
501
            }
502 1
        );
503
    }
504 1
505
    /**
506
     * reads a maximum of length bytes from a socket.
507
     *
508 1
     * <p>Reads from the socket created by the <code>create()</code> or <code>accept()</code> functions.</p>
509
     *
510
     * @param int $length <p>The maximum number of bytes read is specified by the length parameter. Otherwise you can
511
     *                    use <code>\r</code>, <code>\n</code>, or <code>\0</code> to end reading (depending on the type parameter, see
512
     *                    below).</p>
513
     * @param int $type   <p>(Optional) type parameter is a named constant:<ul><li><code>PHP_BINARY_READ</code> (Default)
514
     *                    - use the system <code>recv()</code> function. Safe for reading binary data.</li><li>
515
     *                    <code>PHP_NORMAL_READ</code> - reading stops at <code>\n</code> or <code>\r</code>.</li></ul></p>
516
     *
517
     * @throws Exception\SocketException If there was an error reading or if the host closed the connection.
518
     *
519
     * @see Socket::create()
520
     * @see Socket::accept()
521
     *
522
     * @return string Returns the data as a string. Returns a zero length string ("") when there is no more data to
523
     *                read.
524
     */
525
    public function read($length, $type = PHP_BINARY_READ)
526
    {
527
        return static::exceptionOnFalse(
528
            $this->resource,
529
            function ($resource) use ($length, $type) {
530
                return @socket_read($resource, $length, $type);
531
            }
532
        );
533
    }
534
535
    /**
536
     * Receives data from a connected socket.
537
     *
538
     * <p>Receives length bytes of data in buffer from the socket. <code>receive()</code> can be used to gather data
539
     * from connected sockets. Additionally, one or more flags can be specified to modify the behaviour of the
540
     * function.</p><p>buffer is passed by reference, so it must be specified as a variable in the argument list. Data
541
     * read from socket by <code>receive()</code> will be returned in buffer.</p>
542
     *
543
     * @param string $buffer <p>The data received will be fetched to the variable specified with buffer. If an error
544
     *                       occurs, if the connection is reset, or if no data is available, buffer will be set to <code>NULL</code>.</p>
545
     * @param int    $length Up to length bytes will be fetched from remote host.
546
     * @param int    $flags  <p>The value of flags can be any combination of the following flags, joined with the binary OR
547
     *                       (<code>|</code>) operator.<ul><li><code>MSG_OOB</code> - Process out-of-band data.</li><li><code>MSG_PEEK</code>
548
     *                       - Receive data from the beginning of the receive queue without removing it from the queue.</li><li>
549
     *                       <code>MSG_WAITALL</code> - Block until at least length are received. However, if a signal is caught or the
550
     *                       remote host disconnects, the function may return less data.</li><li><code>MSG_DONTWAIT</code> - With this flag
551
     *                       set, the function returns even if it would normally have blocked.</li></ul></p>
552
     *
553
     * @throws Exception\SocketException If there was an error receiving data.
554
     *
555
     * @return int Returns the number of bytes received.
556
     */
557
    public function receive(&$buffer, $length, $flags)
558
    {
559
        return static::exceptionOnFalse(
560
            $this->resource,
561
            function ($resource) use (&$buffer, $length, $flags) {
562
                return @socket_recv($resource, $buffer, $length, $flags);
563
            }
564
        );
565
    }
566
567
    /**
568
     * Runs the select() system call on the given arrays of sockets with a specified timeout.
569
     *
570
     * <p>accepts arrays of sockets and waits for them to change status. Those coming with BSD sockets background will
571
     * recognize that those socket resource arrays are in fact the so-called file descriptor sets. Three independent
572
     * arrays of socket resources are watched.</p><p><b>WARNING:</b> On exit, the arrays are modified to indicate which
573
     * socket resource actually changed status.</p><p>ou do not need to pass every array to <code>select()</code>. You
574
     * can leave it out and use an empty array or <code>NULL</code> instead. Also do not forget that those arrays are
575
     * passed by reference and will be modified after <code>select()</code> returns.
576
     *
577
     * @param Socket[] &$read               <p>The sockets listed in the read array will be watched to see if characters become
578
     *                                      available for reading (more precisely, to see if a read will not block - in particular, a socket resource is also
579
     *                                      ready on end-of-file, in which case a <code>read()</code> will return a zero length string).</p>
580
     * @param Socket[] &$write              The sockets listed in the write array will be watched to see if a write will not block.
581
     * @param Socket[] &$except             he sockets listed in the except array will be watched for exceptions.
582
     * @param int      $timeoutSeconds      The seconds portion of the timeout parameters (in conjunction with
583
     *                                      timeoutMilliseconds). The timeout is an upper bound on the amount of time elapsed before <code>select()</code>
584
     *                                      returns. timeoutSeconds may be zero, causing the <code>select()</code> to return immediately. This is useful for
585
     *                                      polling. If timeoutSeconds is <code>NULL</code> (no timeout), the <code>select()</code> can block
586
     *                                      indefinitely.</p>
587
     * @param int      $timeoutMilliseconds See the description for timeoutSeconds.
588
     *
589
     * @throws SocketException If there was an error.
590
     *
591
     * @return int Returns the number of socket resources contained in the modified arrays, which may be zero if the
592
     *             timeout expires before anything interesting happens.
593
     */
594
    public static function select(
595
        &$read,
596
        &$write,
597
        &$except,
598
        $timeoutSeconds,
599
        $timeoutMilliseconds = 0
600
    ) {
601
        $readSockets = null;
602
        $writeSockets = null;
603
        $exceptSockets = null;
604
605
        if (!is_null($read)) {
606
            $readSockets = self::mapClassToRawSocket($read);
607
        }
608
        if (!is_null($write)) {
609
            $writeSockets = self::mapClassToRawSocket($write);
610
        }
611
        if (!is_null($except)) {
612
            $exceptSockets = self::mapClassToRawSocket($except);
613
        }
614
615
        $return = @socket_select(
616
            $readSockets,
617
            $writeSockets,
618
            $exceptSockets,
619
            $timeoutSeconds,
620
            $timeoutMilliseconds
621
        );
622
623
        if ($return === false) {
624
            throw new SocketException();
625
        }
626
627
        $read = [];
628
        $write = [];
629
        $except = [];
630
631
        if ($readSockets) {
632
            $read = static::mapRawSocketToClass($readSockets);
633
        }
634
        if ($writeSockets) {
635
            $write = static::mapRawSocketToClass($writeSockets);
636
        }
637
        if ($exceptSockets) {
638
            $except = static::mapRawSocketToClass($exceptSockets);
639
        }
640
641
        return $return;
642
    }
643
644
    /**
645
     * Maps an array of Sockets to an array of socket resources.
646
     *
647
     * @param Socket[] $sockets An array of sockets to map.
648
     *
649
     * @return resource[] Returns the corresponding array of resources.
650
     */
651
    protected static function mapClassToRawSocket($sockets)
652
    {
653
        return array_map(function (Socket $socket) {
654
            return $socket->resource;
655
        }, $sockets);
656
    }
657
658
    /**
659 1
     * Maps an array of socket resources to an array of Sockets.
660
     *
661 1
     * @param resource[] $sockets An array of socket resources to map.
662 1
     *
663 1
     * @return Socket[] Returns the corresponding array of Socket objects.
664
     */
665
    protected static function mapRawSocketToClass($sockets)
666
    {
667
        return array_map(function ($rawSocket) {
668
            return self::$map[(string) $rawSocket];
669
        }, $sockets);
670
    }
671
672
    /**
673
     * Performs the closure function.  If it returns false, throws a SocketException using the provided resource.
674
     *
675
     * @param resource $resource Socket Resource
676
     * @param callable $closure  A function that takes 1 parameter (a socket resource)
677
     *
678
     * @throws SocketException
679
     */
680
    protected static function exceptionOnFalse($resource, callable $closure)
681
    {
682
        $result = $closure($resource);
683
684
        if ($result === false) {
685
            throw new SocketException($resource);
686
        }
687
688
        return $result;
689
    }
690
691
    /**
692
     * Write to a socket.
693 1
     *
694
     * <p>The function <code>write()</code> writes to the socket from the given buffer.</p>
695 1
     *
696
     * @param string $buffer The buffer to be written.
697
     * @param int    $length The optional parameter length can specify an alternate length of bytes written to the socket.
698
     *                       If this length is greater than the buffer length, it is silently truncated to the length of the buffer.
699
     *
700
     * @throws Exception\SocketException If there was a failure.
701 1
     *
702
     * @return int Returns the number of bytes successfully written to the socket.
703 1
     */
704 View Code Duplication
    public function write($buffer, $length = null)
705
    {
706
        if (null === $length) {
707 1
            $length = strlen($buffer);
708
        }
709
710
        // make sure everything is written
711 1
        do {
712 1
            $return = @socket_write($this->resource, $buffer, $length);
713
714
            if (false !== $return && $return < $length) {
715
                $buffer = substr($buffer, $return);
716
                $length -= $return;
717
            } else {
718
                break;
719
            }
720
        } while (true);
721
722
        if ($return === false) {
723
            throw new SocketException($this->resource);
724
        }
725
726
        return $return;
727
    }
728
729
    /**
730
     * Sends data to a connected socket.
731
     *
732
     * <p>Sends length bytes to the socket from buffer.</p>
733
     *
734
     * @param string $buffer A buffer containing the data that will be sent to the remote host.
735
     * @param int    $flags  <p>The value of flags can be any combination of the following flags, joined with the binary OR
736
     *                       (<code>|</code>) operator.<ul><li><code>MSG_OOB</code> - Send OOB (out-of-band) data.</li><li>
737
     *                       <code>MSG_EOR</code> - Indicate a record mark. The sent data completes the record.</li><li><code>MSG_EOF</code> -
738
     *                       Close the sender side of the socket and include an appropriate notification of this at the end of the sent data.
739
     *                       The sent data completes the transaction.</li><li><code>MSG_DONTROUTE</code> - Bypass routing, use direct
740
     *                       interface.</li></ul></p>
741
     * @param int    $length The number of bytes that will be sent to the remote host from buffer.
742
     *
743
     * @throws Exception\SocketException If there was a failure.
744
     *
745
     * @return int Returns the number of bytes sent.
746
     */
747 View Code Duplication
    public function send($buffer, $flags = 0, $length = null)
748
    {
749
        if (null === $length) {
750
            $length = strlen($buffer);
751
        }
752
753
        // make sure everything is written
754
        do {
755
            $return = @socket_send($this->resource, $buffer, $length, $flags);
756
757
            if (false !== $return && $return < $length) {
758
                $buffer = substr($buffer, $return);
759
                $length -= $return;
760
            } else {
761
                break;
762
            }
763
        } while (true);
764
765
        if ($return === false) {
766
            throw new SocketException($this->resource);
767
        }
768
769
        return $return;
770
    }
771
772
    /**
773
     * Set the socket to blocking / non blocking.
774
     *
775
     * <p>Removes (blocking) or set (non blocking) the <code>O_NONBLOCK</code> flag on the socket.</p><p>When an
776
     * operation is performed on a blocking socket, the script will pause its execution until it receives a signal or it
777
     * can perform the operation.</p><p>When an operation is performed on a non-blocking socket, the script will not
778
     * pause its execution until it receives a signal or it can perform the operation. Rather, if the operation would
779
     * result in a block, the called function will fail.</p>
780
     *
781
     * @param bool $bool Flag to indicate if the Socket should block (<code>true</code>) or not block
782
     *                   (<code>false</code>).
783
     *
784
     * @return void
785
     */
786
    public function setBlocking($bool)
787
    {
788
        if ($bool) {
789
            @socket_set_block($this->resource);
790
        } else {
791
            @socket_set_nonblock($this->resource);
792
        }
793
    }
794
}
795