# client-common is the shared tooling layer under the HTTPlug HTTP clients

> php-http/client-common is an MIT licensed PHP package that provides the pieces every HTTPlug client would otherwise write again, namely a BatchClient that sends requests in parallel, a convenience client with HTTP method names as class methods, and emulator and decorator layers over synchronous and asynchronous clients. It is the layer between the HTTPlug interfaces and the concrete transports.

**php-http/client-common** — Common HTTP Client implementations and tools for HTTPlug

- Repository: https://github.com/php-http/client-common
- Website: http://httplug.io
- Stars: 1,050 · Forks: 55
- Language: PHP
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/php-http-client-common

## Three tools that every client would otherwise reinvent

The README describes the package as common HTTP Client implementations and tools for HTTPlug, and the usage section lists exactly what it provides.

- BatchClient to handle sending requests in parallel
- A convenience client with HTTP method names as class methods
- Emulator, decorator layers for sync/async clients

That third line is the one that explains the package's position. HTTPlug defines the interfaces an HTTP client implements, and there are several client packages that implement them, each talking to a different transport. What they all need, and what none of them should own, is the layer of behaviour that sits above the transport.

The BatchClient is the clearest example. Sending a batch of requests in parallel is transport independent. A client that can issue a request can issue ten of them at once, but the code to do it correctly, to collect the responses, to handle the case where one of them fails, is the same for every transport. Putting it here means the parallel behaviour is implemented once and behaves identically whichever client is underneath.

The convenience client is a smaller idea with a large effect on readability. A class where the HTTP method is a method name, so a GET is a call named get and a POST is a call named post, turns a call that has to construct a request object into something that reads as the request it makes. The alternative is every call site constructing a request, which is verbose and easy to get subtly wrong when a header differs between two calls that are otherwise the same.

The emulator and decorator layers for sync and async clients are the least visible and the most architecturally important. A decorator wraps a client and adds behaviour to it, a logging decorator, a retry decorator, a timing decorator, a decorator that records the requests for a test. An emulator is the other half of that idea, a client that answers from a recorded set of responses instead of making a request, which is what makes it possible to test code that makes HTTP calls without a network.

## The ecosystem, and where this package sits in it

The README points at httplug.io as the homepage and at the documentation site docs.php-http.org for the official documentation, and the specific page for this package is the client-common component page. The project has a Discord-style structure too, with a Security section that gives a contact address at security@php-http.org for anything security related.

The important structural fact is the one the package name implies. The HTTPlug project is split into interfaces and implementations, and the interfaces are what let an application depend on the abstraction rather than on a particular transport. That is the standard argument for a client abstraction layer, and it has a well known cost: an abstraction is only worth it if you actually have more than one implementation to choose between, or if you expect to.

This package sits between the two. It is not an interface, and it is not a transport. It is the shared implementation layer that every transport can build on, and that is what makes it useful to a library author who writes a client. The README frames the benefit for that audience as making it easier to maintain the code responsible for handling configuration, loading and to get at a set of useful methods and an easy-to-work-with API, which is the standard way of saying, you do not have to write the batch and decorator layers again.

For an application developer the benefit is indirect but real. Because the parallel client and the convenience wrapper live in the common package rather than in a specific transport, the code you write against them keeps working if you change transport, and the same convenience methods are available whichever client you end up installing.

The default branch here is 2.x, which is worth noting for a package with this many dependents, since the major version is tied to the interface generation the package is built against.

## A small package with a serious quality toolchain

The README is short, but the repository top level listing shows a project with a full quality setup, and the badges confirm it.

The tooling files are the interesting part. There is a .php-cs-fixer.dist.php and a .php_cs.dist for code style, a phpstan.neon.dist for static analysis, a phpspec.yml.dist and a phpspec.ci.yml for the specification testing setup, a phpunit.xml.dist for the unit tests, and a .scrutinizer.yml for the continuous analysis service.

The badges line up with those files. There is a build status badge from a tests workflow, a code coverage badge and a quality score badge from Scrutinizer, a total downloads badge from Packagist, and a release badge.

The phpspec configuration is the notable one for a PHP project of this kind. phpspec is a specification testing tool where the test is written as a description of behaviour rather than as a set of assertions against a class, and the presence of a dedicated phpspec.yml.dist alongside phpunit.xml.dist means the project uses both, phpspec for the behaviour of the units and PHPUnit for the integration level tests.

The composer scripts are minimal, which is normal for a library, with test mapping to the test suite, and the README gives the command.

```bash
$ composer test
```

There is also a CHANGELOG.md, a .editorconfig, .gitattributes and .gitignore, and the usual contribution pointers, with a contributing guide on the same documentation site and a security contact for responsible disclosure.

The release history is unhurried and the version numbers are precise. 2.7.1 came out on 2023-11-30, 2.7.2 on 2024-09-24 and 2.7.3 on 2025-11-29, and the last push to the 2.x branch was on 2026-09-11. A library that other people's transports build on cannot move quickly, and the release pattern shows a project that ships when there is something to ship.

## Where client-common is the wrong choice

