Shipeasy
ReferencePHP

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

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://edge.shipeasy.devEdge API base.
disableTelemetryfalseOpt 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.
stickyStorenullA 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 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://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/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?
✎ Edit this page

On this page