phrity/net-stream
PSR-7 StreamInterface and PSR-17 StreamFactory implementations built for socket-based I/O. Includes Stream, SocketStream, SocketClient/Server, context wrapper, stream collections, and stream-specific exceptions and utilities for network connections.
Installation:
composer require phrity/net-stream
Ensure your project uses PHP 8.1+ (Laravel 10+).
First Use Case: Basic Socket Client
Create a simple TCP client to connect to a server (e.g., example.com:80):
use Phrity\Net\SocketClient;
$client = new SocketClient('tcp://example.com:80');
$stream = $client->connect();
// Write data
$stream->write("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
// Read response
$response = $stream->read(1024);
echo $response;
// Close
$stream->close();
First Use Case: PSR-7 Stream Factory Create a PSR-7-compliant stream from a resource (e.g., file or socket):
use Phrity\Net\StreamFactory;
$factory = new StreamFactory();
$stream = $factory->createStreamFromResource(fopen('file.txt', 'r'));
// Use in Laravel HTTP responses
return response()->stream(fn () => $stream);
Where to Look First:
$client = new SocketClient('tcp://127.0.0.1:3333');
$stream = $client->connect(['timeout' => 5.0]); // Float timeout
$stream->write(json_encode(['cmd' => 'ping']));
$response = $stream->read(256);
$stream->close();
$server = new SocketServer('tcp://127.0.0.1:3333');
$server->setContext(new Context(['timeout' => 10]));
$server->listen();
while ($stream = $server->accept()) {
$data = $stream->read(256);
$stream->write("ACK: $data");
$stream->close();
}
$this->app->bind(\Psr\Http\Message\StreamFactoryInterface::class, function () {
return new \Phrity\Net\StreamFactory();
});
$stream = app(\Psr\Http\Message\StreamFactoryInterface::class)->createStream();
$stream->write("Hello, PSR-7!");
return response()->stream(fn () => $stream);
$collection = new StreamCollection();
$collection->add($stream1);
$collection->add($stream2);
// Wait for readable streams
$readable = $collection->waitRead(1.0); // Timeout: 1 second
foreach ($readable as $stream) {
$data = $stream->read(1024);
// Process data...
}
$context = new Context([
'ssl' => [
'local_cert' => 'path/to/cert.pem',
'verify_peer' => true,
],
'timeout' => 30.0,
]);
$client = new SocketClient('ssl://example.com:443', $context);
Context for custom logic:
$context = new Context();
$context->onConnect(function ($stream) {
logger()->info('New connection established', ['stream' => $stream->getId()]);
});
$context->onFailure(function ($stream, $exception) {
logger()->error('Connection failed', ['error' => $exception->getMessage()]);
});
$server = new SocketServer('tcp://0.0.0.0:8080', $context);
SocketServer to accept connections.StreamCollection.waitRead() to process data asynchronously:
$collection = new StreamCollection();
$server = new SocketServer('tcp://0.0.0.0:5000');
$server->setContext($context);
while (true) {
$collection->add($server->accept());
$readable = $collection->waitRead(0.5); // Non-blocking
foreach ($readable as $stream) {
$data = $stream->read(1024);
// Broadcast $data to clients or process it
}
}
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->bind(\Psr\Http\Message\StreamFactoryInterface::class, function () {
return new \Phrity\Net\StreamFactory();
});
}
public function streamData()
{
$stream = app(\Psr\Http\Message\StreamFactoryInterface::class)->createStream();
$stream->write("Chunk 1\n");
$stream->write("Chunk 2\n");
return response()->stream(fn () => $stream);
}
SocketStream for protocol-specific logic:
class CustomProtocolStream extends \Phrity\Net\SocketStream
{
public function parse(): array
{
$data = $this->read(1024);
return json_decode($data, true);
}
}
$stream = new CustomProtocolStream($socketResource);
$payload = $stream->parse();
Laravel Service Container: Bind the package’s classes for dependency injection:
$this->app->singleton(\Phrity\Net\SocketClient::class, function () {
return new \Phrity\Net\SocketClient('tcp://default-host:1234');
});
Queue Jobs for Socket Operations: Dispatch long-running socket tasks to queues:
SocketJob::dispatch($socketData)->onQueue('socket');
// SocketJob.php
public function handle()
{
$client = new SocketClient($this->socketConfig);
$stream = $client->connect();
// Process data...
$stream->close();
}
ReactPHP Integration: For async Laravel apps, combine with ReactPHP:
use React\EventLoop\Factory;
use Phrity\Net\SocketServer;
$loop = Factory::create();
$server = new SocketServer('tcp://0.0.0.0:8080');
$server->onConnect(function ($stream) use ($loop) {
$loop->addTimer(1, function () use ($stream) {
$stream->write("Heartbeat\n");
});
});
$loop->run();
Testing: Use Laravel’s HTTP tests for PSR-7 streams:
public function testStreamResponse()
{
$stream = new \Phrity\Net\Stream();
$stream->write("Test data");
$response = response()->stream(fn () => $stream);
$this->get('/endpoint')->see("Test data");
}
For sockets, mock SocketClient:
$mockStream = $this->createMock(\Phrity\Net\SocketStream::class);
$mockStream->method('read')->willReturn("Mock data");
$client = $this->createMock(\Phrity\Net\SocketClient::class);
$client->method('connect')->willReturn($mockStream);
$this->app->instance(\Phrity\Net\SocketClient::class, $client);
close() or `How can I help you explore Laravel packages today?