# prometheus_client_php aggregates metrics in Redis because PHP workers share nothing

> PromPHP's prometheus_client_php is a PHP instrumentation library whose collectors aggregate in Redis, APCu, APCng, Predis or memory, because PHP worker processes hold no shared state between requests. It suits PHP teams that want Prometheus counters, gauges, histograms and summaries without a daemon, and it pushes a storage decision onto you.

**PromPHP/prometheus_client_php** — Prometheus instrumentation library for PHP applications

- Repository: https://github.com/PromPHP/prometheus_client_php
- Website: https://prometheus.io/docs/concepts/metric_types/
- Stars: 553 · Forks: 110
- Language: PHP
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/promphp-prometheus-client-php

## Five adapters exist because PHP workers share no state between requests

The design constraint that shapes this library is stated in one line in the documentation: usually PHP worker processes do not share any state. A counter incremented during a request dies with that request unless something outside the process holds it, so the library's core job is client-side aggregation into a store that outlives the request. You pick from five adapters: Redis, Predis, APC, APCng, or an in-memory adapter. The Redis adapter needs a separate binary running, the documentation recommending a local Redis instance next to your PHP workers, while Predis and the APC family need only the APCu extension installed. The in-memory adapter exists for the case where you do not need persistent metrics between requests, such as a long running cron job or script. The choice is not cosmetic. It determines what your metrics survive, whether the scrape endpoint can see values written by other workers, and what infrastructure your monitoring has to depend on.

## The registry is the object your code touches, and getOrRegister is the shortcut

Every metric is reached through a `CollectorRegistry`, and the common entry point is the default registry. A counter is a two-line call:

```php
\Prometheus\CollectorRegistry::getDefault()
    ->getOrRegisterCounter('', 'some_quick_counter', 'just a quick measurement')
    ->inc();
```

The three arguments are the namespace, the metric name and the help text, and the empty namespace in this example is what the documentation uses for a quick measurement. The `getOrRegister` methods combine two steps, and the documentation also shows what those two steps are when you want them separately: `registerCounter` creates the collector and `getCounter` retrieves one that is already registered, which is how you increment the same counter from two different places in your code without registering it twice. The enhanced example shows the full set of types on one registry, with a counter taking `incBy`, a gauge taking `set` with a float value, a histogram taking `observe` with explicit bucket boundaries, and a summary taking `observe` with a sliding window length and quantile boundaries. The help strings in that example are terse on purpose, but the arguments are ordered consistently, which is what makes the four types learnable as one API.

## The scrape endpoint is a text renderer, not a framework feature

Exposing the metrics is a matter of rendering the registry's samples in the Prometheus text format and setting a content type. The documentation does it in three lines:

```php
$registry = \Prometheus\CollectorRegistry::getDefault();

$renderer = new RenderTextFormat();
$result = $renderer->render($registry->getMetricFamilySamples());

header('Content-type: ' . RenderTextFormat::MIME_TYPE);
echo $result;
```

There is no built-in HTTP route, no middleware and no framework integration in what is described here. You mount the endpoint yourself in whatever web layer you use, which is a deliberate choice rather than a gap. It means the library composes with any PHP framework, any SAPI, and any routing setup, and it also means the scraper target, the path and the access control around the endpoint are yours to define. The constant `RenderTextFormat::MIME_TYPE` is used rather than a hard-coded header string, so a future format change stays consistent. The same rendering path is used in the in-memory example, which is a useful hint that the renderer is decoupled from the storage adapter.

## Redis, Predis and APC share a prefix, and mixing them is unsupported

The adapters are not independent namespaces. The documentation carries a note that `Redis::setPrefix()` and `Predis::setPrefix()` share the same prefix, and that using both adapters with different prefixes in the same application is not supported. That single line is the most likely thing to bite a team that migrates from the phpredis extension to the pure-PHP Predis client mid-project, or that runs two applications on one Redis with different key layouts and expects them not to collide. The defaults are shown in a documented block:

```php
\Prometheus\Storage\Redis::setDefaultOptions(
    [
        'host' => '127.0.0.1',
        'port' => 6379,
        'password' => null,
        'timeout' => 0.1, // in seconds
        'read_timeout' => '10', // in seconds
        'persistent_connections' => false
    ]
);
```

A `timeout` of 0.1 seconds for the connect attempt is the setting worth noticing, since a slow or absent Redis turns every metric write into a delay on the request path. The `read_timeout` is ten seconds, and persistent connections are off by default. Predis is used either with a fresh client or with an existing connection through `Predis::fromExistingConnection($client)`, which is the path to take when your application already holds a Redis connection and you do not want a second one.

## APC and APCng need no separate binary, which is the point

The APC family is the option that makes this library usable without any extra infrastructure, and the documentation treats it as a first-class choice rather than a fallback. Creating a registry with either adapter is a single line, `new CollectorRegistry(new APCng())` or `new CollectorRegistry(new APC())`, and a separate file, `README.APCng.md`, is pointed at for more detail. The distinction that matters is between APC and APCng, and the repository tracks it in testing: the performance test suite is described as testing the APC and APCng adapters head-to-head and reporting if the APCng adapter is slower for any actions. APCu is shared memory per host, so its aggregation scope is a single machine, which makes it fast and dependency-free but means a multi-host deployment needs one scrape target per host, or a Redis adapter in front of a shared store. There is also a PDO storage option, shown with a MySQL DSN and with an in-memory SQLite DSN, which covers the case where the metric state should live in a relational database instead of a cache.

