Installation & configuration
This page is the canonical home for installing the SDK and calling Shipeasy\configure() — every other page and snippet assumes configure() already ran at…
Generated from the SDK's own /docs/ — also served raw at
https://shipeasy-ai.github.io/sdk-php/pages/installation.md.
This page is the canonical home for installing the SDK and calling
Shipeasy\configure() — every other page and snippet assumes configure()
already ran at startup.
Requirements
- PHP 8.1+
- Extensions:
ext-json,ext-curl(both required) - Hosts: plain PHP-FPM, Laravel, Symfony, WordPress, Slim — anything PHP-FPM, Swoole, or RoadRunner can serve.
Install
composer require shipeasy/shipeasyThe package is shipeasy/shipeasy on Packagist. It registers a Composer files
autoload, so the package-level functions (Shipeasy\configure, Shipeasy\see,
…) are available immediately after require 'vendor/autoload.php':
require 'vendor/autoload.php';
use function Shipeasy\configure; // configure-once front door
use Shipeasy\Client; // lightweight, user-bound handleOptional: OpenFeature
open-feature/sdk (^2.0) is an optional dependency. Install it only if you
use Shipeasy\OpenFeature\ShipeasyProvider — see OpenFeature:
composer require open-feature/sdkConfigure once
Configure the SDK once at startup with Shipeasy\configure. It is
first-config-wins — the first call sets up the SDK and fires the one-shot
fetch; later calls are ignored.
use function Shipeasy\configure;
configure(
$_ENV['SHIPEASY_SERVER_KEY'], // $apiKey — the Shipeasy SERVER key (never the client key)
fn ($u) => [ // $attributes — optional transform; DEFAULT is identity
'user_id' => $u->id, // (omit it when your user array already IS the attribute map)
'plan' => $u->plan,
],
[ // $opts — optional configure() options (all optional)
'env' => 'prod', // read environment for the blob
'baseUrl' => 'https://edge.shipeasy.dev', // edge API base
'disableTelemetry' => false, // opt out of the usage beacon
'telemetryUrl' => null, // override telemetry endpoint
'privateAttributes' => ['email'], // attrs stripped from event payloads
'stickyStore' => null, // Shipeasy\StickyBucketStore for durable bucketing
],
);After configure(), bind a user per request — no key, no per-call user argument:
use Shipeasy\Client;
$client = new Client($currentUser); // construct once per callsite; runs the attributes transform
$enabled = $client->getFlag('new_checkout');configure() arguments
| Arg | Type | Notes |
|---|---|---|
$apiKey | string | The Shipeasy server key. Required. |
$attributes | callable|null | (yourUser) => attributeMap. Default is identity — omit it when your user array already IS the Shipeasy attribute map. Runs once per new Client($user). |
$opts | array | The options below (all optional). |
$opts keys
| Key | Default | Meaning |
|---|---|---|
env | 'prod' | The read environment for the blob. |
baseUrl | https://edge.shipeasy.dev | Edge API base. |
disableTelemetry | false | Opt out of the usage-telemetry beacon. |
telemetryUrl | (built-in) | Override the telemetry endpoint. |
privateAttributes | [] | Attribute names stripped from outbound event payloads (LD/Statsig privateAttributes). See Advanced. |
stickyStore | null | A Shipeasy\StickyBucketStore for durable experiment bucketing. See Advanced. |
The public client key (a separate value) is not passed to configure().
It is only used for the i18n loader <script> tag — see i18n.
Identity default
The Client constructor runs the attributes transform on your user (identity
by default — the array IS the attribute map), then merges the request's
__se_anon_id cookie when neither user_id nor anonymous_id was supplied, so
logged-out traffic buckets stably. An explicit user_id/anonymous_id always
wins. The SDK does not read env vars itself — pass $_ENV['SHIPEASY_SERVER_KEY']
explicitly.
The fetch model (no background poll)
configure() fetches the rule blob once per request the moment it runs, so
the first new Client($user)->getFlag(...) resolves against real rules with no
explicit init step. PHP is request-scoped and has no background poll thread:
- PHP-FPM / classic request lifecycle —
configure()fetches once per request. Nothing else to do. - Long-running runtimes (Swoole, RoadRunner, queue/CLI workers) — a process outlives many requests, so refresh the blob on a schedule (e.g. from a periodic task at your chosen interval) to keep it fresh across requests.
Framework wiring
Call configure() exactly once per process/request bootstrap. The frameworks
below differ only in where that single call lives.
Laravel — php artisan shipeasy:install
The package ships a Laravel service provider (auto-discovered — no manual registration). Install in four steps:
# 1. Require the package — auto-discovery registers ShipeasyServiceProvider.
composer require shipeasy/shipeasy
# 2. Publish config/shipeasy.php and seed the .env keys. Add --i18n to also
# seed SHIPEASY_CLIENT_KEY and surface the @shipeasyI18n directive.
php artisan shipeasy:install# 3. Set your keys in .env (minted at https://app.shipeasy.ai → Settings → SDK keys):
SHIPEASY_SERVER_KEY=... # server-side secret — NEVER sent to the browser
SHIPEASY_CLIENT_KEY=... # public client key — only used by @shipeasyI18n (with --i18n)Once SHIPEASY_SERVER_KEY is set the provider calls Shipeasy\configure() for
you on boot (reading config/shipeasy.php) — you never call configure()
yourself. Then bind the authenticated user per request in a controller:
use Shipeasy\Client;
public function checkout(\Illuminate\Http\Request $request)
{
$client = new Client($request->user()); // construct once per request
if ($client->getFlag('new_checkout')) {
// …
}
}{{-- 4. Place the layout helpers in your Blade <head>
(e.g. resources/views/layouts/app.blade.php): --}}
<head>
@shipeasyBootstrap($user) {{-- SSR flags/experiments bootstrap tag --}}
@shipeasyI18n {{-- i18n loader tag (with --i18n; reads config) --}}
{{-- … --}}
</head>@shipeasyBootstrap($user) echoes Shipeasy\bootstrapScriptTag($user) and
@shipeasyI18n echoes Shipeasy\i18nScriptTag(config('shipeasy.client_key'), config('shipeasy.i18n_profile')). Following the Laravel convention, the install
command does not edit your layout — it tells you where to place the
directives.
Mapping your user model
To map your user model to the Shipeasy attribute map, set attributes in
config/shipeasy.php to an invokable class name (resolved from the
container); leave it null for the identity default:
// config/shipeasy.php
'attributes' => \App\Shipeasy\ShipeasyAttributes::class,
// app/Shipeasy/ShipeasyAttributes.php
final class ShipeasyAttributes
{
public function __invoke($user): array
{
return ['user_id' => (string) $user->id, 'plan' => $user->plan];
}
}Bind __se_anon_id for logged-out traffic by calling Shipeasy\Identity::ensure()
from a middleware — see Advanced.
Manual fallback (no auto-discovery)
If you have disabled package auto-discovery, register the provider by hand in
bootstrap/providers.php (Laravel 11+) or config/app.php:
// bootstrap/providers.php
return [
// …
Shipeasy\Laravel\ShipeasyServiceProvider::class,
];Or skip the package provider entirely and call configure() from your own
provider's boot():
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use function Shipeasy\configure;
class ShipeasyServiceProvider extends ServiceProvider
{
public function boot(): void
{
configure(config('services.shipeasy.server_key'), fn ($u) => [
'user_id' => $u->id,
'plan' => $u->plan,
]);
}
}Symfony — services bootstrap
Call configure() once from a kernel boot listener or a service initializer
(reading %env(SHIPEASY_SERVER_KEY)%), then build a Client per request:
composer require shipeasy/shipeasy// e.g. from a service constructed at container boot
use function Shipeasy\configure;
configure($_ENV['SHIPEASY_SERVER_KEY'], fn ($u) => ['user_id' => $u->getId()]);use Shipeasy\Client;
$client = new Client($security->getUser()); // construct once per request
$enabled = $client->getFlag('new_checkout');WordPress — plugin/theme bootstrap
Hook configure() onto an early action (init / after_setup_theme) and read
the key from a constant or option:
composer require shipeasy/shipeasyadd_action('init', function () {
\Shipeasy\configure(SHIPEASY_SERVER_KEY, fn ($u) => [
'user_id' => (string) $u->ID,
]);
});$client = new \Shipeasy\Client(wp_get_current_user()); // construct once per request
$enabled = $client->getFlag('new_checkout');Plain PHP / PHP-FPM — configure once per request
Under classic PHP-FPM each request is a fresh process state, so configure()
runs once per request at the top of your bootstrap (before any output). The
one-shot fetch then serves every evaluation in that request.
composer require shipeasy/shipeasyrequire 'vendor/autoload.php';
use function Shipeasy\configure;
use Shipeasy\{Client, Identity};
configure($_ENV['SHIPEASY_SERVER_KEY']); // once per request — fetches the blob
Identity::ensure(); // read/mint __se_anon_id for logged-out traffic
$client = new Client($currentUser); // construct once per callsite
$enabled = $client->getFlag('new_checkout');For Swoole / RoadRunner / queue workers, configure once at worker boot and refresh the blob on a schedule to keep it fresh across requests.
Overview
shipeasy/shipeasy is the PHP server SDK for Shipeasy — feature flags (gates), dynamic configs, kill switches, A/B experiments, metric tracking, see() error…
Configuration
Shipeasy\configure() is the single setup call. The full reference — install, per-framework wiring, and the $opts table — lives on Installation. This page…