Download the PHP package skedli/http-middleware without Composer

On this page you can find all versions of the php package skedli/http-middleware. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package http-middleware

HTTP Middleware

License

Overview

Provides PSR-15 middleware for HTTP requests, including correlation ID propagation, structured request/response logging, error handling, stateless JWT authentication, extensible health check support, and idempotency enforcement.

Built on top of PSR-15 and PSR-7, the middleware can be used with any framework that supports the MiddlewareInterface and RequestHandlerInterface standards.

Installation

How to use

Correlation ID

The middleware reads the Correlation-Id header from the incoming request. If present and non-empty, it reuses the value. Otherwise, it generates a new UUID v4. In both cases, the correlation ID is:

Default usage

Create the middleware with CorrelationIdMiddleware::create() and register it in your application. The default provider generates a UUID v4 when no Correlation-Id header is present.

In a Slim 4 application:

The correlation ID is accessible in any handler via the request attribute:

Reusing an existing correlation ID

When the incoming request already contains the Correlation-Id header, the middleware preserves it instead of generating a new one. This enables end-to-end traceability across service boundaries.

No additional configuration is needed. The middleware handles this automatically.

Custom provider

Implement the CorrelationIdProvider interface to replace the default UUID v4 generation strategy:

Then configure the middleware with the custom provider:

Using the correlation ID in your own logs

The middleware exposes the correlation ID as a request attribute. To include it in your own log entries, read the attribute and pass it through the PSR-3 $context array. Any PSR-3 logger works. The tiny-blocks/logger package is one such option, since it implements Psr\Log\LoggerInterface.

Output formatting depends on the logger implementation.

Request and response logging

The LogMiddleware provides structured logging for every HTTP request and response. It captures method, URI, query parameters, body, status code, and duration automatically.

Default usage

Create the middleware with a tiny-blocks/logger instance:

In a Slim 4 application:

What is logged

The middleware logs two entries per request cycle, one for the incoming request and one for the outgoing response .

Request is always logged at info level:

Response is logged at info for success (2xx/3xx) and error for failures (4xx/5xx):

The query_parameters and body fields are only included when present. A GET request without query parameters or body produces a minimal log:

Automatic correlation ID binding

When used together with the CorrelationIdMiddleware, the LogMiddleware automatically binds the correlation ID to the logger context. No additional configuration is needed, just register both middleware in the correct order:

The correlation ID is then automatically included in every log entry:

If the CorrelationIdMiddleware is not registered, the LogMiddleware works normally without the correlation ID.

Error handling

The ErrorMiddleware catches uncaught exceptions during request processing, maps them to structured JSON responses using a consumer-provided ExceptionMapping, and optionally logs them. When the mapping returns null, the middleware either returns a generic 500 Internal Server Error response (fallback enabled, the default) or rethrows the exception (fallback disabled).

When used together with CorrelationIdMiddleware, the ErrorMiddleware automatically binds the correlation ID to the error log context.

Default usage

withMapping() is required. Provide an ExceptionMapping implementation that converts domain exceptions into MappedError instances.

Mapped response body:

Fallback response body (unmapped exception, fallback enabled by default):

Exception mapping

ExceptionMapping is the single required dependency. mapTo() receives the thrown exception and returns a MappedError or null. Returning null defers to the fallback strategy.

MappedError carries four public fields:

Field Type Description
code string Machine-readable error code included in the response.
status int HTTP response status code (400–599).
message string Human-readable error description.
headers array<string, string\|string[]> Optional HTTP response headers (default []).

To include response headers, pass headers when constructing a MappedError. The example below adds Retry-After when login is throttled:

Error handling settings

ErrorHandlingSettings controls response detail exposure and logging verbosity:

Setting Default Description
logErrors false Enables logging of exceptions when a logger is provided.
logErrorDetails false Includes exception class, file, line, and stack trace in the log context.
displayErrorDetails false Includes exception class, file, line, and stack trace in the response body.

Development: full visibility in response and logs:

Fallback response body when displayErrorDetails is true:

Production: log details, minimal response:

Log output:

Logging errors

Provide a tiny-blocks/logger instance and enable logErrors in the settings. The logger alone is not sufficient. The logErrors setting must be explicitly enabled.

Log output:

If the CorrelationIdMiddleware is not registered, the ErrorMiddleware works normally without the correlation ID.