## PushGateway support was removed to keep the package small

Since version 2.0.0 the library no longer supports the Prometheus PushGateway, and the reason given is deliberate: the goal is to keep the package as small as possible. The documentation directs anyone who needs it to a companion library, `promphp/prometheus_push_gateway_php`, installed with a single Composer command. This is a meaningful boundary for a decision, because PushGateway is the standard way to export metrics from short-lived batch jobs, and the in-memory adapter described earlier is the other option for that case. The trade-off is a two-package setup for batch workloads, and the loss of a feature that other client libraries keep in-tree. It is also worth noticing what the split implies: the core package now contains only the collectors, the storage adapters and the renderers, with the push path and its own dependency weight living elsewhere. The repository's examples directory still carries a `pushgateway.php` example alongside the per-metric examples, so the historical usage is visible even though the support moved.

## Installing and running the tests needs Redis, an extension and Docker

The library is a Composer package, and the install is one line:

```bash
composer require promphp/prometheus_client_php
```

The development requirements are more demanding. The listed dependencies are PHP ^8.2 with only supported PHP versions supported, the PHP Redis extension, the PHP APCu extension, Composer, and Redis itself. The test workflow starts a Redis instance through the compose file:

```bash
docker-compose up redis
```

The compose file also defines an nginx service on port 8080 mapped to 80, a php-fpm service that links to Redis and receives `REDIS_HOST=redis` in its environment, a redis service publishing 6379, and a phpunit service that mounts the working tree. The black box test path brings the whole stack up with `docker-compose up`, then picks an adapter per run, with `ADAPTER=apc`, `ADAPTER=apcng` and `ADAPTER=redis` each running the tests in `tests/Test/`. A performance group runs the APC against APCng comparison inside the same container. Running the suite directly is also documented, with a note that `REDIS_HOST` has to be exported when Redis is not listening on localhost.

## What this library does not do, and where the alternatives sit

The limits are worth being blunt about. The library is scoped to the Prometheus exposition format and the four metric types, so anything needing a different monitoring backend is out of scope by construction. It is also a client library only: it does not push to a collector, it does not aggregate across hosts, and it does not define a scrape configuration, so a Prometheus server, its service discovery and its retention settings remain your responsibility. The storage decision is yours too, and it is the decision that determines whether the numbers mean anything. An in-memory adapter under FPM produces a scrape endpoint that reads zeros for everything written by other workers, which looks like a broken dashboard rather than a configuration mistake. The alternative approach worth comparing is a client that keeps no server-side aggregation and instead relies on the scrape target to pull each worker's own numbers, which is the model that works when a single node's metrics are genuinely separate and no cross-process view is needed. The other alternative is not to instrument in PHP at all and to let an exporter collect the same signals from the runtime and the application server. prometheus_client_php wins when you need business-level counters, not host metrics, and when you want the metric definitions to live in the code that produces them.

## Conclusion

prometheus_client_php is the right choice for a PHP application that runs under FPM or a similar per-request model, because it is one of the few metric libraries that treats cross-request aggregation as the default problem rather than an edge case, and the metric types map directly onto the Prometheus exposition format. It is a poor fit for a long-running daemon written in PHP, where the in-memory adapter is the documented option and metrics would not survive between requests, and a poor fit for anyone who wants PushGateway support, which was removed in version 2.0.0 and now lives in a separate companion package. Before adopting it, decide which adapter you will run in production and confirm that your PHP version is one of the supported releases, since the library requires PHP ^8.2, and check whether the Redis prefix conflict between `Redis::setPrefix()` and `Predis::setPrefix()` affects your deployment.

## FAQ

### How do I install prometheus_client_php?

Add it as a Composer dependency with composer require promphp/prometheus_client_php. The library requires PHP ^8.2, and only supported PHP versions are supported.

### Which storage adapter should I use for PHP metrics?

The library offers Redis, Predis, APC, APCng, an in-memory adapter and a PDO storage. The documentation recommends running a local Redis instance next to your PHP workers, and notes that the in-memory adapter suits cases where metrics do not need to persist between requests.

### Does prometheus_client_php support the Prometheus PushGateway?

Not since version 2.0.0, because the maintainers wanted to keep the package small. The documentation points to the separate promphp/prometheus_push_gateway_php companion library for that.

### How do I expose the metrics for scraping?

Render the registry samples with RenderTextFormat, set the content type to RenderTextFormat::MIME_TYPE and echo the result. The library does not provide a route, so you mount that endpoint in your own web layer.

## Sources

- [License: Apache-2.0](https://github.com/PromPHP/prometheus_client_php/blob/main/LICENSE)
- [Project website](https://prometheus.io/docs/concepts/metric_types/)
- [PromPHP/prometheus_client_php on GitHub](https://github.com/PromPHP/prometheus_client_php)
- [README](https://github.com/PromPHP/prometheus_client_php/blob/main/README.md)
- [Releases](https://github.com/PromPHP/prometheus_client_php/releases)

---

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