├── LICENSE
├── README.md
├── composer.json
├── infection.json.dist
├── phpcs.xml.dist
├── phpstan.neon
└── src
├── Exception
└── SocketException.php
├── Server.php
└── Socket.php
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2016 Navarr Barnier
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sockets
2 |
3 | [](https://packagist.org/packages/navarr/Sockets)
4 | [](https://packagist.org/packages/navarr/Sockets)
5 | [](https://packagist.org/packages/navarr/Sockets)
6 | [](https://packagist.org/packages/navarr/Sockets)
7 | 
8 | 
9 | [](https://dashboard.stryker-mutator.io/reports/github.com/navarr/Sockets/main)
10 |
11 | Sockets is a PHP Library intent on making working with PHP Sockets easier, including the creation and management of a Socket Server.
12 |
13 | ## Work in Progress
14 |
15 | The code is currently still a work in progress, with the Socket class itself not yet fully complete. There is a lot I still need to understand about how sockets work both in PHP and probably in C in order to make everything work amazingly.
16 |
17 | Not everything is tested yet, and not everything works properly yet.
18 |
19 | It is advised not to seriously use this until I create git tag 1.0.0. There will be breaking changes before then.
20 |
21 | ## Usage of SocketServer
22 |
23 | Using SocketServer is supposed to be an easy and trivial task (and the class should be documented enough to understand what it's doing without me).
24 |
25 | ### Example of an ECHO Server
26 |
27 | ```php
28 | addHook(Server::HOOK_CONNECT, array($this, 'onConnect'));
41 | $this->addHook(Server::HOOK_INPUT, array($this, 'onInput'));
42 | $this->addHook(Server::HOOK_DISCONNECT, array($this, 'onDisconnect'));
43 | $this->run();
44 | }
45 |
46 | public function onConnect(Server $server, Socket $client, $message)
47 | {
48 | echo 'Connection Established',"\n";
49 | }
50 |
51 | public function onInput(Server $server, Socket $client, $message)
52 | {
53 | echo 'Received "',$message,'"',"\n";
54 | $client->write($message, strlen($message));
55 | }
56 |
57 | public function onDisconnect(Server $server, Socket $client, $message)
58 | {
59 | echo 'Disconnection',"\n";
60 | }
61 | }
62 |
63 | $server = new EchoServer('0.0.0.0');
64 |
65 | ```
66 |
67 | ## Development
68 |
69 | ### Documentation
70 |
71 | This project uses [phpdoc](https://www.phpdoc.org/) to generate documentation. To generate the documentation, you will need to satisfy some dependencies. First, you need to get [graphviz](http://www.graphviz.org/). It is available through most Linux distros, but you can always download it and install it from the site if you aren't on Linux. If you install manually, make sure the binaries are on your PATH somewhere. Next, run the following commands within this directory (assumes you already have [composer](https://getcomposer.org/) installed and available on your path as `composer`).
72 |
73 | ```bash
74 | composer install # this will install all of the development dependencies for this project
75 | vendor/bin/phpdoc -d ./src -t ./docs # this will generate the documentation into a docs directory
76 | ```
77 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "navarr/sockets",
3 | "description": "Sockets in PHP",
4 | "minimum-stability": "stable",
5 | "authors": [
6 | {
7 | "name": "Navarr Barnier",
8 | "email": "me@navarr.me",
9 | "homepage": "http://tech.navarr.me/",
10 | "role": "Developer"
11 | }
12 | ],
13 | "require": {
14 | "php": "^8",
15 | "ext-sockets": "*"
16 | },
17 | "license": "MIT",
18 | "require-dev": {
19 | "phpunit/phpunit": "^9.5",
20 | "phpstan/phpstan": "^1",
21 | "infection/infection": "^0.26",
22 | "squizlabs/php_codesniffer": "^3.6",
23 | "jetbrains/phpstorm-attributes": "^1",
24 | "roave/security-advisories": "dev-master"
25 | },
26 | "autoload": {
27 | "psr-4": {
28 | "Navarr\\Socket\\": "src/"
29 | }
30 | },
31 | "autoload-dev": {
32 | "psr-4": {
33 | "Navarr\\Socket\\Test\\": "tests/"
34 | }
35 | },
36 | "scripts": {
37 | "test": [
38 | "@composer install",
39 | "phpunit"
40 | ]
41 | },
42 | "config": {
43 | "allow-plugins": {
44 | "infection/extension-installer": true
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/infection.json.dist:
--------------------------------------------------------------------------------
1 | {
2 | "source": {
3 | "directories": [
4 | "src"
5 | ]
6 | },
7 | "phpUnit": {
8 | "configDir": "."
9 | },
10 | "mutators": {
11 | "@default": true
12 | },
13 | "logs": {
14 | "stryker": {
15 | "report": "/^.*$/"
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/phpcs.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
This class uses the
46 | * Factory pattern to create instances. Please use the create
method to create new instances
47 | * of this class.
48 | *
49 | * @param SocketResource $resource The php socket resource. This is just a reference to the socket object created
50 | * using the socket_create
method.
51 | * @see Socket::create()
52 | *
53 | */
54 | protected function __construct(protected SocketResource $resource)
55 | {
56 | self::$map[$this->__toString()] = $this;
57 | }
58 |
59 | /**
60 | * Cleans up the Socket and dereferences the internal resource.
61 | */
62 | public function __destruct()
63 | {
64 | $this->close();
65 | unset($this->resource);
66 | }
67 |
68 | /**
69 | * Return the php socket resource name.
70 | *
71 | *
Resources are always converted to strings with the structure "Resource id#1", where 1 is the resource number 72 | * assigned to the resource by PHP at runtime. While the exact structure of this string should not be relied on and 73 | * is subject to change, it will always be unique for a given resource within the lifetime of the script execution 74 | * and won't be reused.
75 | * 76 | *If the resource object has been dereferrenced (set to null
), this will return an empty
77 | * string.
After the socket socket has been created using create()
, bound to a name with
90 | * bind()
, and told to listen for connections with listen()
, this function will accept
91 | * incoming connections on that socket. Once a successful connection is made, a new Socket resource is returned,
92 | * which may be used for communication. If there are multiple connections queued on the socket, the first will be
93 | * used. If there are no pending connections, this will block until a connection becomes present. If socket has
94 | * been made non-blocking using setBlocking()
, a SocketException
will be thrown.
The Socket returned by this method may not be used to accept new connections. The original listening Socket, 97 | * however, remains open and may be reused.
98 | * 99 | * @return Socket A new Socket representation of the accepted socket. 100 | * @throws SocketException If the Socket is set as non-blocking and there are no pending connections. 101 | * 102 | * @see Socket::bind() 103 | * @see Socket::listen() 104 | * @see Socket::setBlocking() 105 | * 106 | * @see Socket::create() 107 | */ 108 | public function accept(): self 109 | { 110 | $return = @socket_accept($this->resource); 111 | 112 | if ($return === false) { 113 | throw new SocketException($this->resource); 114 | } 115 | 116 | return new self($return); 117 | } 118 | 119 | /** 120 | * Binds a name to a socket. 121 | * 122 | *Binds the name given in address to the php socket resource currently in use. This has to be done before a
123 | * connection is established using connect()
or listen()
.
If the socket is of the AF_INET family, the address is an IP in dotted-quad notation
126 | * (e.g. 127.0.0.1
).
If the socket is of the AF_UNIX family, the address is the path of the
127 | * Unix-domain socket (e.g. /tmp/my.sock
).
(Optional) The port parameter is only used when binding an AF_INET socket, and 129 | * designates the port on which to listen for connections.
130 | * 131 | * @return bool Returns `true` if the bind was successful. 132 | * @throws SocketException If the bind was unsuccessful. 133 | */ 134 | public function bind(string $address, int $port = 0): bool 135 | { 136 | return static::exceptionOnFalse( 137 | $this->resource, 138 | static function ($resource) use ($address, $port) { 139 | return @socket_bind($resource, $address, $port); 140 | } 141 | ); 142 | } 143 | 144 | /** 145 | * Close the socket. 146 | * 147 | *Closes the php socket resource currently in use and removes the reference to it in the internal map.
148 | */ 149 | public function close(): void 150 | { 151 | unset(self::$map[$this->__toString()]); 152 | try { 153 | @socket_close($this->resource); 154 | } catch (Error $e) { 155 | if (!str_contains($e->getMessage(), 'has already been closed')) { 156 | throw $e; 157 | } 158 | } 159 | } 160 | 161 | /** 162 | * Connect to a socket. 163 | * 164 | *Initiate a connection to the address given using the current php socket resource, which must be a valid
165 | * socket resource created with create()
.
166 | *
167 | * @param string $address
The address parameter is either an IPv4 address in dotted-quad notation (e.g.
168 | * 127.0.0.1
) if the socket is AF_INET, a valid IPv6 address (e.g. ::1
) if IPv6 support
169 | * is enabled and the socket is AF_INET6, or the pathname of a Unix domain socket, if the socket family is
170 | * AF_UNIX.
(Optional) The port parameter is only used and is mandatory when connecting to an AF_INET 172 | * or an AF_INET6 socket, and designates the port on the remote host to which a connection should be made.
173 | * 174 | * @return bool Returnstrue
if connection was successful.
175 | * @throws SocketException If connection was unsuccessful or if the socket is non-blocking.
176 | * @see Socket::listen()
177 | * @see Socket::create()
178 | * @see Socket::bind()
179 | */
180 | public function connect(string $address, int $port = 0): bool
181 | {
182 | return static::exceptionOnFalse(
183 | $this->resource,
184 | static function ($resource) use ($address, $port) {
185 | return @socket_connect($resource, $address, $port);
186 | }
187 | );
188 | }
189 |
190 | /**
191 | * Build Socket objects based on an array of php socket resources.
192 | *
193 | * @param SocketResource[] $resources A list of php socket resource objects.
194 | *
195 | * @return Socket[] Returns an array of Socket objects built from the given php socket resources.
196 | */ 197 | protected static function constructFromResources(array $resources): array 198 | { 199 | return array_map(static function ($resource) { 200 | return new self($resource); 201 | }, $resources); 202 | } 203 | 204 | /** 205 | * Create a socket. 206 | * 207 | *Creates and returns a Socket. A typical network connection is made up of two sockets, one performing the role 208 | * of the client, and another performing the role of the server.
209 | * 210 | * @param int $domainThe domain parameter specifies the protocol family to be used by the socket.
211 | * AF_INET
- IPv4 Internet based protocols. TCP and UDP are common protocols of this protocol family.
212 | *
AF_INET6
- IPv6 Internet based protocols. TCP and UDP are common protocols of this
213 | * protocol family.
AF_UNIX
- Local communication protocol family. High efficiency and low
214 | * overhead make it a great form of IPC (Interprocess Communication).
The type parameter selects the type of communication to be used by the socket.
216 | * SOCK_STREAM
- Provides sequenced, reliable, full-duplex, connection-based byte streams. An
217 | * out-of-band data transmission mechanism may be supported. The TCP protocol is based on this socket type
218 | * .
SOCK_DGRAM
- Supports datagrams (connectionless, unreliable messages of a fixed maximum
219 | * length). The UDP protocol is based on this socket type.
SOCK_SEQPACKET
- Provides a
220 | * sequenced, reliable, two-way connection-based data transmission path for datagrams of fixed maximum length; a
221 | * consumer is required to read an entire packet with each read call.
SOCK_RAW
- Provides raw
222 | * network protocol access. This special type of socket can be used to manually construct any type of protocol. A
223 | * common use for this socket type is to perform ICMP requests (like ping).
SOCK_RDM
-
224 | * Provides a reliable datagram layer that does not guarantee ordering. This is most likely not implemented on
225 | * your operating system.
The protocol parameter sets the specific protocol within the specified domain to be
227 | * used when communicating on the returned socket. The proper value can be retrieved by name by using
228 | * getprotobyname()
. If the desired protocol is TCP, or UDP the corresponding constants
229 | * SOL_TCP
, and SOL_UDP
can also be used.
Some of the common protocol types
230 | * icmp - The Internet Control Message Protocol is used primarily by gateways and hosts to report errors in 231 | * datagram communication. The "ping" command (present in most modern operating systems) is an example 232 | * application of ICMP.
udp - The User Datagram Protocol is a connectionless, unreliable, protocol with 233 | * fixed record lengths. Due to these aspects, UDP requires a minimum amount of protocol overhead.
tcp - 234 | * The Transmission Control Protocol is a reliable, connection based, stream oriented, full duplex protocol. TCP 235 | * guarantees that all data packets will be received in the order in which they were sent. If any packet is 236 | * somehow lost during communication, TCP will automatically retransmit the packet until the destination host 237 | * acknowledges that packet. For reliability and performance reasons, the TCP implementation itself decides the 238 | * appropriate octet boundaries of the underlying datagram communication layer. Therefore, TCP applications must 239 | * allow for the possibility of partial record transmission.
240 | * 241 | * @return Socket Returns a Socket object based on the successful creation of the php socket. 242 | * @throws SocketException If there is an error creating the php socket. 243 | * 244 | */ 245 | public static function create(int $domain, int $type, int $protocol): self 246 | { 247 | $return = @socket_create($domain, $type, $protocol); 248 | 249 | if ($return === false) { 250 | throw new SocketException(); 251 | } 252 | 253 | $socket = new self($return); 254 | $socket->domain = $domain; 255 | $socket->type = $type; 256 | $socket->protocol = $protocol; 257 | 258 | return $socket; 259 | } 260 | 261 | /** 262 | * Opens a socket on port to accept connections. 263 | * 264 | *Creates a new socket resource of type AF_INET
listening on all local interfaces on the given
265 | * port waiting for new connections.
The backlog parameter defines the maximum length the queue of pending connections may
269 | * grow to. SOMAXCONN
may be passed as the backlog parameter.
Creates two connected and indistinguishable sockets. This function is commonly used in IPC (InterProcess 297 | * Communication).
298 | * 299 | * @param int $domainThe domain parameter specifies the protocol family to be used by the socket. See
300 | * create()
for the full list.
The type parameter selects the type of communication to be used by the socket. See
302 | * create()
for the full list.
The protocol parameter sets the specific protocol within the specified domain to be
304 | * used when communicating on the returned socket. The proper value can be retrieved by name by using
305 | * getprotobyname()
. If the desired protocol is TCP, or UDP the corresponding constants
306 | * SOL_TCP
, and SOL_UDP
can also be used. See create()
for the full list
307 | * of supported protocols.
308 | *
309 | * @return Socket[] An array of Socket objects containing identical sockets.
310 | * @throws SocketException If the creation of the php sockets is not successful.
311 | *
312 | * @see Socket::create()
313 | *
314 | */
315 | public static function createPair(int $domain, int $type, int $protocol): array
316 | {
317 | $array = [];
318 | $return = @socket_create_pair($domain, $type, $protocol, $array);
319 |
320 | if ($return === false) {
321 | throw new SocketException();
322 | }
323 |
324 | $sockets = self::constructFromResources($array);
325 |
326 | foreach ($sockets as $socket) {
327 | $socket->domain = $domain;
328 | $socket->type = $type;
329 | $socket->protocol = $protocol;
330 | }
331 |
332 | return $sockets;
333 | }
334 |
335 | /**
336 | * Gets socket options.
337 | *
338 | *
Retrieves the value for the option specified by the optname parameter for the current socket.
339 | * 340 | * @param int $levelThe level parameter specifies the protocol level at which the option resides. For
341 | * example, to retrieve options at the socket level, a level parameter of SOL_SOCKET
would be used.
342 | * Other levels, such as TCP
, can be used by specifying the protocol number of that level. Protocol
343 | * numbers can be found by using the getprotobyname()
function.
344 | * @param int $optname
Available Socket Options
SO_DEBUG
- Reports whether debugging
345 | * information is being recorded. Returns int.
SO_BROADCAST
- Reports whether transmission of
346 | * broadcast messages is supported. Returns int.
SO_REUSERADDR
- Reports whether local
347 | * addresses can be reused. Returns int.
SO_KEEPALIVE
- Reports whether connections are kept
348 | * active with periodic transmission of messages. If the connected socket fails to respond to these messages, the
349 | * connection is broken and processes writing to that socket are notified with a SIGPIPE signal. Returns int.
SO_LINGER
- Reports whether the socket lingers on close()
if data is
351 | * present. By default, when the socket is closed, it attempts to send all unsent data. In the case of a
352 | * connection-oriented socket, close()
will wait for its peer to acknowledge the data. If
353 | * l_onoff
is non-zero and l_linger
is zero, all the unsent data will be discarded and
354 | * RST (reset) is sent to the peer in the case of a connection-oriented socket. On the other hand, if
355 | * l_onoff
is non-zero and l_linger
is non-zero, close()
will block until
356 | * all the data is sent or the time specified in l_linger
elapses. If the socket is non-blocking,
357 | * close()
will fail and return an error. Returns an array with two keps: l_onoff
and
358 | * l_linger
.
SO_OOBINLINE
- Reports whether the socket leaves out-of-band data
359 | * inline. Returns int.
SO_SNDBUF
- Reports the size of the send buffer. Returns int.
SO_RCVBUF
- Reports the size of the receive buffer. Returns int.
SO_ERROR
- Reports information about error status and clears it. Returns int.
SO_TYPE
- Reports the socket type (e.g. SOCK_STREAM
). Returns int.
SO_DONTROUTE
- Reports whether outgoing messages bypass the standard routing facilities.
364 | * Returns int.
SO_RCVLOWAT
- Reports the minimum number of bytes to process for socket input
365 | * operations. Returns int.
SO_RCVTIMEO
- Reports the timeout value for input operations.
366 | * Returns an array with two keys: sec
which is the seconds part on the timeout value and
367 | * usec
which is the microsecond part of the timeout value.
SO_SNDTIMEO
-
368 | * Reports the timeout value specifying the amount of time that an output function blocks because flow control
369 | * prevents data from being sent. Returns an array with two keys: sec
which is the seconds part on
370 | * the timeout value and usec
which is the microsecond part of the timeout value.
SO_SNDLOWAT
- Reports the minimum number of bytes to process for socket output operations.
372 | * Returns int.
TCP_NODELAY
- Reports whether the Nagle TCP algorithm is disabled. Returns
373 | * int.
IP_MULTICAST_IF
- The outgoing interface for IPv4 multicast packets. Returns the index
374 | * of the interface (int).
IPV6_MULTICAST_IF
- The outgoing interface for IPv6 multicast
375 | * packets. Returns the same thing as IP_MULTICAST_IF
.
IP_MULTICAST_LOOP
- The
376 | * multicast loopback policy for IPv4 packets, which determines whether multicast packets sent by this socket
377 | * also reach receivers in the same host that have joined the same multicast group on the outgoing interface used
378 | * by this socket. This is the case by default. Returns int.
IPV6_MULTICAST_LOOP
- Analogous
379 | * to IP_MULTICAST_LOOP
, but for IPv6. Returns int.
IP_MULTICAST_TTL
- The
380 | * time-to-live of outgoing IPv4 multicast packets. This should be a value between 0 (don't leave the interface)
381 | * and 255. The default value is 1 (only the local network is reached). Returns int.
382 | * IPV6_MULTICAST_HOPS
- Analogous to IP_MULTICAST_TTL
, but for IPv6 packets. The value -1
383 | * is also accepted, meaning the route default should be used. Returns int.
If the given socket is of type AF_INET
or AF_INET6
,
404 | * getPeerName()
will return the peers (remote) IP address in appropriate notation (e.g.
405 | * 127.0.0.1
or fe80::1
) in the address parameter and, if the optional port parameter
406 | * is present, also the associated port.
If the given socket is of type AF_UNIX
,
407 | * getPeerName()
will return the Unix filesystem path (e.g. /var/run/daemon.sock) in
408 | * the address parameter.
Returns true
if the retrieval of the peer name was successful.
If the retrieval of the peer name fails or if the socket type is not
413 | * AF_INET
, AF_INET6
, or AF_UNIX
.
Note: getSockName()
should not be used with AF_UNIX
sockets created with
431 | * connect()
. Only sockets created with accept()
or a primary server socket following a
432 | * call to bind()
will return meaningful values.
If the given socket is of type AF_INET
or AF_INET6
,
435 | * getSockName()
will return the local IP address in appropriate notation (e.g. 127.0.0.1
436 | * or fe80::1
) in the address parameter and, if the optional port parameter is present, also the
437 | * associated port.
If the given socket is of type AF_UNIX
, getSockName()
will
438 | * return the Unix filesystem path (e.g. /var/run/daemon.sock) in the address parameter.
Returns true
if the retrieval of the socket name was successful.
If the retrieval of the socket name fails or if the socket type is not
443 | * AF_INET
, AF_INET6
, or AF_UNIX
.
Imports a stream that encapsulates a socket into a socket extension resource.
464 | * 465 | * @param resource $stream The stream resource to import. 466 | * 467 | * @return Socket Returns a Socket object based on the stream. 468 | * @throws SocketException If the import of the stream is not successful. 469 | * 470 | */ 471 | public static function importStream($stream): self 472 | { 473 | if (get_resource_type($stream) === 'Unknown') { 474 | throw new InvalidArgumentException('$stream must be a resource'); 475 | } 476 | $return = @socket_import_stream($stream); 477 | 478 | // As of PHP 8, `$return` can only be {@see SocketResource} or `false` 479 | if ($return === false) { 480 | throw new SocketException(); 481 | } 482 | 483 | return new self($return); 484 | } 485 | 486 | /** 487 | * Listens for a connection on a socket. 488 | * 489 | *After the socket has been created using create()
and bound to a name with bind()
,
490 | * it may be told to listen for incoming connections on socket.
A maximum of backlog incoming connections will be queued for processing. If a 493 | * connection request arrives with the queue full the client may receive an error with an indication of 494 | * ECONNREFUSED, or, if the underlying protocol supports retransmission, the request may be ignored so that 495 | * retries may succeed.
Note: The maximum number passed to the backlog parameter highly depends on
496 | * the underlying platform. On Linux, it is silently truncated to SOMAXCONN
. On win32, if passed
497 | * SOMAXCONN
, the underlying service provider responsible for the socket will set the backlog to a
498 | * maximum reasonable value. There is no standard provision to find out the actual backlog value on this platform.
499 | *
true
on success.
502 | * @throws SocketException If the listen fails.
503 | *
504 | */
505 | public function listen(int $backlog = 0): bool
506 | {
507 | return static::exceptionOnFalse(
508 | $this->resource,
509 | static function ($resource) use ($backlog) {
510 | return @socket_listen($resource, $backlog);
511 | }
512 | );
513 | }
514 |
515 | /**
516 | * reads a maximum of length bytes from a socket.
517 | *
518 | * Reads from the socket created by the create()
or accept()
functions.
The maximum number of bytes read is specified by the length parameter. Otherwise you can
521 | * use \r
, \n
, or \0
to end reading (depending on the type parameter, see
522 | * below).
(Optional) type parameter is a named constant:
PHP_BINARY_READ
524 | * (Default) - use the system recv()
function. Safe for reading binary data.PHP_NORMAL_READ
- reading stops at \n
or \r
.Receives length bytes of data in buffer from the socket. receive()
can be used to gather data
549 | * from connected sockets. Additionally, one or more flags can be specified to modify the behaviour of the
550 | * function.
buffer is passed by reference, so it must be specified as a variable in the argument list. Data
551 | * read from socket by receive()
will be returned in buffer.
The data received will be fetched to the variable specified with buffer. If an error
554 | * occurs, if the connection is reset, or if no data is available, buffer will be set to NULL
.
The value of flags can be any combination of the following flags, joined with the binary
557 | * OR (|
) operator.
MSG_OOB
- Process out-of-band data.MSG_PEEK
- Receive data from the beginning of the receive queue without removing it
559 | * from the queue.MSG_WAITALL
- Block until at least length are received. However, if a
560 | * signal is caught or the remote host disconnects, the function may return less data.MSG_DONTWAIT
- With this flag set, the function returns even if it would normally have blocked.
562 | * accepts arrays of sockets and waits for them to change status. Those coming with BSD sockets background will 582 | * recognize that those socket resource arrays are in fact the so-called file descriptor sets. Three independent 583 | * arrays of socket resources are watched.
WARNING: On exit, the arrays are modified to indicate which 584 | * socket resource actually changed status.
ou do not need to pass every array to select()
. You
585 | * can leave it out and use an empty array or NULL
instead. Also do not forget that those arrays are
586 | * passed by reference and will be modified after select()
returns.
587 | *
588 | * @param Socket[] &$read
The sockets listed in the read array will be watched to see if characters become
589 | * available for reading (more precisely, to see if a read will not block - in particular, a socket resource is
590 | * also ready on end-of-file, in which case a read()
will return a zero length string).
select()
595 | * returns. timeoutSeconds may be zero, causing the select()
to return immediately. This is useful
596 | * for polling. If timeoutSeconds is NULL
(no timeout), the select()
can block
597 | * indefinitely.
598 | * @param int $timeoutMilliseconds See the description for timeoutSeconds.
599 | *
600 | * @return int Returns the number of socket resources contained in the modified arrays, which may be zero if the
601 | * timeout expires before anything interesting happens.
602 | * @throws SocketException If there was an error.
603 | *
604 | */
605 | public static function select(
606 | array &$read,
607 | array &$write,
608 | array &$except,
609 | ?int $timeoutSeconds,
610 | int $timeoutMilliseconds = 0
611 | ): int {
612 | $readSockets = self::mapClassToRawSocket($read);
613 | $writeSockets = self::mapClassToRawSocket($write);
614 | $exceptSockets = self::mapClassToRawSocket($except);
615 |
616 | $return = @socket_select(
617 | $readSockets,
618 | $writeSockets,
619 | $exceptSockets,
620 | $timeoutSeconds,
621 | $timeoutMilliseconds
622 | );
623 |
624 | if ($return === false) {
625 | throw new SocketException();
626 | }
627 |
628 | $read = [];
629 | $write = [];
630 | $except = [];
631 |
632 | if ($readSockets) {
633 | $read = static::mapRawSocketToClass($readSockets);
634 | }
635 | if ($writeSockets) {
636 | $write = static::mapRawSocketToClass($writeSockets);
637 | }
638 | if ($exceptSockets) {
639 | $except = static::mapRawSocketToClass($exceptSockets);
640 | }
641 |
642 | return $return;
643 | }
644 |
645 | /**
646 | * Maps an array of Sockets to an array of socket resources.
647 | *
648 | * @param Socket[] $sockets An array of sockets to map.
649 | *
650 | * @return SocketResource[] Returns the corresponding array of resources.
651 | */
652 | protected static function mapClassToRawSocket(array $sockets): array
653 | {
654 | return array_filter(
655 | array_map(
656 | static function (Socket $socket) {
657 | return $socket->resource;
658 | },
659 | $sockets
660 | )
661 | );
662 | }
663 |
664 | /**
665 | * Maps an array of socket resources to an array of Sockets.
666 | *
667 | * @param SocketResource[] $sockets An array of socket resources to map.
668 | *
669 | * @return Socket[] Returns the corresponding array of Socket objects.
670 | */
671 | protected static function mapRawSocketToClass(array $sockets): array
672 | {
673 | return array_map(
674 | static function ($rawSocket) {
675 | return self::$map[spl_object_hash($rawSocket)];
676 | },
677 | $sockets
678 | );
679 | }
680 |
681 | /**
682 | * Performs the closure function. If it returns false, throws a SocketException using the provided resource.
683 | *
684 | * @template T
685 | * @param ?SocketResource $resource Socket Resource
686 | * @param callable(SocketResource):T $closure A function that takes 1 parameter (a socket resource)
687 | * @return T
688 | *
689 | * @throws SocketException
690 | */
691 | protected static function exceptionOnFalse(?SocketResource $resource, callable $closure): mixed
692 | {
693 | if ($resource === null) {
694 | throw new SocketException('Socket is not connected');
695 | }
696 |
697 | $result = $closure($resource);
698 |
699 | if ($result === false) {
700 | throw new SocketException($resource);
701 | }
702 |
703 | return $result;
704 | }
705 |
706 | /**
707 | * Write to a socket.
708 | *
709 | * The function write()
writes to the socket from the given buffer.
Sends length bytes to the socket from buffer.
748 | * 749 | * @param string $buffer A buffer containing the data that will be sent to the remote host. 750 | * @param int $flagsThe value of flags can be any combination of the following flags, joined with the binary
751 | * OR (|
) operator.
MSG_OOB
- Send OOB (out-of-band) data.MSG_EOR
- Indicate a record mark. The sent data completes the record.MSG_EOF
- Close the sender side of the socket and include an appropriate notification
754 | * of this at the end of the sent data. The sent data completes the transaction.MSG_DONTROUTE
- Bypass routing, use direct interface.Removes (blocking) or set (non blocking) the O_NONBLOCK
flag on the socket.
When an 791 | * operation is performed on a blocking socket, the script will pause its execution until it receives a signal or it 792 | * can perform the operation.
When an operation is performed on a non-blocking socket, the script will not 793 | * pause its execution until it receives a signal or it can perform the operation. Rather, if the operation would 794 | * result in a block, the called function will fail.
795 | * 796 | * @param bool $bool Flag to indicate if the Socket should block (true
) or not block
797 | * (false
).
798 | * @throws SocketException
799 | */
800 | public function setBlocking(bool $bool): void
801 | {
802 | if ($bool) {
803 | @socket_set_block($this->resource);
804 | } else {
805 | @socket_set_nonblock($this->resource);
806 | }
807 | }
808 | }
809 |
--------------------------------------------------------------------------------