Declarative mapping table

ExceptionMappingTable is an alternative to writing a custom ExceptionMapping class. It is useful when there are many exception types to map: rules are registered with a fluent builder instead of imperative if or match chains. Registration order is preserved and the first matching rule wins.

Method Description
when(class-string<Throwable>) Exact-class match.
whenAny(non-empty-list<class-string<Throwable>>) Exact match against any class in the list.
whenSubclassOf(class-string<Throwable>) instanceof match (catches subclasses too).
mapsTo(code, status, message, headers?) Fixed MappedError.
resolvesWith(Closure) Closure receives the matched exception and returns a MappedError.

Building the table inside mapTo is convenient and stateless per call, but the table is rebuilt on every invocation. Long-running runtimes can cache the built table as a property or a static variable if profiling shows it matters.

Per-group usage

By default, unmapped exceptions produce a generic 500 fallback response. Set withFallbackOnUnmapped(false) on an inner middleware to propagate unmapped exceptions outward instead, so a wider scope can handle them.

The outer middleware acts as the catch-all. The inner middleware handles only the exceptions it recognizes and lets the rest propagate. When withFallbackOnUnmapped(false) is set, the exception is rethrown before any logging occurs.

Authentication

The AuthenticationMiddleware enforces stateless token-based authentication on incoming requests. It extracts the Bearer token from the Authorization header, validates it using a TokenDecoder, and propagates the authenticated user context as a request attribute.

No database access is performed validation relies exclusively on the token's cryptographic signature and its claims.

Default usage

Configure the middleware with a signing algorithm and key material. The built-in JwtTokenDecoder handles JWT validation using firebase/php-jwt.

With RSA (asymmetric):

With HMAC (symmetric):

In a Slim 4 application:

Using a JWKS endpoint

In a microservices architecture, the Identity Provider (IdP) typically exposes its public keys through a JWKS endpoint. The middleware can fetch the public key directly from this endpoint, eliminating the need to manually distribute key material across services.

The JWKS fetch goes through a Skedli\HttpMiddleware\Authentication\JwksProvider supplied by the consumer. The library does not bundle a concrete HTTP client. Use HttpJwksProvider::with(client:, factory:, jwksUrl:) to combine any PSR-18 client (Guzzle, Symfony HTTP Client, php-http/curl-client, etc.) with a PSR-17 request factory and the JWKS URL. Timeouts, retries, and TLS configuration are owned by the consumer's client.

If the project already wires a PSR-18 client and a PSR-17 factory for another HTTP integration, the same instances can be passed to HttpJwksProvider::with(...). The signature is intentionally compatible with NetworkTransport::with(client:, factory:) from tiny-blocks/http, so a project already using that package can share the same client and factory arguments. Timeouts, retries, and TLS configuration remain owned by the shared client.

The JWKS fetch is lazy. No network call is made during build(). The public key is resolved on the first incoming request and cached for the lifetime of the middleware instance. The algorithm defaults to RS256 when not explicitly set.

In a Slim 4 application with an environment variable:

When the JWKS endpoint is unreachable or returns an invalid response, the middleware returns a 401 Unauthorized response with a descriptive error message.

When authentication fails, the middleware returns a 401 Unauthorized response:

Possible error messages:

Message Cause
Missing Authorization header. The request has no Authorization header.
Authorization header must use Bearer scheme. The header does not start with Bearer.
Bearer token is empty. The header is Bearer with no token value.
Token is invalid or could not be decoded. The token is malformed or the signature does not match.
Token has expired. The token exp claim is in the past.
Token is missing the subject (sub) claim. The token has no sub claim.
Failed to fetch JWKS from <url>: <reason>. The JWKS endpoint is unreachable or timed out.
Invalid JWKS response from <url>. The JWKS JSON has no keys.
JWKS response does not contain a valid RSA key (...). The JWKS key is missing the n or e fields.

Supported algorithms

The SigningAlgorithm enum defines the supported algorithms:

Algorithm Type Use case
RS256 RSA Public/private key (asymmetric).
RS384 RSA Public/private key (asymmetric).
RS512 RSA Public/private key (asymmetric).
HS256 HMAC Shared secret (symmetric).
HS384 HMAC Shared secret (symmetric).
HS512 HMAC Shared secret (symmetric).
ES256 ECDSA Elliptic curve (asymmetric).
ES384 ECDSA Elliptic curve (asymmetric).

