sabre/http
sabre/http is a lightweight PHP toolkit for working with HTTP requests and responses. It wraps superglobals and output functions into extendable, mockable Request/Response objects, with SAPI helpers to create a request and support dependency-injected handlers.
Installation:
composer require sabre/http:~5.0.0
Add to composer.json under require.
First Use Case:
use Sabre\HTTP\Sapi;
$request = Sapi::getRequest(); // Initialize once (e.g., in a service provider)
use Sabre\HTTP\Response;
$response = new Response();
$response->setStatus(200);
$response->setBody('Hello, World!');
Sapi::sendResponse($response); // Send once (e.g., in middleware)
Where to Look First:
RequestInterface and ResponseInterface.examples/ in the repo for practical use cases (e.g., reverseproxy.php, asyncclient.php).RequestDecorator/ResponseDecorator for extending functionality without modifying core classes.Request Lifecycle:
RequestInterface to services/middleware.
public function handle(RequestInterface $request) {
$method = $request->getMethod();
$path = $request->getPath();
// Logic...
}
$query = $request->getQueryParameters();
$post = $request->getPostData();
Response Handling:
$response->setBody(fopen('large-file.txt', 'r'));
$response->setHeader('Content-Type', 'application/json');
$response->addHeaders(['Cache-Control' => 'no-store']);
HTTP Client:
$client = new \Sabre\HTTP\Client();
$response = $client->send($request);
$client->sendAsync($request, function($response) {
// Handle success
}, function($error) {
// Handle error
});
$client->wait(); // Block until all requests complete
Decorators for Extensibility:
class AuthRequest extends \Sabre\HTTP\RequestDecorator {
public function getUser() {
return $this->getHeader('X-User-ID');
}
}
$request = new AuthRequest(Sapi::getRequest());
class LoggingResponse extends \Sabre\HTTP\ResponseDecorator {
public function __construct(\Sabre\HTTP\ResponseInterface $response) {
parent::__construct($response);
// Log response before sending
}
}
Reverse Proxy:
$request = Sapi::getRequest();
$request->setBaseUrl('/proxy/');
$proxyRequest = clone $request;
$proxyRequest->setUrl('https://target.com' . $request->getPath());
$response = $client->send($proxyRequest);
Sapi::sendResponse($response);
Sapi::getRequest() in middleware to access the request early:
public function handle($request, Closure $next) {
$sabreRequest = Sapi::getRequest();
// Modify or log $sabreRequest
return $next($request);
}
RequestInterface/ResponseInterface for unit tests:
$mockRequest = $this->createMock(RequestInterface::class);
$mockRequest->method('getMethod')->willReturn('GET');
$client->on('error:401', function($request, $response, &$retry) {
$request->setHeader('Authorization', 'Bearer ' . $this->getToken());
$retry = true;
});
Double Initialization:
Sapi::getRequest() multiple times may return inconsistent results (e.g., $_POST modified).Stream Consumption:
getBodyAsString() consumes the stream, making subsequent reads empty.$body = $request->getBodyAsString();
// OR
$stream = $request->getBodyAsStream();
rewind($stream);
Header Case Sensitivity:
getHeader()/removeHeader() treat names case-sensitively.strtolower()) when comparing.Async Client Memory Leaks:
$client->wait() can leave pending requests consuming resources.wait() or use a queue system for long-running async tasks.Base URL Mismatches:
getPath() throws LogicException if the path is outside the base URL.setBaseUrl() matches your application’s routing structure (e.g., /api/v1).var_dump($request->getHeaders());
echo $request->getBodyAsString();
1.1. Explicitly set if needed:
$request->setHttpVersion('1.0');
filter_var() to sanitize URLs before passing to setUrl():
$url = filter_var($url, FILTER_VALIDATE_URL);
RequestDecorator/ResponseDecorator to add domain-specific methods:
class ApiRequest extends RequestDecorator {
public function getApiVersion() {
return $this->getHeader('X-API-Version') ?: 'v1';
}
}
Sapi::getRequest() with a custom factory:
Sapi::setRequestFactory(function() {
return new MockRequest();
});
public function handle($request, Closure $next) {
$request = new AuthRequest(Sapi::getRequest());
$response = $next($request);
return new LoggingResponse($response);
}
$client->on('afterRequest', function($request, $response) {
Log::info('Request to ' . $request->getUrl() . ' responded with ' . $response->getStatus());
});
~5.0.0 needs PHP 7.4+).sabre/http:^4.2 for PHP 5.4 support if needed.php://input may not be available in all SAPIs (e.g., cli). Use $HTTP_RAW_POST_DATA as a fallback.Host) are auto-populated by Sapi::getRequest(). Avoid manually setting them unless necessary.Request/Response objects where possible.sendAsync() with a callback to avoid blocking:
for ($i = 0; $i < 1000; $i++) {
$client->sendAsync($request, function($response) {
// Process response non-blockingly
});
}
$client->wait(); // Optional if using a queue
$response->setBody(fopen('large-file.zip', 'r
How can I help you explore Laravel packages today?