middlewares/utils
Common utilities for Middlewares PSR packages: auto-discovered PSR-7/PSR-17 Factory (Diactoros, Guzzle, Slim, Nyholm, Sunrise) plus PSR-15 Dispatcher for testing, callable handler helpers, and HTTP error exceptions.
Installation:
composer require middlewares/utils
No additional configuration is required for basic usage.
First Use Case: Create a PSR-7 request/response in a Laravel controller or middleware:
use Middlewares\Utils\Factory;
// Create a request
$request = Factory::createRequest('GET', '/api/users');
// Create a response
$response = Factory::createResponse(200)
->withHeader('Content-Type', 'application/json')
->withBody(Factory::createStream(json_encode(['users' => []])));
Where to Look First:
Factory for all PSR-7/PSR-17 objects (requests, responses, streams, etc.).// In a Laravel middleware or service
public function handle($request, Closure $next) {
$response = Factory::createResponse(200)
->withBody(Factory::createStream('Hello, Laravel!'));
return $next($request)->withHeader('X-Custom', 'Value');
}
bootstrap/app.php:
use Middlewares\Utils\Factory;
use Middlewares\Utils\FactoryDiscovery;
Factory::setFactory(new FactoryDiscovery(
'Laravel\Psr17Factories', // Custom factory class
FactoryDiscovery::SLIM,
FactoryDiscovery::GUZZLE
));
Dispatcher to test middleware stacks in isolation.use Middlewares\Utils\Dispatcher;
use App\Http\Middleware\Authenticate;
$middlewareStack = [
new Authenticate(),
function ($request, $next) {
return $next($request)->withHeader('X-Test', 'Passed');
}
];
$response = Dispatcher::run($middlewareStack, Factory::createRequest('GET', '/'));
$this->assertEquals('Passed', $response->getHeaderLine('X-Test'));
phpunit.xml for middleware tests:
<env name="PSR_HTTP_FACTORY" value="Middlewares\Utils\Factory"/>
CallableHandler.use Middlewares\Utils\CallableHandler;
$middleware = new CallableHandler(function ($request, $next) {
return $next($request)->withHeader('X-Processed', 'true');
});
// Register in Laravel's kernel
$this->app->middleware($middleware);
Route::get('/test', function () {
return response()->json(['status' => 'ok']);
})->middleware(new CallableHandler(function ($request, $next) {
// Pre-processing logic
return $next($request);
}));
HttpErrorException for consistent error responses.use Middlewares\Utils\HttpErrorException;
try {
if (!$user) {
throw HttpErrorException::create(404, ['error' => 'User not found']);
}
} catch (HttpErrorException $e) {
return response()->json($e->getContext(), $e->getCode());
}
public function render($request, Throwable $exception) {
if ($exception instanceof HttpErrorException) {
return response()->json($exception->getContext(), $exception->getCode());
}
return parent::render($request, $exception);
}
$this->app->bind(\Psr\Http\Message\ResponseFactoryInterface::class, function () {
return Factory::getResponseFactory();
});
public function register() {
$this->app->singleton(\Psr\Http\Message\RequestFactoryInterface::class, function () {
return Factory::getRequestFactory();
});
}
FactoryDiscovery class prioritizes libraries in this order:
Slim > Guzzle > Diactoros > Nyholm > Sunrise.
If you need a specific library (e.g., Diactoros), explicitly set it:
Factory::setFactory(new FactoryDiscovery(
FactoryDiscovery::DIACTOROS,
FactoryDiscovery::GUZZLE
));
Factory::getFactory() to inspect the current factory instance.php -v to verify compatibility before upgrading.CallableHandler QuirksPsr\Http\Message\ResponseInterface,null or a scalar,__toString().
Otherwise, it throws UnexpectedValueException.$callable = new CallableHandler(function () {
return 'valid'; // Scalar is allowed
});
Dispatcher LimitationsDispatcher does not support PSR-11 containers (e.g., Laravel's Illuminate\Container\Container).
Use CallableHandler or Laravel's built-in middleware resolution for DI.handle() method directly:
$middleware = new \App\Http\Middleware\Authenticate($this->app['request']);
$response = $middleware->handle($request, function () {
return Factory::createResponse(200);
});
Factory::createStream() are not seekable by default.
Use Laminas\Diactoros\Stream for seekable streams:
$stream = new \Laminas\Diactoros\Stream('php://memory', 'r+');
$uploadedFile = Factory::createUploadedFile($stream);
Illuminate\Http\File for uploads:
$file = new \Illuminate\Http\File($uploadedFile->getStream(), $uploadedFile->getClientFilename());
AppServiceProvider:
public function boot() {
\Middlewares\Utils\Factory::setFactory(new \Laravel\Psr17Factories());
}
Factory in Laravel's Http\Middleware:
public function handle($request, Closure $next) {
$response = $next($request);
return Factory::getResponseFactory()->createResponse()
->withHeader('X-Cache', 'Miss');
}
Tip: Enable PSR-17 factory logging:
\Middlewares\Utils\Factory::getFactory()->setResponseFactory(
new \Laminas\Diactoros\ResponseFactory()
);
Check logs for factory initialization errors.
Tip: Use var_dump(Factory::getFactory()) to debug factory configuration.
Extension Point: Implement Psr\Http\Factory\FactoryInterface for custom factories:
class CustomFactory implements \Psr\Http\Factory\FactoryInterface {
public function createRequest(string $method, $uri, array $serverParams = []): \Psr\Http\Message\RequestInterface {
// Custom logic
}
// ... other methods
}
Register it via Factory::setFactory(new CustomFactory()).
Extension Point: Subclass HttpErrorException for custom error contexts:
class
How can I help you explore Laravel packages today?