php-http/socket-client
PSR-7/PSR-18 HTTP client built on PHP streams. Supports TCP and UNIX domain sockets, TLS/SSL encryption, and client certificates. Lightweight, dependency-minimal option for making HTTP requests via socket connections.
Installation:
composer require php-http/socket-client
Ensure php-http/httplug and php-http/client-common are also installed (dependencies).
First Use Case: Create a basic HTTP client using the socket client:
use Http\Client\Common\PluginClient;
use Http\Client\Common\Plugin\BaseUriPlugin;
use Http\Client\Common\Plugin\HeaderAppendPlugin;
use Http\Client\Socket\Client;
use Http\Message\MessageFactory\GuzzleMessageFactory;
$client = new Client(
new GuzzleMessageFactory(),
['scheme' => 'http', 'host' => 'example.com', 'port' => 80]
);
$pluginClient = new PluginClient($client);
$pluginClient->addPlugin(new BaseUriPlugin('https://example.com'));
$pluginClient->addPlugin(new HeaderAppendPlugin('Accept', 'application/json'));
$response = $pluginClient->get('/api/endpoint');
echo $response->getBody();
Where to Look First:
src/Client.php for core functionality.tests/ for usage examples and edge cases.Plugin-Based Extensibility: Use plugins to modify requests/responses globally (e.g., auth, retries, logging):
$client = new PluginClient(new Client($messageFactory, $socketConfig));
$client->addPlugin(new AuthPlugin('Bearer', 'token123'));
Connection Pooling:
Reuse the client instance for multiple requests (sockets are connectionless by default, but plugins like Http\Client\Common\Plugin\PoolPlugin can optimize):
$client = new Client($messageFactory, ['host' => 'api.example.com']);
$client->sendRequest(new Request('GET', '/data'));
$client->sendRequest(new Request('POST', '/data', [], '{"key":"value"}'));
Async-Like Behavior: While not truly async, chain requests with callbacks for sequential processing:
$response1 = $client->get('/user/1');
$userId = json_decode($response1->getBody(), true)['id'];
$response2 = $client->get("/user/{$userId}/posts");
Custom Socket Configuration: Override defaults (e.g., timeout, SSL):
$client = new Client($messageFactory, [
'host' => 'secure.example.com',
'port' => 443,
'ssl' => [
'verify_peer' => true,
'allow_self_signed' => false,
],
'timeout' => 10.0,
]);
Integration with Laravel:
Bind the client to the container in AppServiceProvider:
$this->app->singleton('http.client.socket', function () {
return new PluginClient(new Client(
new GuzzleMessageFactory(),
['host' => config('services.api.host')]
));
});
Use in controllers:
$response = app('http.client.socket')->get('/endpoint');
No Built-in Retries:
Unlike Guzzle, this client lacks retry logic. Use Http\Client\Common\Plugin\RetryPlugin:
$client->addPlugin(new RetryPlugin());
SSL Certificate Validation: By default, SSL peer verification is enabled. Disable only for testing:
$client = new Client($messageFactory, [
'ssl' => ['verify_peer' => false], // ⚠️ Insecure!
]);
Connection Leaks:
Ensure sockets are properly closed (handled automatically in most cases, but debug with stream_socket_shutdown() if issues arise).
Plugin Order Matters:
Plugins execute in registration order. Auth plugins should run before BaseUriPlugin:
$client->addPlugin(new AuthPlugin(...)); // Runs first
$client->addPlugin(new BaseUriPlugin(...));
No Middleware: Unlike Laravel HTTP clients, this uses plugins, not middleware. Convert middleware to plugins if needed.
Enable Verbose Logging:
Use Http\Client\Common\Plugin\LoggerPlugin with Monolog:
$logger = new \Monolog\Logger('socket_client');
$client->addPlugin(new LoggerPlugin($logger, LoggerPlugin::DEBUG));
Check Raw Responses: Inspect headers/body for errors:
$status = $response->getStatusCode();
$body = $response->getBody();
$headers = $response->getHeaders();
Test Locally:
Use localhost with php -S for quick debugging:
$client = new Client($messageFactory, ['host' => '127.0.0.1', 'port' => 8000]);
Custom Message Factories:
Extend Http\Message\MessageFactory\MessageFactoryInterface for non-Guzzle messages (e.g., Symfony HTTP).
Socket-Specific Plugins: Create plugins to modify socket options dynamically:
class TimeoutPlugin implements Plugin {
public function __invoke(Transfer $transfer): void {
$transfer->setSocketOption(SOL_SOCKET, SO_RCVTIMEO, ['sec' => 5, 'usec' => 0]);
}
}
Event Dispatching:
Use Http\Client\Common\Plugin\EventPlugin to hook into request/response lifecycle:
$client->addPlugin(new EventPlugin(function (Event $event) {
if ($event->isRequest()) {
$event->getRequest()->setHeader('X-Custom', 'Value');
}
}));
Proxy Support: Configure via socket options:
$client = new Client($messageFactory, [
'proxy' => 'tcp://proxy.example.com:8080',
]);
How can I help you explore Laravel packages today?