The first wrong case is an application that makes one kind of request to one service and has no plan to change either. The whole point of the HTTPlug model is that the transport is swappable, and the whole point of this package is that the shared behaviour is swappable too. If you are not going to swap, you are carrying an abstraction layer and a decorator stack for no benefit, and the concrete client package alone would do the job with less code to understand.

The second wrong case is a team that wants one opinionated stack. This package exists to make the client swappable, and the cost of that is that every request your application makes goes through a chain of decorators, each of which does something, and the sum of those is latency and a debugging surface. For a service where request overhead matters and the transport is never going to change, calling the transport directly is faster and simpler.

The third wrong case is a team that has already built these things. The parallel sending, the logging decorator, the test double that answers from a fixture, every one of those is a day of work per project. If your organisation has an internal HTTP client layer that already does this, adopting a second one means two conventions to follow and two places for a bug fix to land.

The fourth wrong case is expecting the package to be the thing that talks HTTP. It is not, and the README is clear that it provides common tools for HTTP Clients. Installing client-common alone gives you decorators over nothing, and you still need a client package to do anything.

## How it differs from writing the helpers yourself

The comparison worth making is with the alternative that most teams end up at, which is a small internal package of HTTP helpers that sits on top of whichever client they use.

That approach has one clear advantage, which is that it fits. The helper is written for the application, so it can be shaped around the application's logging conventions, its retry policy and its notion of what a timeout means, and it can carry the small pieces of domain knowledge, the name of the service, the header the service expects, the retry budget for that particular endpoint.

The difference in approach here is that the helpers are transport neutral and shared. A decorator written against the client interface works with any client, so it is written once for the whole organisation rather than once per application, and an application that swaps transports keeps its decorators, its batch calls and its tests.

The second difference is the emulator. Writing a test double for an HTTP client is more work than it looks, because a client interface has to be implemented faithfully enough that the code under test behaves the same. A shared emulator is that work done once, and it is the part of the package that pays for itself fastest in a codebase with a lot of tests.

The third difference is the convenience client. The method-per-HTTP-verb shape is a convention, and conventions are worth adopting when they are already the convention in the ecosystem, because a developer reading your code has seen the shape before. Writing your own fluent wrapper produces something that looks similar but is not the same, and the difference in name resolution, in how the request object is built and in how a response is returned is where the small incompatibilities live.

The fourth difference is where the abstraction is weakest. A decorator that adds behaviour is only as good as the interface it decorates, and the more specific the transport's behaviour, the more a decorator has to approximate it. The BatchClient is safe because parallelism is genuinely transport independent, and a retry decorator is less safe because whether a request is retried can depend on whether the transport has already consumed the body. The package's value is highest where the interface is genuinely the right abstraction, and lower where the transport's specifics leak through.

## Conclusion

client-common fits a PHP team that has committed to the HTTPlug interfaces and wants the parallel sending, the fluent method wrappers and the emulator and decorator layers written once rather than in each transport implementation. It does not fit a team that only needs to make one HTTP request, since a concrete client package is all that is required for that, and it does not fit a project that wants a single opinionated stack rather than an interchangeable set. Before adopting it, check which HTTPlug client package you are installing alongside it and confirm the version line, since the current release is 2.7.3 from 2025-11-29 on the 2.x branch with the last push on 2026-09-11, and note that the package is MIT licensed with a security contact at security@php-http.org.

## FAQ

### What does php-http/client-common provide?

Common tools for HTTP clients built on HTTPlug: a BatchClient to handle sending requests in parallel, a convenience client with HTTP method names as class methods, and emulator and decorator layers for sync and async clients.

### How do I install client-common?

Through Composer with composer require php-http/client-common. The package is MIT licensed, the official documentation is at docs.php-http.org under the client-common component page, and the project homepage is httplug.io.

### Do I need a separate HTTPlug client package to use client-common?

Yes. The package provides the shared layer that sits above the transports, so it decorates or batches requests for a client that implements the HTTPlug interfaces. Installing client-common alone gives you the tooling with nothing underneath it, and the README lists the concrete clients as separate packages.

### What is the current version of client-common?

2.7.3, released on 2025-11-29, on the 2.x branch which is the default branch. The previous releases were 2.7.2 on 2024-09-24 and 2.7.1 on 2023-11-30, and the last push was on 2026-09-11.

### How is the php-http project tested and where do I report a security issue?

The repository carries a phpspec.yml.dist and a phpspec.ci.yml for specification testing, a phpunit.xml.dist for unit tests, a phpstan.neon.dist for static analysis, .php-cs-fixer.dist.php for style, and a .scrutinizer.yml for continuous analysis. The test suite runs with composer test, and security issues should go to security@php-http.org.

## Sources

- [License: MIT](https://github.com/php-http/client-common/blob/2.x/LICENSE)
- [php-http/client-common on GitHub](https://github.com/php-http/client-common)
- [Project website](http://httplug.io)
- [README](https://github.com/php-http/client-common/blob/2.x/README.md)
- [Releases](https://github.com/php-http/client-common/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/php-http-client-common
