├── 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 | [![Latest Stable Version](http://poser.pugx.org/navarr/Sockets/v)](https://packagist.org/packages/navarr/Sockets) 4 | [![Total Downloads](http://poser.pugx.org/navarr/Sockets/downloads)](https://packagist.org/packages/navarr/Sockets) 5 | [![Latest Unstable Version](http://poser.pugx.org/navarr/Sockets/v/unstable)](https://packagist.org/packages/navarr/Sockets) 6 | [![License](http://poser.pugx.org/navarr/Sockets/license)](https://packagist.org/packages/navarr/Sockets) 7 | ![Tests](https://github.com/navarr/Sockets/actions/workflows/commit.yml/badge.svg) 8 | ![Code Coverage](https://codecov.io/gh/navarr/Sockets/branch/main/graph/badge.svg?token=C9DtrzMCrD) 9 | [![Mutation Score](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fnavarr%2FSockets%2Fmain)](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 | 3 | 4 | src 5 | 6 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | treatPhpDocTypesAsCertain: true 3 | level: 9 4 | paths: 5 | - src 6 | ignoreErrors: 7 | - message: '#should return (string|int) but returns bool\|#' 8 | path: src/Socket.php 9 | -------------------------------------------------------------------------------- /src/Exception/SocketException.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | protected array $hooks = []; 17 | 18 | /** 19 | * IP Address. 20 | * 21 | * @var string 22 | */ 23 | protected string $address; 24 | 25 | /** 26 | * Port Number. 27 | * 28 | * @var int 29 | */ 30 | protected int $port; 31 | 32 | /** 33 | * Seconds to wait on a socket before timing out. 34 | * 35 | * @var int|null 36 | */ 37 | protected ?int $timeout = null; 38 | 39 | /** 40 | * Domain. 41 | * 42 | * @see http://php.net/manual/en/function.socket-create.php 43 | * 44 | * @var int One of AF_INET, AF_INET6, AF_UNIX 45 | */ 46 | protected int $domain; 47 | 48 | /** 49 | * The Master Socket. 50 | * 51 | * @var ?Socket 52 | */ 53 | protected ?Socket $masterSocket = null; 54 | 55 | /** 56 | * Maximum Amount of Clients Allowed to Connect. 57 | * 58 | * @var int 59 | */ 60 | protected int $maxClients = PHP_INT_MAX; 61 | 62 | /** 63 | * Maximum amount of characters to read in from a socket at once 64 | * This integer is passed directly to socket_read. 65 | * 66 | * @var int 67 | */ 68 | protected int $maxRead = 1024; 69 | 70 | /** 71 | * Connected Clients. 72 | * 73 | * @var Socket[] 74 | */ 75 | protected array $clients = []; 76 | 77 | /** 78 | * Type of Read to use. One of PHP_BINARY_READ, PHP_NORMAL_READ. 79 | * 80 | * @var int 81 | */ 82 | protected int $readType = PHP_BINARY_READ; 83 | 84 | /** 85 | * Constant String for Generic Connection Hook. 86 | */ 87 | public const HOOK_CONNECT = '__NAVARR_SOCKET_SERVER_CONNECT__'; 88 | 89 | /** 90 | * Constant String for Generic Input Hook. 91 | */ 92 | public const HOOK_INPUT = '__NAVARR_SOCKET_SERVER_INPUT__'; 93 | 94 | /** 95 | * Constant String for Generic Disconnect Hook. 96 | */ 97 | public const HOOK_DISCONNECT = '__NAVARR_SOCKET_SERVER_DISCONNECT__'; 98 | 99 | /** 100 | * Constant String for Server Timeout. 101 | */ 102 | public const HOOK_TIMEOUT = '__NAVARR_SOCKET_SERVER_TIMEOUT__'; 103 | 104 | /** 105 | * Return value from a hook callable to tell the server not to run the other hooks. 106 | */ 107 | public const RETURN_HALT_HOOK = false; 108 | 109 | /** 110 | * Return value from a hook callable to tell the server to halt operations. 111 | */ 112 | public const RETURN_HALT_SERVER = '__NAVARR_HALT_SERVER__'; 113 | 114 | /** 115 | * Setup the configuration for the server 116 | * 117 | * @param string $address An IPv4, IPv6, or Unix socket address 118 | * @param int $port 119 | * @param ?int $timeout Seconds to wait on a socket before timing it out 120 | * @throws SocketException 121 | */ 122 | public function __construct(string $address, int $port = 0, ?int $timeout = 0) 123 | { 124 | $this->address = $address; 125 | $this->port = $port; 126 | $this->timeout = $timeout; 127 | 128 | switch (true) { 129 | case filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4): 130 | $this->domain = AF_INET; 131 | break; 132 | case filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6): 133 | $this->domain = AF_INET6; 134 | break; 135 | default: 136 | $this->domain = AF_UNIX; 137 | } 138 | } 139 | 140 | /** 141 | * Start the server, binding to ports and listening for connections. 142 | * 143 | * If you call {@see run} you do not need to call this method. 144 | * 145 | * @throws SocketException 146 | */ 147 | public function start(): void 148 | { 149 | set_time_limit(0); 150 | $this->masterSocket = Socket::create($this->domain, SOCK_STREAM, 0); 151 | $this->masterSocket->bind($this->address, $this->port); 152 | $this->masterSocket->getSockName($this->address, $this->port); 153 | $this->masterSocket->listen(); 154 | } 155 | 156 | public function __destruct() 157 | { 158 | $this->shutDownEverything(); 159 | } 160 | 161 | /** 162 | * Run the Server for as long as loopOnce returns true. 163 | * 164 | * @throws SocketException 165 | * @see loopOnce 166 | */ 167 | public function run(): void 168 | { 169 | if ($this->masterSocket === null) { 170 | $this->start(); 171 | } 172 | 173 | do { 174 | $test = $this->loopOnce(); 175 | } while ($test); 176 | 177 | $this->shutDownEverything(); 178 | } 179 | 180 | /** 181 | * This is the main server loop. This code is responsible for adding connections and triggering hooks. 182 | * 183 | * @return bool Whether or not to shutdown the server 184 | * @throws SocketException 185 | */ 186 | protected function loopOnce(): bool 187 | { 188 | if ($this->masterSocket === null) { 189 | throw new RuntimeException('Socket must be started before running server loop'); 190 | } 191 | 192 | // Get all the Sockets we should be reading from 193 | $read = array_merge([$this->masterSocket], $this->clients); 194 | 195 | // Set up a block call to socket_select 196 | $write = []; 197 | $except = []; 198 | $ret = Socket::select($read, $write, $except, $this->timeout); 199 | if ( 200 | !is_null($this->timeout) 201 | && $ret === 0 202 | && $this->triggerHooks(self::HOOK_TIMEOUT, $this->masterSocket) === false 203 | ) { 204 | // This only happens when a hook tells the server to shut itself down. 205 | return false; 206 | } 207 | 208 | // If there is a new connection, add it 209 | if ($this->masterSocket !== null && in_array($this->masterSocket, $read)) { 210 | unset($read[array_search($this->masterSocket, $read)]); 211 | $socket = $this->masterSocket->accept(); 212 | $this->clients[] = $socket; 213 | 214 | if ($this->triggerHooks(self::HOOK_CONNECT, $socket) === false) { 215 | // This only happens when a hook tells the server to shut itself down. 216 | return false; 217 | } 218 | unset($socket); 219 | } 220 | 221 | // Check for input from each client 222 | foreach ($read as $client) { 223 | $input = $this->read($client); 224 | 225 | if ($input === '') { 226 | if ($this->disconnect($client) === false) { 227 | // This only happens when a hook tells the server to shut itself down. 228 | return false; 229 | } 230 | } elseif ($this->triggerHooks(self::HOOK_INPUT, $client, $input) === false) { 231 | // This only happens when a hook tells the server to shut itself down. 232 | return false; 233 | } 234 | unset($input); 235 | } 236 | 237 | // Unset the variables we were holding on to 238 | unset($read, $write, $except); 239 | 240 | // Tells self::run to Continue the Loop 241 | return true; 242 | } 243 | 244 | /** 245 | * Overrideable Read Functionality. 246 | * 247 | * @param Socket $client 248 | * @throws SocketException 249 | */ 250 | protected function read(Socket $client): string 251 | { 252 | return $client->read($this->maxRead, $this->readType); 253 | } 254 | 255 | /** 256 | * Disconnect the supplied Client Socket. 257 | * 258 | * @param Socket $client 259 | * @param string $message Disconnection Message. Could be used to trigger a disconnect with a status code 260 | * 261 | * @return bool Whether or not to continue running the server (true: continue, false: shutdown) 262 | */ 263 | public function disconnect(Socket $client, string $message = ''): bool 264 | { 265 | $clientIndex = array_search($client, $this->clients); 266 | $return = $this->triggerHooks( 267 | self::HOOK_DISCONNECT, 268 | $this->clients[$clientIndex], 269 | $message 270 | ); 271 | 272 | $this->clients[$clientIndex]->close(); 273 | unset($this->clients[$clientIndex], $client); 274 | 275 | if ($return === false) { 276 | return false; 277 | } 278 | 279 | unset($return); 280 | 281 | return true; 282 | } 283 | 284 | /** 285 | * Triggers the hooks for the supplied command. 286 | * 287 | * @param string $command Hook to listen for (e.g. HOOK_CONNECT, HOOK_INPUT, HOOK_DISCONNECT, HOOK_TIMEOUT) 288 | * @param Socket $client 289 | * @param string|null $input Message Sent along with the Trigger 290 | * 291 | * @return bool Whether or not to continue running the server (true: continue, false: shutdown) 292 | */ 293 | protected function triggerHooks(string $command, Socket $client, ?string $input = null): bool 294 | { 295 | if (isset($this->hooks[$command])) { 296 | foreach ($this->hooks[$command] as $callable) { 297 | $continue = $callable($this, $client, $input); 298 | 299 | if ($continue === self::RETURN_HALT_HOOK) { 300 | break; 301 | } 302 | if ($continue === self::RETURN_HALT_SERVER) { 303 | return false; 304 | } 305 | unset($continue); 306 | } 307 | } 308 | 309 | return true; 310 | } 311 | 312 | /** 313 | * Attach a Listener to a Hook. 314 | * 315 | * @param string $command Hook to listen for 316 | * @param callable $callable A callable with the signature (Server, Socket, string). Callable should return false 317 | * if it wishes to stop the server, and true if it wishes to continue. 318 | * 319 | * @return void 320 | */ 321 | public function addHook(string $command, callable $callable): void 322 | { 323 | if (!isset($this->hooks[$command])) { 324 | $this->hooks[$command] = []; 325 | } else { 326 | $k = array_search($callable, $this->hooks[$command]); 327 | if ($k !== false) { 328 | return; 329 | } 330 | unset($k); 331 | } 332 | 333 | $this->hooks[$command][] = $callable; 334 | } 335 | 336 | /** 337 | * Remove the provided Callable from the provided Hook. 338 | * 339 | * @param string $command Hook to remove callable from 340 | * @param callable $callable The callable to be removed 341 | * 342 | * @return void 343 | */ 344 | public function removeHook(string $command, callable $callable): void 345 | { 346 | if (isset($this->hooks[$command]) && in_array($callable, $this->hooks[$command])) { 347 | $hook = array_search($callable, $this->hooks[$command]); 348 | unset($this->hooks[$command][$hook], $hook); 349 | } 350 | } 351 | 352 | /** 353 | * Disconnect all the Clients and shut down the server. 354 | * 355 | * @return void 356 | */ 357 | private function shutDownEverything(): void 358 | { 359 | foreach ($this->clients as $client) { 360 | $this->disconnect($client); 361 | } 362 | try { 363 | $this->masterSocket?->close(); 364 | } catch (Error $e) { 365 | // Haven't solved this one yet, but harmless. 366 | if (!str_contains($e->getMessage(), 'must not be accessed before initialization')) { 367 | throw $e; 368 | } 369 | } 370 | unset( 371 | $this->hooks, 372 | $this->address, 373 | $this->port, 374 | $this->timeout, 375 | $this->domain, 376 | $this->masterSocket, 377 | $this->maxClients, 378 | $this->maxRead, 379 | $this->clients, 380 | $this->readType 381 | ); 382 | } 383 | } 384 | -------------------------------------------------------------------------------- /src/Socket.php: -------------------------------------------------------------------------------- 1 | A simple wrapper for PHP's socket functions.

17 | */ 18 | class Socket implements Stringable 19 | { 20 | /** 21 | * @var int Should be set to one of the php predefined constants for Sockets - AF_UNIX, AF_INET, or AF_INET6 22 | */ 23 | protected int $domain; 24 | 25 | /** 26 | * @var int Should be set to one of the php predefined constants for Sockets - SOCK_STREAM, SOCK_DGRAM, 27 | * SOCK_SEQPACKET, SOCK_RAW, SOCK_RDM 28 | */ 29 | protected int $type; 30 | 31 | /** 32 | * @var int Should be set to the protocol number to be used. Can use getprotobyname to get the value. 33 | * Alternatively, there are two predefined constants for Sockets that could be used - SOL_TCP, SOL_UDP 34 | */ 35 | protected int $protocol; 36 | 37 | /** 38 | * @var array An internal storage of php socket resources and their associated Socket object. 39 | */ 40 | protected static array $map = []; 41 | 42 | /** 43 | * Sets up the Socket Resource and stores it in the local map. 44 | * 45 | *

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.

78 | * 79 | * @return string The string representation of the resource or an empty string if the resource was null. 80 | */ 81 | public function __toString(): string 82 | { 83 | return spl_object_hash($this->resource); 84 | } 85 | 86 | /** 87 | * Accept a connection. 88 | * 89 | *

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.

95 | * 96 | *

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().

124 | * 125 | * @param string $address

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).

128 | * @param int $port

(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.

171 | * @param int $port

(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 Returns true 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 $domain

The 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).

215 | * @param int $type

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.

226 | * @param int $protocol

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.

266 | * 267 | * @param int $port The port on which to listen on all interfaces. 268 | * @param int $backlog

The backlog parameter defines the maximum length the queue of pending connections may 269 | * grow to. SOMAXCONN may be passed as the backlog parameter.

270 | * 271 | * @return Socket Returns a Socket object based on the successful creation of the php socket. 272 | * @throws SocketException If the socket is not successfully created. 273 | * 274 | * @see Socket::bind() 275 | * @see Socket::listen() 276 | * 277 | * @see Socket::create() 278 | */ 279 | public static function createListen(int $port, int $backlog = 128): self 280 | { 281 | $return = @socket_create_listen($port, $backlog); 282 | 283 | if ($return === false) { 284 | throw new SocketException(); 285 | } 286 | 287 | $socket = new self($return); 288 | $socket->domain = AF_INET; 289 | 290 | return $socket; 291 | } 292 | 293 | /** 294 | * Creates a pair of indistinguishable sockets and stores them in an array. 295 | * 296 | *

Creates two connected and indistinguishable sockets. This function is commonly used in IPC (InterProcess 297 | * Communication).

298 | * 299 | * @param int $domain

The domain parameter specifies the protocol family to be used by the socket. See 300 | * create() for the full list.

301 | * @param int $type

The type parameter selects the type of communication to be used by the socket. See 302 | * create() for the full list.

303 | * @param int $protocol

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 $level

The 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.

350 | *

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.

360 | *

SO_RCVBUF - Reports the size of the receive buffer. Returns int.

361 | *

SO_ERROR - Reports information about error status and clears it. Returns int.

362 | *

SO_TYPE - Reports the socket type (e.g. SOCK_STREAM). Returns int.

363 | *

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.

371 | *

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.

384 | * 385 | * @return mixed See the descriptions based on the option being requested above. 386 | * @throws SocketException If there was an error retrieving the option. 387 | * 388 | */ 389 | public function getOption(int $level, int $optname): mixed 390 | { 391 | return static::exceptionOnFalse( 392 | $this->resource, 393 | static function ($resource) use ($level, $optname) { 394 | return @socket_get_option($resource, $level, $optname); 395 | } 396 | ); 397 | } 398 | 399 | /** 400 | * Queries the remote side of the given socket which may either result in host/port or in a Unix filesystem 401 | * path, dependent on its type. 402 | * 403 | * @param string $address

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.

409 | * @param int $port (Optional) If given, this will hold the port associated to the address. 410 | * 411 | * @return bool

Returns true if the retrieval of the peer name was successful.

412 | * @throws SocketException

If the retrieval of the peer name fails or if the socket type is not 413 | * AF_INET, AF_INET6, or AF_UNIX.

414 | * 415 | */ 416 | public function getPeerName(string &$address, int &$port): bool 417 | { 418 | return static::exceptionOnFalse( 419 | $this->resource, 420 | static function ($resource) use (&$address, &$port) { 421 | return @socket_getpeername($resource, $address, $port); 422 | } 423 | ); 424 | } 425 | 426 | /** 427 | * Queries the local side of the given socket which may either result in host/port or in a Unix filesystem path, 428 | * dependent on its type. 429 | * 430 | *

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.

433 | * 434 | * @param string $address

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.

439 | * @param int $port If provided, this will hold the associated port. 440 | * 441 | * @return bool

Returns true if the retrieval of the socket name was successful.

442 | * @throws SocketException

If the retrieval of the socket name fails or if the socket type is not 443 | * AF_INET, AF_INET6, or AF_UNIX.

444 | * 445 | */ 446 | public function getSockName(string &$address, int &$port): bool 447 | { 448 | if (!in_array($this->domain, [AF_UNIX, AF_INET, AF_INET6])) { 449 | return false; 450 | } 451 | 452 | return static::exceptionOnFalse( 453 | $this->resource, 454 | static function ($resource) use (&$address, &$port) { 455 | return @socket_getsockname($resource, $address, $port); 456 | } 457 | ); 458 | } 459 | 460 | /** 461 | * Imports a stream. 462 | * 463 | *

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.

491 | * 492 | * @param int $backlog

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 | *

500 | * 501 | * @return bool Returns 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.

519 | * 520 | * @param int $length

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).

523 | * @param int $type

(Optional) type parameter is a named constant:

  • PHP_BINARY_READ 524 | * (Default) - use the system recv() function. Safe for reading binary data.
  • 525 | * PHP_NORMAL_READ - reading stops at \n or \r.

526 | * 527 | * @return string Returns the data as a string. Returns a zero length string ("") when there is no more data to 528 | * read. 529 | * @throws SocketException If there was an error reading or if the host closed the connection. 530 | * 531 | * @see Socket::accept() 532 | * 533 | * @see Socket::create() 534 | */ 535 | public function read(int $length, int $type = PHP_BINARY_READ): string 536 | { 537 | return static::exceptionOnFalse( 538 | $this->resource, 539 | static function ($resource) use ($length, $type) { 540 | return @socket_read($resource, $length, $type); 541 | } 542 | ); 543 | } 544 | 545 | /** 546 | * Receives data from a connected socket. 547 | * 548 | *

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.

552 | * 553 | * @param string $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.

555 | * @param int $length Up to length bytes will be fetched from remote host. 556 | * @param int $flags

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.
  • 558 | *
  • 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.
  • 561 | *
  • MSG_DONTWAIT - With this flag set, the function returns even if it would normally have blocked. 562 | *

563 | * 564 | * @return int Returns the number of bytes received. 565 | * @throws SocketException If there was an error receiving data. 566 | * 567 | */ 568 | public function receive(string &$buffer, int $length, int $flags): int 569 | { 570 | return static::exceptionOnFalse( 571 | $this->resource, 572 | static function ($resource) use (&$buffer, $length, $flags) { 573 | return @socket_recv($resource, $buffer, $length, $flags); 574 | } 575 | ); 576 | } 577 | 578 | /** 579 | * Runs the select() system call on the given arrays of sockets with a specified timeout. 580 | * 581 | *

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).

591 | * @param Socket[] &$write The sockets listed in the write array will be watched to see if a write will not block. 592 | * @param Socket[] &$except he sockets listed in the except array will be watched for exceptions. 593 | * @param ?int $timeoutSeconds The seconds portion of the timeout parameters (in conjunction with 594 | * timeoutMilliseconds). The timeout is an upper bound on the amount of time elapsed before 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.

710 | * 711 | * @param string $buffer The buffer to be written. 712 | * @param int|null $length The optional parameter length can specify an alternate length of bytes written to the 713 | * socket. If this length is greater than the buffer length, it is silently truncated to the length of the buffer. 714 | * 715 | * @return int Returns the number of bytes successfully written to the socket. 716 | * @throws SocketException If there was a failure. 717 | * 718 | */ 719 | public function write(string $buffer, ?int $length = null): int 720 | { 721 | if (null === $length) { 722 | $length = strlen($buffer); 723 | } 724 | 725 | // make sure everything is written 726 | do { 727 | $return = @socket_write($this->resource, $buffer, $length); 728 | 729 | if (false !== $return && $return < $length) { 730 | $buffer = substr($buffer, $return); 731 | $length -= $return; 732 | } else { 733 | break; 734 | } 735 | } while (true); 736 | 737 | if ($return === false) { 738 | throw new SocketException($this->resource); 739 | } 740 | 741 | return $return; 742 | } 743 | 744 | /** 745 | * Sends data to a connected socket. 746 | * 747 | *

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 $flags

The 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.
  • 752 | * MSG_EOR - Indicate a record mark. The sent data completes the record.
  • 753 | *
  • 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.
  • 755 | *
  • MSG_DONTROUTE - Bypass routing, use direct interface.

756 | * @param int|null $length The number of bytes that will be sent to the remote host from buffer. 757 | * 758 | * @return int Returns the number of bytes sent. 759 | * @throws SocketException If there was a failure. 760 | * 761 | */ 762 | public function send(string $buffer, int $flags = 0, ?int $length = null): int 763 | { 764 | if (null === $length) { 765 | $length = strlen($buffer); 766 | } 767 | 768 | // make sure everything is written 769 | do { 770 | $return = @socket_send($this->resource, $buffer, $length, $flags); 771 | 772 | if (false !== $return && $return < $length) { 773 | $buffer = substr($buffer, $return); 774 | $length -= $return; 775 | } else { 776 | break; 777 | } 778 | } while (true); 779 | 780 | if ($return === false) { 781 | throw new SocketException($this->resource); 782 | } 783 | 784 | return $return; 785 | } 786 | 787 | /** 788 | * Set the socket to blocking / non blocking. 789 | * 790 | *

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 | --------------------------------------------------------------------------------