solophp/application

PSR compliant Application class with middleware support and routing

Installs: 69

Dependents: 1

Suggesters: 0

Security: 0

Stars: 0

Watchers: 1

Forks: 0

Open Issues: 0

pkg:composer/solophp/application

v2.1.0 2025-10-19 15:21 UTC

This package is auto-updated.

Last update: 2025-10-19 15:23:59 UTC


README

Latest Version on Packagist License PHP Version

A PSR-compliant HTTP 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\Contracts\Router\RouterInterface

Installation

Install via Composer:

composer require solophp/application

Dependencies

Suggested

Router

The application requires a router implementation that implements Solo\Contracts\Router\RouterInterface. You can use the suggested solophp/router package or provide your own implementation:

# Install the default router implementation
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 implements Solo\Contracts\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\Contracts\Router\RouterInterface:

interface RouterInterface
{
    /**
     * Adds a new route to the router.
     *
     * @param string $method HTTP method (GET, POST, etc.)
     * @param string $path Route path, without group prefix
     * @param callable|array|string $handler Route handler (callable or controller reference)
     * @param array{group?:string,middlewares?:array<int,callable>,name?:string} $options
     */
    public function addRoute(
        string $method,
        string $path,
        callable|array|string $handler,
        array $options = []
    ): void;

    /**
     * Matches the requested method and URL against registered routes.
     *
     * @param string $method HTTP method of the request
     * @param string $uri Requested URI
     * @return array{handler:callable|array|string,params:array<string,string>,middlewares:array<int,callable>}|false
     */
    public function match(string $method, string $uri): array|false;
}

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));

// Factory function (receives container, returns middleware instance)
$app->addMiddleware(function ($container) {
    return new RateLimitMiddleware(
        $container->get(CacheInterface::class),
        maxRequests: 100
    );
});

Middleware can be added as:

  • Class name (will be resolved through container)
  • Object instance
  • Callable factory (receives container, must return middleware 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 Parameters

Inside your handlers, you can access the matched route parameters through the request's attributes:

public function handle(ServerRequestInterface $request): ResponseInterface
{
    $handler = $request->getAttribute('handler');
    $params = $request->getAttribute('params', []);

    // Access route parameters
    $userId = $params['id'] ?? null;

    // ...
}

The request contains the following attributes after routing:

  • handler - The matched route handler (callable, array, or string)
  • params - Array of route parameters extracted from the URL

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 container
  • NotFoundExceptionInterface - Service not found in container
  • InvalidArgumentException - Invalid route handler or middleware

License

The MIT License (MIT). Please see License File for more information.