Installation:
composer require carthage-software/elissa-bundle
Add the bundle to config/bundles.php:
CarthageSoftware\ElissaBundle\ElissaBundle::class => ['all' => true],
First Use Case: Inject PSR-7 factories into a service (no extra config needed):
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\ResponseFactoryInterface;
class MyService {
public function __construct(
private RequestFactoryInterface $requestFactory,
private ResponseFactoryInterface $responseFactory
) {}
}
Auto-Tagged PSR-15 Handlers:
Create a class implementing \Psr\Http\Server\RequestHandlerInterface and tag it as a psr15.handler service:
#[Tag('psr15.handler')]
class MyHandler implements RequestHandlerInterface {
public function handle(ServerRequestInterface $request): ResponseInterface {
return new Response();
}
}
Dependency Injection: All PSR-7 factories (StreamFactoryInterface, RequestFactoryInterface, etc.) are auto-configured and injectable via Symfony’s DI container.
use Psr\Http\Message\UploadedFileFactoryInterface;
class FileUploader {
public function __construct(
private UploadedFileFactoryInterface $uploadedFileFactory
) {}
}
Creating Requests/Responses:
$request = $this->requestFactory->createRequest('GET', '/');
$response = $this->responseFactory->createResponse(200);
Auto-Tagging: Annotate handlers with #[Tag('psr15.handler')] to register them automatically.
#[Tag('psr15.handler', ['priority' => 10])] // Optional: Set priority
class AuthHandler implements RequestHandlerInterface {}
Middleware Integration:
#[Tag('psr15.middleware')]:
#[Tag('psr15.middleware')]
class LoggingMiddleware implements MiddlewareInterface {
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
// ...
}
}
autoconfigure to auto-load middleware from vendor packages (e.g., nelmio/cors-bundle).Stack Composition:
Leverage Symfony’s psr15.middleware and psr15.handler tags to compose middleware stacks declaratively. Example in config/services.yaml:
services:
App\Middleware\FirstMiddleware:
tags: ['psr15.middleware']
App\Middleware\SecondMiddleware:
tags: ['psr15.middleware']
#[Tag('psr15.handler', ['priority' => 20])]
class ApiHandler implements RequestHandlerInterface {
public function handle(ServerRequestInterface $request): ResponseInterface {
return new JsonResponse(['data' => 'hello']);
}
}
#[Route('/api')] on a controller that delegates to the PSR-15 handler).Priority Conflicts:
#[Tag('psr15.middleware', ['priority' => 5])]
Symfony Router vs. PSR-15:
# config/packages/framework.yaml
framework:
http_method_override: false
router:
strict_requirements: ~
Stream Detachment:
$stream = $this->streamFactory->createStream();
// ... use stream ...
$stream->detach();
Circular Dependencies:
Middleware/Handler Order:
Use Symfony’s debug:container to inspect tagged services:
php bin/console debug:container --tag=psr15.middleware
Request/Response Inspection: Log requests/responses in middleware for debugging:
#[Tag('psr15.middleware')]
class DebugMiddleware implements MiddlewareInterface {
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
error_log($request->getUri());
$response = $handler->handle($request);
error_log($response->getStatusCode());
return $response;
}
}
Reuse PSR-7 Objects:
$this->responseFactory->createResponse(200)
->withHeader('Content-Type', 'application/json');
Custom Factories:
class CustomStreamFactory implements StreamFactoryInterface {
public function createStream(string $content = ''): StreamInterface {
return new CustomStream($content);
}
}
Register as a service:
services:
Psr\Http\Message\StreamFactoryInterface: '@App\Service\CustomStreamFactory'
Performance:
$responsePool = new ResponsePool($this->responseFactory);
Testing:
$this->mockBuilder()
->disableOriginal()
->getMockBuilder(StreamFactoryInterface::class)
->disableOriginalConstructor()
->getMock();
Symfony 6+:
#[AsService] and #[Tag] attributes for cleaner service definitions:
#[AsService(tag: 'psr15.middleware')]
class MyMiddleware implements MiddlewareInterface {}
How can I help you explore Laravel packages today?