solophp / application
PSR compliant Application class with middleware support and routing
Requires
- php: >=8.1
- psr/container: ^2.0
- psr/http-factory: ^1.0
- psr/http-message: ^2.0
- psr/http-server-handler: ^1.0
- psr/http-server-middleware: ^1.0
- solophp/router: ^2.0
Requires (Dev)
- mockery/mockery: ^1.6
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.0
- squizlabs/php_codesniffer: ^3.7
README
A PSR compliant application class with middleware support, routing capabilities, and CORS handling.
Requirements
- PHP 8.1 or higher
- PSR-7 HTTP message interfaces implementation
- PSR-11 container implementation
- PSR-17 HTTP factory implementation
- Router implementation that implements
Solo\Application\RouterInterface
Installation
Install via Composer:
composer require solophp/application
Dependencies
- psr/container ^2.0
- psr/http-message ^2.0
- psr/http-server-handler ^1.0
- psr/http-server-middleware ^1.0
- psr/http-factory ^1.0
Router Implementation
This package requires a router implementation that implements Solo\Router\RouterInterface
.
We recommend using solophp/router:
composer require solophp/router
Basic Usage
use Solo\Application\Application; use Solo\Application\CorsHandlerInterface; use Solo\Router\RouteCollector; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; // Create router implementation $router = new RouteCollector(); // Create application with router $app = new Application($router, $container, $responseFactory); // Add routes directly to router $router->get('/hello/{name}', function (ServerRequestInterface $request, ResponseInterface $response, array $args) { $response->getBody()->write("Hello, {$args['name']}!"); return $response; }); // Add middleware $app->addMiddleware(SomeMiddleware::class); // Run application $response = $app->run($request);
Constructor Options
public function __construct( RouterInterface $router, ContainerInterface $container, ResponseFactoryInterface $responseFactory, ?CorsHandlerInterface $corsHandler = null )
$router
- Router implementation that implementsSolo\Router\RouterInterface
$container
- PSR-11 container implementation$responseFactory
- PSR-17 response factory implementation$corsHandler
- Optional CORS handler for API requests
Router Interface
The application works with any router implementation that implements Solo\Router\RouterInterface
:
interface RouterInterface { public function addRoute( string $method, string $group, string $path, callable|array|string $handler, array $middleware = [], ?string $page = null ): void; public function matchRoute(string $requestMethod, string $url): array|false; public function getRoutes(): array; }
This allows you to use any router implementation or create your own.
CORS Support
The application includes built-in CORS (Cross-Origin Resource Sharing) support through an optional CORS handler:
Using the Default CORS Handler
The package provides a ready-to-use CorsHandler
implementation:
use Solo\Application\Application; use Solo\Application\CorsHandler; // Basic CORS with default settings (allows all origins) $corsHandler = new CorsHandler(); $app = new Application($router, $container, $responseFactory, $corsHandler); // Custom CORS configuration $corsHandler = new CorsHandler( allowedOrigins: ['http://localhost:3000', 'https://myapp.com'], allowedMethods: ['GET', 'POST', 'PUT', 'DELETE'], allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'], allowCredentials: true, maxAge: 3600 ); $app = new Application($router, $container, $responseFactory, $corsHandler);
Custom CORS Handler
You can also implement your own CORS handler:
use Solo\Application\CorsHandlerInterface; class MyCorsHandler implements CorsHandlerInterface { public function shouldApplyCors(ServerRequestInterface $request): bool { // Your logic to determine if CORS should be applied return true; } public function addCorsHeaders(ResponseInterface $response, ServerRequestInterface $request): ResponseInterface { // Add appropriate CORS headers return $response ->withHeader('Access-Control-Allow-Origin', '*') ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS') ->withHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); } } $corsHandler = new MyCorsHandler(); $app = new Application($router, $container, $responseFactory, $corsHandler);
The application automatically handles OPTIONS requests when a CORS handler is provided, returning appropriate CORS headers without requiring explicit route definitions.
Middleware
Add middleware using the addMiddleware()
method:
$app->addMiddleware(CorsMiddleware::class); $app->addMiddleware(new AuthMiddleware($container));
Middleware can be added as either:
- Class name (will be resolved through container)
- Object instance
Note: All middleware must be valid objects implementing middleware interface.
Routing
The application provides convenient routing methods that delegate to the router implementation:
// Route with controller $app->post('/api/users', [UserController::class, 'create']); // Route with callable controller $app->post('/api/users', UserController::class); // Route with callback $app->get('/api/users', function ($request, $response) { // Handle request return $response; }); // Route with middleware $app->get('/admin/dashboard', [DashboardController::class, 'index']) ->addMiddleware(AdminAuthMiddleware::class); // Route with page attribute $app->get('/blog/{slug}', [BlogController::class, 'show'], [], 'blog.show');
Route Information
Inside your handlers, you can access the matched route information through the request's 'route' attribute:
public function handle(ServerRequestInterface $request): ResponseInterface { $route = $request->getAttribute('route'); // $route contains: method, group, handler, args, middleware, page // Example: access the page attribute $currentPage = $route['page'] ?? null; // Example: check current route group $isAdmin = str_starts_with($route['group'], '/admin'); // ... }
The route attribute contains all information about the matched route, including:
method
- HTTP methodgroup
- Route group prefixhandler
- Route handlerargs
- Route parametersmiddleware
- Route middlewarepage
- Optional page identifier
CORS Handler Interface
If you need to implement custom CORS handling, implement the CorsHandlerInterface
:
use Solo\Application\CorsHandlerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; class CustomCorsHandler implements CorsHandlerInterface { public function shouldApplyCors(ServerRequestInterface $request): bool { // Implement your logic to determine when CORS should be applied // For example, check if request has an Origin header return $request->hasHeader('Origin'); } public function addCorsHeaders(ResponseInterface $response, ServerRequestInterface $request): ResponseInterface { $origin = $request->getHeaderLine('Origin'); // Add CORS headers based on your requirements return $response ->withHeader('Access-Control-Allow-Origin', $origin) ->withHeader('Access-Control-Allow-Credentials', 'true') ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS') ->withHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With') ->withHeader('Access-Control-Max-Age', '86400'); } }
Development
Running Tests
# Run tests composer test # Run tests with coverage composer test-coverage # Run code style check composer cs # Fix code style issues composer cs-fix # Run static analysis composer stan # Run all checks composer check
Exception Handling
The following exceptions may be thrown:
ContainerExceptionInterface
- Error retrieving service from containerNotFoundExceptionInterface
- Service not found in containerInvalidArgumentException
- Invalid route handler or middleware
License
The MIT License (MIT). Please see License File for more information.