Download the PHP package alkinbg/http-idempotency-bundle without Composer

On this page you can find all versions of the php package alkinbg/http-idempotency-bundle. 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-idempotency-bundle

HttpIdempotencyBundle

HTTP request idempotency for Symfony controllers using Idempotency-Key, deterministic request fingerprints, shared state, and Symfony Lock.

[!IMPORTANT] This bundle does not provide exactly-once execution. It reduces duplicate execution for retried HTTP requests by coordinating a shared idempotency record and a lock. There is still an unavoidable failure window between an application's side effect and persistence of the completed idempotency record. Provider-level idempotency, database constraints, transactions, outbox patterns, and other domain guarantees remain complementary.

The package is stable as of 1.0. The public API listed under Extension interfaces is covered by semantic-versioning compatibility commitments throughout the 1.x series.

What problem this solves

Clients retry requests. Mobile networks fail, reverse proxies time out, workers restart, and users double-submit forms. For non-safe operations, a retry can accidentally execute the same business action twice.

HttpIdempotencyBundle lets an application explicitly mark a controller as idempotent. Requests to that controller must provide an idempotency key. The bundle fingerprints the request, coordinates execution with shared storage and a non-blocking Symfony lock, stores a replayable response, and returns that stored response for an identical retry without calling the controller again.

It is deliberately opt-in. Unmarked controllers are not touched.

Requirements

For multi-process or multi-node production deployments, the cache and lock backends must be shared by every application instance.

Installation

Enable without Symfony Flex

Add the bundle to config/bundles.php:

Basic usage

Mark a controller method or the whole controller class with #[Idempotent]:

The client sends one idempotency key:

An identical retry with the same key replays the stored response instead of executing the controller again.

HTTP outcomes

For a protected controller, the bundle can return these problem responses before application execution:

Status Meaning
400 Bad Request The idempotency key is missing, empty, duplicated as visible to HttpFoundation, contains control characters, or exceeds the configured maximum length.
409 Conflict Another request for the same idempotency identity is still processing, or the non-blocking execution lock cannot be acquired.
422 Unprocessable Content The same idempotency key was already used for a different request fingerprint.
503 Service Unavailable The storage/lock backend fails or lock ownership cannot be safely verified.

Protected StreamedResponse and BinaryFileResponse results are rejected explicitly with a 500 problem response because they cannot be safely snapshotted by the default response policy.

The bundle uses application/problem+json for its own error responses and does not include the idempotency key, fingerprint, lock resource, or backend exception details in them.

Configuration

Defaults:

replay_ttl controls how long a completed response remains replayable. processing_ttl controls how long an unfinished processing marker is retained. lock_ttl is the maximum expected lock duration passed to Symfony Lock. max_key_length is measured in bytes.

Production Redis example

The important requirement is not Redis specifically; it is that both state and locking are shared across all workers and nodes. Redis is one practical way to provide that.

Use the service id generated by your Symfony lock configuration for lock_factory.

Lock TTL and request duration

lock_ttl must be longer than the maximum time a protected request is expected to run. If ownership is lost or the lock has expired before the response is persisted, the bundle fails closed with 503 rather than recording a completed response it can no longer prove it owns.

A very large lock TTL is not a substitute for correct request-duration limits. Choose the value from the real upper bound of the operation and infrastructure timeouts.

Operation, scope, fingerprint, and identity

These concepts are intentionally separate.

Operation identifies the protected application action. Named Symfony routes are preferred because they provide a stable deterministic operation name. If no usable named route/controller identity can be derived, protection fails rather than silently creating an unstable identity.

Scope separates principals. The default scope is anonymous when no Symfony UserInterface is available. Authenticated requests use the user class and getUserIdentifier() so the same key can be used independently by different users.

Fingerprint describes the concrete request. The default SHA-256 fingerprint includes method, operation, path, normalized query parameters, normalized Content-Type, and raw request body. A changed payload with the same key therefore produces 422 instead of replaying an unrelated response. Custom RequestFingerprintInterface implementations must return a lowercase 64-character SHA-256 hexadecimal digest; the bundle validates this before any storage or lock backend access.