Accessing the authenticated user

On successful authentication, the middleware injects an AuthenticatedUser instance as a request attribute. The AuthenticatedUser is an interface with three methods:

Custom token decoder

Implement the TokenDecoder interface to replace the built-in JWT validation with your own strategy. The decoder must validate the token locally (stateless), without performing any network call or database query.

Then configure the middleware with the custom decoder:

Custom authenticated user

The AuthenticatedUser is an interface, so you can extend it with additional claims specific to your domain. Return your custom implementation from your TokenDecoder:

Access the extended claims in your handler:

Builder precedence

When multiple configuration options are provided, the builder resolves them in this order:

Priority Configuration Behavior
1st withTokenDecoder(...) Custom decoder wins everything else is ignored.
2nd withJwksProvider(...) Fetches JWKS, converts to PEM, defaults to RS256.
3rd withKeyMaterial(...) + withAlgorithm(...) Uses PEM and algorithm directly.

Building the middleware without a TokenDecoder, key material, or a JwksProvider throws a TokenValidationFailed exception. Using key material without an algorithm also throws.

Authorization

RequireAuthorization is a PSR-15 middleware that applies an authorization predicate to the AuthenticatedUser already verified by AuthenticationMiddleware. It must be placed in the pipeline after authentication: the middleware reads the user from the AuthenticationMiddleware::AUTHENTICATED_USER_ATTRIBUTE request attribute. When the attribute is absent, holds a different type, or the predicate returns false, the request is rejected with a 403 response without invoking the next handler.

Custom TokenDecoder implementations can return a richer AuthenticatedUser subtype, and the predicate parameter may be typed against that subtype (e.g., fn(TenantAwareUser $user): bool => $user->tenantId() === $tenantId).

When authorization fails, the middleware returns a 403 Forbidden response:

Health check

Two separate endpoints are provided: liveness and readiness. The liveness endpoint is queried by the orchestrator (e.g., ECS) to decide whether to restart the task. A failure triggers an immediate restart. The readiness endpoint is queried by the load balancer (e.g., ALB target health check) to decide routing. A failure removes the task from the pool without restarting it. Both endpoints read the APP_NAME environment variable for the service name, defaulting to "app" if not set.

Liveness

The LivenessHandler implements RequestHandlerInterface and provides a liveness probe endpoint. It always returns 200 OK without executing any dependency checks, making it suitable for ECS liveness probes where a failure triggers a task restart.

In a Slim 4 application:

Response:

Readiness

The ReadinessHandler implements RequestHandlerInterface and provides a readiness probe endpoint. It executes registered dependency checks (e.g., database, cache, message broker) and returns a tri-state overall status derived from the aggregate outcome. When a drain marker file exists, the endpoint short-circuits immediately without executing any checks.

Default usage

At least one check must be registered. Calling build() without any check throws ReadinessMisconfigured.

In a Slim 4 application:

Custom health checks

Implement the HealthCheck interface to verify the availability of an external dependency. Each check provides a component() method that returns a stable generic category (e.g., "cache", "queue") and an optional name() discriminator label. The check() method returns a HealthCheckResult.

When all checks are UP, the response is 200 OK:

When a critical check is DOWN, the response is 503 Service Unavailable:

If a check throws an unhandled exception, it is caught and reported as DOWN with the exception message.

Doctrine health check

The library provides a built-in DoctrineHealthCheck that verifies database connectivity via Doctrine DBAL. It receives a Connection instance and executes a SQL probe query to verify availability.

By default, the check:

The optional withName() builder method sets a discriminator label. Use it to distinguish multiple database connections or, if your monitoring requires it, to explicitly expose the schema name as an opt-in decision:

Non-critical checks

Checks default to critical: true. A non-critical check that is DOWN produces a DEGRADED overall status and HTTP 200. This is useful for optional dependencies like caches whose unavailability does not stop the service.

Multiple instances of the same component

When the same component category appears more than once (e.g., a primary and a read replica), use withName() to add a discriminator label. The component field remains the generic category; name distinguishes the instances.

When both are UP, the response is:

Degraded readiness

When all critical checks are UP but at least one non-critical check is DOWN, the overall status is DEGRADED. The HTTP status code is still 200 OK. The service is operational and accepts traffic, but one optional dependency is unavailable.

