PHPackages                             greenflags/greenflags-php - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. greenflags/greenflags-php

ActiveLibrary

greenflags/greenflags-php
=========================

PHP SDK for GreenFlags feature flags: cached snapshot reads, geofence evaluation. Zero dependencies, billing-safe for PHP-FPM via pluggable snapshot cache.

v0.3.0(1mo ago)00MITPHPPHP &gt;=8.1

Since Jul 11Pushed 1mo agoCompare

[ Source](https://github.com/greenflags-dev/greenflags-php)[ Packagist](https://packagist.org/packages/greenflags/greenflags-php)[ Docs](https://greenflags.dev)[ RSS](/packages/greenflags-greenflags-php/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (2)Versions (3)Used By (0)

greenflags/greenflags-php
=========================

[](#greenflagsgreenflags-php)

Official PHP SDK for consuming **GreenFlags feature flags** from any PHP application: Laravel, Symfony, WordPress plugins, plain PHP.

**Zero dependencies** — PHP standard library only (streams + JSON). Built to minimize billable requests even in request-scoped PHP-FPM, via a pluggable snapshot cache: at most **one network read per TTL per server**, no matter how many requests you handle.

> **Status:** `0.1.0`. Full changelog in [`CHANGELOG.md`](./CHANGELOG.md).

```
composer require greenflags/greenflags-php
```

---

Table of Contents
-----------------

[](#table-of-contents)

- [Why it exists](#why-it-exists)
- [The PHP-FPM problem (and the fix)](#the-php-fpm-problem-and-the-fix)
- [Features](#features)
- [Requirements](#requirements)
- [Quick Start](#quick-start)
- [Usage Guide](#usage-guide)
- [Laravel Recipe](#laravel-recipe)
- [WordPress / WooCommerce Recipe](#wordpress--woocommerce-recipe)
- [API Reference](#api-reference)
- [Geofence](#geofence)
- [Error Handling](#error-handling)
- [Billing Model](#billing-model)
- [Development](#development)
- [Versioning](#versioning)

---

Why it exists
-------------

[](#why-it-exists)

GreenFlags exposes a read endpoint (`GET /v1/flags`) where **every 2xx response counts as a billable read**. A naive integration that fetches flags on every request can generate thousands of unnecessary reads.

The PHP-FPM problem (and the fix)
---------------------------------

[](#the-php-fpm-problem-and-the-fix)

In Node/Go/Python, an in-memory snapshot survives between requests. **Classic PHP-FPM tears the process down after every request** — in-memory caching alone would still pay one read per request.

This SDK fixes that with a `SnapshotCacheInterface` (two methods: `get`/`set` a JSON string with a TTL) plus the `sync()` method:

1. `sync(60)` checks the cache. Fresh snapshot there? **Zero network calls.**
2. Cache empty or expired? One `GET /v1/flags` (1 billable read), stored back with the TTL.

Result: at most one read per minute per server (with `ttl=60`), whether you serve 10 or 10,000 requests. Adapters included: `ApcuCache` (shared across FPM workers) and `InMemoryCache` (Octane/workers/tests). A Redis or Laravel Cache adapter is ~5 lines (see the recipe below).

Features
--------

[](#features)

- ✅ Zero dependencies — Composer installs nothing else.
- ✅ Billing-safe in PHP-FPM via the snapshot cache + `sync()`.
- ✅ Fail-open — a failed refresh keeps the last loaded snapshot; `getFlag` returns your default for missing flags.
- ✅ Client-side geofence evaluation — the end-user's location never leaves your server.
- ✅ Injectable HTTP layer — trivial to test, or to swap streams for Guzzle/cURL.
- ✅ PHP 8.1+, `declare(strict_types=1)`, readonly value objects.

Requirements
------------

[](#requirements)

- **PHP 8.1+** with `ext-json`.
- A GreenFlags **API token**, generated from the [dashboard](https://app.greenflags.dev) for a specific `project + environment`. See the [API docs](https://greenflags.dev/docs/) for the full contract.

Quick Start
-----------

[](#quick-start)

```
use GreenFlags\Client;
use GreenFlags\Cache\ApcuCache;

$flags = new Client(
    url: 'https://app.greenflags.dev',
    apiToken: getenv('GREENFLAGS_API_TOKEN'),
    cache: new ApcuCache(),
);

$flags->sync(60); // cache hit: 0 requests · cache miss: 1 billable read

if ($flags->isEnabled('new-checkout')) {
    // ship it
}
```

Usage Guide
-----------

[](#usage-guide)

```
// 1. Load the snapshot (cache-first; see sync() above)
$flags->sync(60);

// 2. Read flags — always from memory, never hits the network
$enabled = $flags->isEnabled('my-feature');            // bool sugar
$theme   = $flags->getFlag('theme', 'light');          // string with default
$limit   = $flags->getFlag('rate-limit', 100);         // number with default
$config  = $flags->getFlag('config', []);              // json/array with default

// 3. List everything available
$all      = $flags->getAllFlags();   // list
$snapshot = $flags->getSnapshot();   // array

// 4. Force a network refresh (1 billable read) — e.g. from a cron/artisan command
$flags->refresh(cacheTtlSeconds: 60);
```

### Ground rules

[](#ground-rules)

- `getFlag` / `isEnabled` **never throw for missing flags** — `getFlag` returns your default and `isEnabled` returns `false`.
- `sync()` / `refresh()` **can throw** `GreenFlagsException` — but `sync()` swallows network failures when it already has flags to serve (fail-open for long-running runtimes).
- Pick your cache by deployment: `ApcuCache` for classic FPM, `InMemoryCache` for Octane/queues/CLI, or write a Redis/Laravel adapter for multi-server fleets (one read per TTL for the **whole** fleet).

Laravel Recipe
--------------

[](#laravel-recipe)

```
// app/Providers/AppServiceProvider.php
use GreenFlags\Client;
use GreenFlags\Cache\SnapshotCacheInterface;
use Illuminate\Support\Facades\Cache;

final class LaravelSnapshotCache implements SnapshotCacheInterface
{
    public function get(): ?string
    {
        return Cache::get('greenflags.snapshot');
    }

    public function set(string $snapshotJson, int $ttlSeconds): void
    {
        Cache::put('greenflags.snapshot', $snapshotJson, $ttlSeconds);
    }
}

public function register(): void
{
    $this->app->singleton(Client::class, fn () => new Client(
        url: config('services.greenflags.url', 'https://app.greenflags.dev'),
        apiToken: config('services.greenflags.token'),
        cache: new LaravelSnapshotCache(),
    ));
}
```

```
// anywhere (controller, middleware, Blade view via injection)
$flags = app(GreenFlags\Client::class);
$flags->sync(60);

if ($flags->isEnabled('new-checkout')) { ... }
```

With Redis as Laravel's cache driver, your **entire fleet** shares one snapshot: one billable read per minute total.

WordPress / WooCommerce Recipe
------------------------------

[](#wordpress--woocommerce-recipe)

WordPress ships its own cache primitive — transients — which maps directly onto `SnapshotCacheInterface`. Drop this in a small plugin (or `wp-content/mu-plugins/greenflags.php`):

```
