代码同步

This commit is contained in:
Ghost
2025-03-24 14:59:19 +08:00
parent f0fa19f89f
commit bd2e1e5386
716 changed files with 90318 additions and 26155 deletions

View File

@@ -0,0 +1,143 @@
[Client](Client.md) • [Server](Server.md) • [Message](Message.md) • [Examples](Examples.md) • Changelog • [Contributing](Contributing.md)
# Websocket: Changelog
## `v1.5`
> PHP version `^7.2|^8.0`
### `1.5.8`
* Handle read error during handshake (@sirn-se)
### `1.5.7`
* Large header block fix (@sirn-se)
### `1.5.6`
* Add test for PHP 8.1 (@sirn-se)
* Code standard (@sirn-se)
### `1.5.5`
* Support for psr/log v2 and v3 (@simPod)
* GitHub Actions replaces Travis (@sirn-se)
### `1.5.4`
* Keep open connection on read timeout (@marcroberts)
### `1.5.3`
* Fix for persistent connection (@sirn-se)
### `1.5.2`
* Fix for getName() method (@sirn-se)
### `1.5.1`
* Fix for persistent connections (@rmeisler)
### `1.5.0`
* Convenience send methods; text(), binary(), ping(), pong() (@sirn-se)
* Optional Message instance as receive() method return (@sirn-se)
* Opcode filter for receive() method (@sirn-se)
* Added PHP `8.0` support (@webpatser)
* Dropped PHP `7.1` support (@sirn-se)
* Fix for unordered fragmented messages (@sirn-se)
* Improved error handling on stream calls (@sirn-se)
* Various code re-write (@sirn-se)
## `v1.4`
> PHP version `^7.1`
#### `1.4.3`
* Solve stream closure/get meta conflict (@sirn-se)
* Examples and documentation overhaul (@sirn-se)
#### `1.4.2`
* Force stream close on read error (@sirn-se)
* Authorization headers line feed (@sirn-se)
* Documentation (@matias-pool, @sirn-se)
#### `1.4.1`
* Ping/Pong, handled internally to avoid breaking fragmented messages (@nshmyrev, @sirn-se)
* Fix for persistent connections (@rmeisler)
* Fix opcode bitmask (@peterjah)
#### `1.4.0`
* Dropped support of old PHP versions (@sirn-se)
* Added PSR-3 Logging support (@sirn-se)
* Persistent connection option (@slezakattack)
* TimeoutException on connection time out (@slezakattack)
## `v1.3`
> PHP version `^5.4` and `^7.0`
#### `1.3.1`
* Allow control messages without payload (@Logioniz)
* Error code in ConnectionException (@sirn-se)
#### `1.3.0`
* Implements ping/pong frames (@pmccarren @Logioniz)
* Close behaviour (@sirn-se)
* Various fixes concerning connection handling (@sirn-se)
* Overhaul of Composer, Travis and Coveralls setup, PSR code standard and unit tests (@sirn-se)
## `v1.2`
> PHP version `^5.4` and `^7.0`
#### `1.2.0`
* Adding stream context options (to set e.g. SSL `allow_self_signed`).
## `v1.1`
> PHP version `^5.4` and `^7.0`
#### `1.1.2`
* Fixed error message on broken frame.
#### `1.1.1`
* Adding license information.
#### `1.1.0`
* Supporting huge payloads.
## `v1.0`
> PHP version `^5.4` and `^7.0`
#### `1.0.3`
* Bugfix: Correcting address in error-message
#### `1.0.2`
* Bugfix: Add port in request-header.
#### `1.0.1`
* Fixing a bug from empty payloads.
#### `1.0.0`
* Release as production ready.
* Adding option to set/override headers.
* Supporting basic authentication from user:pass in URL.

View File