Drain-aware readiness

Use withDrainMarker(path: ...) to configure a sentinel file path. When the file exists, ReadinessHandler returns 503 Service Unavailable with {"reason":"draining"} immediately, without executing any checks. This short-circuits dependency calls during graceful shutdown, saving syscalls while the task drains in-flight requests.

When draining, the response is:

Creating the drain marker is the responsibility of the consuming repository, not this library. The container entrypoint script must trap SIGTERM, create the sentinel file, then forward the signal to PHP-FPM and wait:

Response format

The overall status is tri-state:

Overall status Condition HTTP
OK All checks UP 200 OK
DEGRADED All critical UP, at least one non-critical DOWN 200 OK
UNAVAILABLE At least one critical DOWN, or drain active 503 Service Unavailable

Each entry in the checks array contains:

Field Type Present Description
name string When set Discriminator label distinguishing instances of the same component.
status string Always UP or DOWN.
message string When DOWN Present only when the check is DOWN with a reason.
critical boolean Always Whether this check affects the overall HTTP status code.
component string Always Generic component category (e.g., "database", "cache"). Never leaks internal names.
duration_in_milliseconds float Always Time taken to execute the check.

Idempotency

The IdempotencyMiddleware ensures that mutating HTTP requests (POST, PUT, PATCH, DELETE) are processed exactly once. When a client sends a request with an Idempotency-Key header, the middleware hashes the request body (SHA-256) and stores the first successful response. Subsequent requests with the same key replay the stored response, even if the downstream handler would be invoked again.

If the same key is reused with a different request payload, the middleware returns 409 Conflict without forwarding the request.

Default usage

Configure the middleware with an IdempotencyReader and IdempotencyWriter implementation. The DoctrineIdempotencyStore adapter is provided for Doctrine DBAL:

In a Slim 4 application:

Storing responses with Doctrine DBAL

The DoctrineIdempotencyStore persists idempotency entries in a relational table via Doctrine DBAL. Build it with the DoctrineIdempotencyStore::create() builder. The table name defaults to idempotency_keys:

The table must exist before the middleware processes any request. Create it with the following MySQL schema:

Custom scope provider

By default, all requests share a single global namespace. Implement IdempotencyScopeProvider to isolate idempotency keys per tenant, user, or any other scope:

Then configure the middleware with the custom provider:

Builder options

Method Default Description
withReader(reader: ...) required The IdempotencyReader implementation to use.
withWriter(writer: ...) required The IdempotencyWriter implementation to use.
withHeaderName(headerName: ...) 'Idempotency-Key' HTTP header name to read the idempotency key from.
withTtlSeconds(ttlSeconds: ...) 86400 (24 hours) Time-to-live in seconds for each stored response.
withMethods(methods: ...) ['POST','PUT','PATCH','DELETE'] HTTP methods subject to idempotency checks.
withScopeProvider(scopeProvider: ...) global scope (empty namespace) Provider that resolves the namespace for each request.

Custom store

Implement IdempotencyReader and IdempotencyWriter to use any persistence backend. The save method must be race-safe:

Race-condition guarantees

DoctrineIdempotencyStore::save() is race-safe. When two concurrent requests arrive with the same key simultaneously, one insert wins and the other encounters a unique constraint violation. The losing thread re-reads the row written by the winner and returns it as though it had been found on the initial lookup.

If the row disappears between the violation and the re-read (expired in a very narrow window), the original entry is returned as a fallback. In all cases the response is consistent: identical payloads produce the same outcome.

Conflict response

When the same idempotency key is reused with a different payload hash, the middleware returns 409 Conflict without forwarding the request to the handler:

No existing entry is overwritten. The original stored response is preserved.

License

HTTP Middleware is licensed under MIT.


All versions of http-middleware with dependencies

PHP Build Version
Package Version
Requires php Version ^8.5
ext-sodium Version *
doctrine/dbal Version ^4.4
firebase/php-jwt Version ^7.0
psr/http-message Version ^2.0
psr/http-server-handler Version ^1.0
psr/http-server-middleware Version ^1.0
ramsey/uuid Version ^4.9
tiny-blocks/environment-variable Version ^1.2
tiny-blocks/http Version ^6.0
tiny-blocks/logger Version ^1.3
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package skedli/http-middleware contains the following files

Loading the files please wait ...