Shipeasy
SDKsReferencePHP

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/shipeasy

The 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 handle

Optional: 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/sdk

Configure 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://api.shipeasy.ai',  // edge API base
        'isNetworkEnabled'  => null,                         // master egress switch; null = env-derived (on in prod, off elsewhere)
        'disableTelemetry'  => null,                         // usage beacon; null = env-derived (off outside prod)
        'telemetryUrl'      => null,                         // override telemetry endpoint
        'privateAttributes' => ['email'],                    // attrs stripped from event payloads
        'stickyStore'       => null,                         // Shipeasy\StickyBucketStore for durable bucketing
        'logLevel'          => 'warn',                       // SDK diagnostics: silent|error|warn|info|debug
    ],
);

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

ArgTypeNotes
$apiKeystringThe Shipeasy server key. Required.
$attributescallable|null(yourUser) => attributeMap. Default is identity — omit it when your user array already IS the Shipeasy attribute map. Runs once per new Client($user).
$optsarrayThe options below (all optional).

$opts keys

KeyDefaultMeaning
env'prod'The read environment for the blob.
baseUrlhttps://api.shipeasy.aiEdge API base.
isNetworkEnabledenv-derivedMaster switch for all outbound requests. null ⇒ on in production, off everywhere else. See Configuration.
disableTelemetryenv-derivedOpt out of the usage-telemetry beacon. null ⇒ on in production, off everywhere else.
telemetryUrl(built-in)Override the telemetry endpoint.
privateAttributes[]Attribute names stripped from outbound event payloads (LD/Statsig privateAttributes). See Advanced.
stickyStorenullA Shipeasy\StickyBucketStore for durable experiment bucketing. See Advanced.
logLevel'warn'SDK diagnostic verbosity: 'silent', 'error', 'warn', 'info', 'debug'. Runtime reads are fail-safe (never throw) and log at this level. See Configuration.

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 lifecycleconfigure() 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://shipeasy.ai → Connect → SDK Keys):
SHIPEASY_SERVER_KEY=...          # server-side secret — NEVER sent to the browser
SHIPEASY_CLIENT_KEY=...          # public client key — used by @shipeasyI18n / @shipeasyDevtools
SHIPEASY_PROJECT_ID=...          # project id — used by @shipeasyDevtools

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) --}}
    @shipeasyDevtools           {{-- devtools overlay: Shift+Alt+S or ?se=1 --}}
    {{-- … --}}
</head>

@shipeasyBootstrap($user) echoes Shipeasy\bootstrapScriptTag($user), @shipeasyI18n echoes Shipeasy\i18nScriptTag() and @shipeasyDevtools echoes Shipeasy\devtoolsScriptTag() — the provider forwards shipeasy.client_key, shipeasy.i18n_profile and shipeasy.project_id into configure(), so the tags need no arguments. Gate the devtools directive on your own staff check. 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/shipeasy
add_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/shipeasy
require '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.

Was this page helpful?
Updated July 26, 2026

On this page