Idempotency identity is a SHA-256-derived storage/lock identity built from scope, operation, and key. Raw keys are not used as cache or lock resource names.

Anonymous multi-principal warning

All unauthenticated requests share the default anonymous scope. If one endpoint serves multiple independent principals before Symfony authentication is established, provide a custom ScopeResolverInterface implementation that returns a stable non-sensitive scope for the real principal. Do not put secrets or credentials into the scope string.

Response replay behavior

The first successful execution is captured as status, body, and eligible headers. Application 4xx and 5xx responses are also completed responses and are replayed; an application error is not automatically treated as a failed idempotency operation.

The default snapshot excludes hop-by-hop and environment-specific headers including:

Response cookies are intentionally not replayed.

Response listener ordering

The bundle captures its snapshot early in kernel.response at priority 1024. Normal response listeners with lower priorities then continue to mutate the outgoing response.

On a replay, the reconstructed response goes through kernel.response again. This means ordinary security headers, tracing headers, cookies, and other response-time behavior can be generated freshly instead of being copied from the original request.

The bundle cannot control third-party listeners registered at a priority higher than 1024; those listeners run before the bundle's capture point.

Duplicate header visibility

The bundle requires exactly one Idempotency-Key value as exposed by Symfony HttpFoundation. If HttpFoundation exposes multiple values, the request is rejected.

A web server, proxy, CDN, or PHP integration can theoretically coalesce duplicate physical header lines before the request reaches HttpFoundation. Once that wire-level distinction has been irreversibly lost, the bundle cannot reconstruct it. Configure upstream infrastructure to preserve or reject ambiguous duplicate idempotency headers.

Concurrency and failure model

For a new request the bundle performs an initial record read, attempts a non-blocking lock, and then performs a mandatory second read while holding the lock. That second read closes the race where another worker completed the operation between the initial read and lock acquisition.

The simplified state flow is:

The processing marker is intentionally not deleted during abnormal cleanup. Its TTL provides a bounded recovery period after a crashed or interrupted request.

No exactly-once guarantee

There is an unavoidable class of failures where the controller's external side effect succeeds but the process dies before the completed response can be durably stored. A later retry may execute that side effect again after the processing marker expires.

For important writes, combine this bundle with domain-level guarantees such as:

HTTP idempotency coordination is one layer, not a replacement for those guarantees.

Safe HTTP methods

Applying #[Idempotent] to a method Symfony considers safe is treated as a developer configuration error. The bundle uses Request::isMethodSafe() rather than maintaining its own incomplete list.

Extension interfaces

Applications can replace the default behavior through these supported contracts:

Override the corresponding service alias in the application container.

The stable public package surface for the 1.x series is:

Other concrete classes are implementation details unless explicitly documented. They are not part of the supported backward-compatibility surface for 1.x.

Version and support policy

The first stable public release is 1.0.0.

The compatibility target is:

Symfony PHP
7.4.* >= 8.2
8.1.* >= 8.4 as required by Symfony 8.1

Before each stable release, compatibility is checked against the supported dependency floor, latest supported dependencies, real Symfony skeleton applications, and a shared Redis cache/lock integration.

Security and privacy notes

Further documentation

See release checklist.

License

MIT.


All versions of http-idempotency-bundle with dependencies

PHP Build Version
Package Version
Requires php Version >=8.2
psr/cache Version ^3.0
symfony/config Version ^7.4 || ^8.1
symfony/dependency-injection Version ^7.4 || ^8.1
symfony/event-dispatcher Version ^7.4 || ^8.1
symfony/http-foundation Version ^7.4 || ^8.1
symfony/http-kernel Version ^7.4 || ^8.1
symfony/lock Version ^7.4 || ^8.1
symfony/security-core Version ^7.4 || ^8.1
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 alkinbg/http-idempotency-bundle contains the following files

Loading the files please wait ...