@@ -0,0 +1,137 @@
Client • [Server](Server.md) • [Message](Message.md) • [Examples](Examples.md) • [Changelog](Changelog.md) • [Contributing](Contributing.md)
# Websocket: Client
The client can read and write on a WebSocket stream.
It internally supports Upgrade handshake and implicit close and ping/pong operations.
## Class synopsis
```php
WebSocket\Client {
public __construct(string $uri, array $options = [])
public __destruct()
public __toString() : string
public text(string $payload) : void
public binary(string $payload) : void
public ping(string $payload = '') : void
public pong(string $payload = '') : void
public send(mixed $payload, string $opcode = 'text', bool $masked = true) : void
public receive() : mixed
public close(int $status = 1000, mixed $message = 'ttfn') : mixed
public getName() : string|null
public getPier() : string|null
public getLastOpcode() : string
public getCloseStatus() : int
public isConnected() : bool
public setTimeout(int $seconds) : void
public setFragmentSize(int $fragment_size) : self
public getFragmentSize() : int
public setLogger(Psr\Log\LoggerInterface $logger = null) : void
}
```
## Examples
### Simple send-receive operation
This example send a single message to a server, and output the response.
```php
$client = new WebSocket\Client("ws://echo.websocket.org/");
$client->text("Hello WebSocket.org!");
echo $client->receive();
$client->close();
```
### Listening to a server
To continuously listen to incoming messages, you need to put the receive operation within a loop.
Note that these functions **always** throw exception on any failure, including recoverable failures such as connection time out.
By consuming exceptions, the code will re-connect the socket in next loop iteration.
```php
$client = new WebSocket\Client("ws://echo.websocket.org/");
while (true) {
try {
$message = $client->receive();
// Act on received message
// Break while loop to stop listening
} catch (\WebSocket\ConnectionException $e) {
// Possibly log errors
}
}
$client->close();
```
### Filtering received messages
By default the `receive()` method return messages of 'text' and 'binary' opcode.
The filter option allows you to specify which message types to return.
```php
$client = new WebSocket\Client("ws://echo.websocket.org/", ['filter' => ['text']]);
$client->receive(); // Only return 'text' messages
$client = new WebSocket\Client("ws://echo.websocket.org/", ['filter' => ['text', 'binary', 'ping', 'pong', 'close']]);
$client->receive(); // Return all messages
```
### Sending messages
There are convenience methods to send messages with different opcodes.
```php
$client = new WebSocket\Client("ws://echo.websocket.org/");
// Convenience methods
$client->text('A plain text message'); // Send an opcode=text message
$client->binary($binary_string); // Send an opcode=binary message
$client->ping(); // Send an opcode=ping frame
$client->pong(); // Send an unsolicited opcode=pong frame
// Generic send method
$client->send($payload); // Sent as masked opcode=text
$client->send($payload, 'binary'); // Sent as masked opcode=binary
$client->send($payload, 'binary', false); // Sent as unmasked opcode=binary
```
## Constructor options
The `$options` parameter in constructor accepts an associative array of options.
* `context` - A stream context created using [stream_context_create](https://www.php.net/manual/en/function.stream-context-create).
* `filter` - Array of opcodes to return on receive, default `['text', 'binary']`
* `fragment_size` - Maximum payload size. Default 4096 chars.
* `headers` - Additional headers as associative array name => content.
* `logger` - A [PSR-3](https://www.php-fig.org/psr/psr-3/) compatible logger.
* `persistent` - Connection is re-used between requests until time out is reached. Default false.
* `return_obj` - Return a [Message](Message.md) instance on receive, default false
* `timeout` - Time out in seconds. Default 5 seconds.
```php
$context = stream_context_create();
stream_context_set_option($context, 'ssl', 'verify_peer', false);
stream_context_set_option($context, 'ssl', 'verify_peer_name', false);
$client = new WebSocket\Client("ws://echo.websocket.org/", [
'context' => $context, // Attach stream context created above
'filter' => ['text', 'binary', 'ping'], // Specify message types for receive() to return
'headers' => [ // Additional headers, used to specify subprotocol
'Sec-WebSocket-Protocol' => 'soap',
'origin' => 'localhost',
],
'logger' => $my_psr3_logger, // Attach a PSR3 compatible logger
'return_obj' => true, // Return Message instance rather than just text
'timeout' => 60, // 1 minute time out
]);
```
## Exceptions
* `WebSocket\BadOpcodeException` - Thrown if provided opcode is invalid.
* `WebSocket\BadUriException` - Thrown if provided URI is invalid.
* `WebSocket\ConnectionException` - Thrown on any socket I/O failure.
* `WebSocket\TimeoutException` - Thrown when the socket experiences a time out.

View File

@@ -0,0 +1,44 @@
[Client](Client.md) • [Server](Server.md) • [Message](Message.md) • [Examples](Examples.md) • [Changelog](Changelog.md) • Contributing
# Websocket: Contributing
Everyone is welcome to help out!
But to keep this project sustainable, please ensure your contribution respects the requirements below.
## PR Requirements
Requirements on pull requests;
* All tests **MUST** pass.
* Code coverage **MUST** remain at 100%.
* Code **MUST** adhere to PSR-1 and PSR-12 code standards.
## Dependency management
Install or update dependencies using [Composer](https://getcomposer.org/).
```
# Install dependencies
make install
# Update dependencies
make update
```
## Code standard
This project uses [PSR-1](https://www.php-fig.org/psr/psr-1/) and [PSR-12](https://www.php-fig.org/psr/psr-12/) code standards.
```
# Check code standard adherence
make cs-check
```
## Unit testing
Unit tests with [PHPUnit](https://phpunit.readthedocs.io/), coverage with [Coveralls](https://github.com/php-coveralls/php-coveralls)
```
# Run unit tests
make test
# Create coverage
make coverage
```

View File

@@ -0,0 +1,98 @@
[Client](Client.md) • [Server](Server.md) • [Message](Message.md) • Examples • [Changelog](Changelog.md) • [Contributing](Contributing.md)
# Websocket: Examples
Here are some examples on how to use the WebSocket library.
## Echo logger
In dev environment (as in having run composer to include dev dependencies) you have
access to a simple echo logger that print out information synchronously.
This is usable for debugging. For production, use a proper logger.
```php
namespace WebSocket;
$logger = new EchoLogger();
$client = new Client('ws://echo.websocket.org/');
$client->setLogger($logger);
$server = new Server();
$server->setLogger($logger);
```
An example of server output;
```
info | Server listening to port 8000 []
debug | Wrote 129 of 129 bytes. []
info | Server connected to port 8000 []
info | Received 'text' message []
debug | Wrote 9 of 9 bytes. []
info | Sent 'text' message []
debug | Received 'close', status: 1000. []
debug | Wrote 32 of 32 bytes. []
info | Sent 'close' message []
info | Received 'close' message []
```
## The `send` client
Source: [examples/send.php](../examples/send.php)
A simple, single send/receive client.
Example use:
```
php examples/send.php --opcode text "A text message" // Send a text message to localhost
php examples/send.php --opcode ping "ping it" // Send a ping message to localhost
php examples/send.php --uri ws://echo.websocket.org "A text message" // Send a text message to echo.websocket.org
php examples/send.php --opcode text --debug "A text message" // Use runtime debugging
```
## The `echoserver` server
Source: [examples/echoserver.php](../examples/echoserver.php)
A simple server that responds to recevied commands.
Example use:
```
php examples/echoserver.php // Run with default settings
php examples/echoserver.php --port 8080 // Listen on port 8080
php examples/echoserver.php --debug // Use runtime debugging
```
These strings can be sent as message to trigger server to perform actions;
* `exit` - Server will initiate close procedure
* `ping` - Server will send a ping message
* `headers` - Server will respond with all headers provided by client
* `auth` - Server will respond with auth header if provided by client
* For other sent strings, server will respond with the same strings
## The `random` client
Source: [examples/random_client.php](../examples/random_client.php)
The random client will use random options and continuously send/receive random messages.
Example use:
```
php examples/random_client.php --uri ws://echo.websocket.org // Connect to echo.websocket.org
php examples/random_client.php --timeout 5 --fragment_size 16 // Specify settings
php examples/random_client.php --debug // Use runtime debugging
```
## The `random` server
Source: [examples/random_server.php](../examples/random_server.php)
The random server will use random options and continuously send/receive random messages.
Example use:
```
php examples/random_server.php --port 8080 // // Listen on port 8080
php examples/random_server.php --timeout 5 --fragment_size 16 // Specify settings
php examples/random_server.php --debug // Use runtime debugging
```

View File

@@ -0,0 +1,60 @@
[Client](Client.md) • [Server](Server.md) • Message • [Examples](Examples.md) • [Changelog](Changelog.md) • [Contributing](Contributing.md)
# Websocket: Messages
If option `return_obj` is set to `true` on [client](Client.md) or [server](Server.md),
the `receive()` method will return a Message instance instead of a string.
Available classes correspond to opcode;
* WebSocket\Message\Text
* WebSocket\Message\Binary
* WebSocket\Message\Ping
* WebSocket\Message\Pong
* WebSocket\Message\Close
Additionally;
* WebSocket\Message\Message - abstract base class for all messages above
* WebSocket\Message\Factory - Factory class to create Msssage instances
## Message abstract class synopsis
```php
WebSocket\Message\Message {
public __construct(string $payload = '')
public __toString() : string
public getOpcode() : string
public getLength() : int
public getTimestamp() : DateTime
public getContent() : string
public setContent(string $payload = '') : void
public hasContent() : bool
}
```
## Factory class synopsis
```php
WebSocket\Message\Factory {
public create(string $opcode, string $payload = '') : Message
}
```
## Example
Receving a Message and echo some methods.
```php
$client = new WebSocket\Client('ws://echo.websocket.org/', ['return_obj' => true]);
$client->text('Hello WebSocket.org!');
// Echo return same message as sent
$message = $client->receive();
echo $message->getOpcode(); // -> "text"
echo $message->getLength(); // -> 20
echo $message->getContent(); // -> "Hello WebSocket.org!"
echo $message->hasContent(); // -> true
echo $message->getTimestamp()->format('H:i:s'); // -> 19:37:18
$client->close();
```

View File

@@ -0,0 +1,136 @@
[Client](Client.md) • Server • [Message](Message.md) • [Examples](Examples.md) • [Changelog](Changelog.md) • [Contributing](Contributing.md)
# Websocket: Server
The library contains a rudimentary single stream/single thread server.
It internally supports Upgrade handshake and implicit close and ping/pong operations.
Note that it does **not** support threading or automatic association ot continuous client requests.
If you require this kind of server behavior, you need to build it on top of provided server implementation.
## Class synopsis
```php
WebSocket\Server {
public __construct(array $options = [])
public __destruct()
public __toString() : string
public accept() : bool
public text(string $payload) : void
public binary(string $payload) : void
public ping(string $payload = '') : void
public pong(string $payload = '') : void
public send(mixed $payload, string $opcode = 'text', bool $masked = true) : void
public receive() : mixed
public close(int $status = 1000, mixed $message = 'ttfn') : mixed
public getPort() : int
public getPath() : string
public getRequest() : array
public getHeader(string $header_name) : string|null
public getName() : string|null
public getPier() : string|null
public getLastOpcode() : string
public getCloseStatus() : int
public isConnected() : bool
public setTimeout(int $seconds) : void
public setFragmentSize(int $fragment_size) : self
public getFragmentSize() : int
public setLogger(Psr\Log\LoggerInterface $logger = null) : void
}
```
## Examples
### Simple receive-send operation
This example reads a single message from a client, and respond with the same message.
```php
$server = new WebSocket\Server();
$server->accept();
$message = $server->receive();
$server->text($message);
$server->close();
```
### Listening to clients
To continuously listen to incoming messages, you need to put the receive operation within a loop.
Note that these functions **always** throw exception on any failure, including recoverable failures such as connection time out.
By consuming exceptions, the code will re-connect the socket in next loop iteration.
```php
$server = new WebSocket\Server();
while ($server->accept()) {
try {
$message = $server->receive();
// Act on received message
// Break while loop to stop listening
} catch (\WebSocket\ConnectionException $e) {
// Possibly log errors
}
}
$server->close();
```
### Filtering received messages
By default the `receive()` method return messages of 'text' and 'binary' opcode.
The filter option allows you to specify which message types to return.
```php
$server = new WebSocket\Server(['filter' => ['text']]);
$server->receive(); // only return 'text' messages
$server = new WebSocket\Server(['filter' => ['text', 'binary', 'ping', 'pong', 'close']]);
$server->receive(); // return all messages
```
### Sending messages
There are convenience methods to send messages with different opcodes.
```php
$server = new WebSocket\Server();
// Convenience methods
$server->text('A plain text message'); // Send an opcode=text message
$server->binary($binary_string); // Send an opcode=binary message
$server->ping(); // Send an opcode=ping frame
$server->pong(); // Send an unsolicited opcode=pong frame
// Generic send method
$server->send($payload); // Sent as masked opcode=text
$server->send($payload, 'binary'); // Sent as masked opcode=binary
$server->send($payload, 'binary', false); // Sent as unmasked opcode=binary
```
## Constructor options
The `$options` parameter in constructor accepts an associative array of options.
* `filter` - Array of opcodes to return on receive, default `['text', 'binary']`
* `fragment_size` - Maximum payload size. Default 4096 chars.
* `logger` - A [PSR-3](https://www.php-fig.org/psr/psr-3/) compatible logger.
* `port` - The server port to listen to. Default 8000.
* `return_obj` - Return a [Message](Message.md) instance on receive, default false
* `timeout` - Time out in seconds. Default 5 seconds.
```php
$server = new WebSocket\Server([
'filter' => ['text', 'binary', 'ping'], // Specify message types for receive() to return
'logger' => $my_psr3_logger, // Attach a PSR3 compatible logger
'port' => 9000, // Listening port
'return_obj' => true, // Return Message insatnce rather than just text
'timeout' => 60, // 1 minute time out
]);
```
## Exceptions
* `WebSocket\BadOpcodeException` - Thrown if provided opcode is invalid.
* `WebSocket\ConnectionException` - Thrown on any socket I/O failure.
* `WebSocket\TimeoutException` - Thrown when the socket experiences a time out.