Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Client Laravel Package

unleash/client

PHP client for Unleash, the open-source feature flag service. Evaluate toggles and activation strategies in PHP applications to support gradual rollouts, A/B tests, and kill switches, with framework-friendly integration for Laravel projects.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit unleash/client is the official server-side PHP SDK for Unleash: a polling client that fetches a feature-toggle snapshot from the Unleash /client/features API (default ~30s interval), evaluates strategies locally against a context object (userId, sessionId, IP, environment, custom properties), and reports usage metrics back on a separate cadence. That model — local evaluation, no per-request network hop in the hot path — is the correct one for a Laravel request lifecycle. Evaluation is cheap and deterministic once the snapshot is cached.

The friction is that this is a library, not a Laravel package. There is no service provider, no publishable config, no facade, no Blade directive, no testing fake. Every one of those is glue you own and must maintain across SDK upgrades. For a TPM, that means the integration is a small internal package with its own semver and test suite, not a composer require and a config file.

Integration feasibility Feasible in a single sprint for a senior Laravel engineer. The SDK is PSR-compliant and unopinionated: it wants a PSR-18 HTTP client, a PSR-16 cache, and a PSR-3 logger — all of which Laravel's container can supply (Guzzle, Illuminate\Cache\Repository via a PSR-16 adapter or a thin bridge, Monolog). Binding it as a singleton in a service provider is ~40 lines.

Two real constraints:

  • PSR-16 cache, not Laravel's contract. Laravel's cache repository does not implement Psr\SimpleCache\CacheInterface, so you either add an adapter or use a dedicated cache store. Choose deliberately: the cache is the only thing keeping a fleet of PHP-FPM workers from each hammering Unleash every 30 seconds.
  • Dependency surface. The SDK pulls PSR-17/18 interfaces and expects a concrete implementation; Guzzle is the path of least resistance and is already transitively present in most Laravel apps, but version pinning against laravel/framework's HTTP client is worth verifying in a scratch branch before committing.

Technical risk

  • Ecosystem maturity signal. 65 stars and 0 tracked dependents is low. It is the official SDK maintained by Unleash, so quality risk is lower than the numbers imply, but the community blast radius is small: fewer Stack Overflow answers, fewer blog posts, fewer people who have hit your exact Octane bug. Support effectively routes to GitHub issues plus whatever commercial tier you hold.
  • Feature-flag parity lag — narrowing, but still real. v2.11.084 adds semver gte/lte constraint operators, which materially closes the gap on the most common constraint class (version-gated rollouts, "only new clients", "kill switch for versions below X"). Streaming/SSE updates, newer constraint operators beyond the semver family, and impact-metrics surfaces still lag Node/Go/Java. The parity objection downgrades from "we can't do version targeting" to "we can't do streaming yet" — a much cheaper gap to live with at 30s polling.
  • Behavioral change in semver comparison (v2.11.084). The same release removes semver build metadata from comparison. Any existing flag using a semver constraint against version strings that carry build metadata (e.g. 2.4.1+build.77) can change evaluation result after upgrade: previously distinct versions may now compare equal. This is low-probability for most apps but high-impact for a rollout flag, and it fails silently as a wrong boolean rather than an error. Treat the upgrade as behavior-affecting, not cosmetic.
  • Release cadence and version numbering. The jump from v2.10.184 to v2.11.084 shows an unusual numbering scheme and a high release frequency. That reinforces the pinning advice below — you cannot audit a moving target, and a floating constraint means an unplanned behavior change ships on a Tuesday.
  • Long-lived worker state. Under Octane, Swoole, or a long-running queue worker, a singleton SDK instance holds a snapshot in memory for the life of the process. That is fine for polling, but it means toggle changes are not instantly visible and memory is not reclaimed. Needs explicit design, not accident.
  • Fail-open vs fail-closed ambiguity. When Unleash is unreachable and the cache is cold, evaluation silently returns the default. If default is false on an authorization-relevant flag, you have an outage; if true, you have a security hole. This is a per-flag product decision that must be encoded, not inherited from a default argument.
  • Score of 18.3 is a popularity/adoption composite, not a defect count. Treat it as a signal about hiring and troubleshooting cost, not about correctness.

Key questions

  1. Self-hosted Unleash or Unleash Cloud? This sets the SLA, the network path from every app node, and the auth model for the API token.
  2. Is the SDK's polling interval and metrics reporting acceptable against our infrastructure budget (nodes × polls/minute)?
  3. Which flags are fail-open and which are fail-closed, and who signs off per flag?
  4. Do we need flag parity with an SDK that already has streaming, or is 30s polling inside our change-propagation tolerance? (Constraint parity is now largely answered by semver gte/lte; streaming is the remaining gap.)
  5. What is the exit cost if we standardize on Unleash and later reverse the decision — is our access layer thin enough that only the provider swaps?
  6. Do any of our flags target semver constraints, and is our application version string — including build metadata — normalized and correct at every call site? This is now a load-bearing input, not a decorative header.

Integration Approach

Stack fit Good fit, with a wrapper. Recommend an internal acme/feature-flags package that depends on unleash/client and exposes a narrow, framework-native API: Feature::isEnabled(string $flag, ?Context $ctx): bool, Feature::variant(...), and a Feature::fake() test double. The public surface should be ~5 methods, so the SDK never appears in application code. This is the single highest-leverage decision in the integration.

Wire it as:

  • A config file (config/feature_flags.php) with app_name, instance_id, api_url, api_token (from env/Secrets Manager, never committed), cache_store, cache_ttl, metrics_interval, and a bootstrap_file fallback path.
  • A singleton binding with booted()/terminating() hooks, or a scheduled feature-flags:sync command, so polling is explicit rather than a side effect of the first request.
  • A shared cache store (Redis) so all PHP-FPM workers and queue workers read one snapshot instead of each maintaining its own.
  • A single, canonical app_version source (build pipeline → env var → SDK context) so semver constraints read one truthful value rather than config('app.version') drift.

Migration path Assume the app has accumulated env()/config-based booleans, if (config('features.new_checkout')) branches, and possibly hardcoded rollout percentages. Migrate in three passes:

  1. Shadow read. Deploy the wrapper reading from Unleash while production still reads existing config. Assert in logs that both agree, without changing behavior. Run for a full release cycle.
  2. Cutover per domain. Move flags to Unleash authority one domain at a time — checkout, then notifications, then experiments — with the config path left as the fallback default. This preserves rollback by flipping one env var.
  3. Decommission. Delete the config-based branches and the shadow comparison once each domain has held steady for a release.

Do not do a big-bang migration; the shadow-read phase is what produces the evidence needed to defend the change. If we adopt semver targeting, the shadow-read phase must additionally log the normalized version string used in evaluation — otherwise the first semver flag is debugged in production.

Compatibility

  • Verify PHP constraint coverage across all services before requiring the package — one legacy 7.4 service can constrain the whole monorepo.
  • Confirm the chosen PSR-18 client version resolves against laravel/framework and any existing Guzzle pins (guzzlehttp/guzzle 6 vs 7 conflicts are a classic time sink).
  • Laravel version support should be treated as "whatever the wrapper supports," which we control — do not let the SDK's floor dictate the framework upgrade path.
  • Pin exactly, not ^2.11. Given the cadence and the semver-normalization change in this release, upgrade the SDK as a deliberate, reviewed change with a changelog diff on every bump.
  • Octane/queue compatibility must be tested, not assumed: assert that a toggle change becomes visible within the expected poll window in a Swoole worker.
  • Add unit tests to the wrapper covering semver gte/lte boundaries and build-metadata-equal cases (1.2.3, 1.2.3+abc, 1.2.4) before anyone authors a production flag on them.

Sequencing

  1. Week 1 — infrastructure: Unleash environment, project, API token scoping, network egress, cache store decision.
  2. Week 1–2 — wrapper package: provider, config, singleton, PSR-16 adapter, fake() for tests, canonical app-version plumbing, one integration test that boots the container.
  3. Week 2 — observability: health check endpoint, log-based metrics for cache hit/miss and last-successful-sync timestamp, alert on stale snapshot.
  4. Week 3 — shadow read in staging, then production.
  5. Week 4+ — domain-by-domain cutover; enable one low-risk flag end to end (kill switch on a non-critical service) to prove the operational loop before touching revenue paths.
  6. Week 5 — first semver-targeted flag in a non-revenue path, used to validate version normalization before it is trusted for staged rollout of a release.

Operational Impact

Maintenance Ongoing cost is modest but non-zero and it is yours: the wrapper, its tests, and the version pin. Budget roughly 2–4 engineer-days per quarter for SDK bumps plus a small allowance for the first Octane incident. Track SDK releases deliberately rather than floating ^2.11 — the low dependents count means regressions are less likely to be caught by the community before they reach you, and this release is a working example: a one-line semver normalization change is easy to skim past in a changelog. Review release notes for evaluation semantics changes, not just new features. The dominant long-run maintenance burden is not the SDK; it is flag hygiene. Without a lifecycle policy (owner, expiry date, ticket, cleanup SLA on every flag), you will accumulate hundreds of stale toggles and the codebase becomes unreadable. Semver-targeted flags deserve an extra rule: they must declare the version window they were authored for, because they expire on their own as versions advance and silently become dead code.

Support Two tiers: Unleash's own team for SDK and server behavior (GitHub issues, plus contractual support and SLA only on a commercial plan), and internal platform engineering for the wrapper and the config. There is no meaningful third-party knowledge base at 65 stars, so the wrapper's documentation is the actual onboarding artifact — write it or every new engineer re-derives the same PSR-16 adapter. Document the semver constraint syntax and its build-metadata behavior explicitly in that one-pager; it is the part of the API most likely to be used incorrectly and least likely to be answered by a search engine.

Scaling Evaluation is in-process and cheap — CPU cost is negligible next to a typical Laravel request, and semver comparison does not change that. The scaling concern is entirely about polling fan-out: without a shared cache, N nodes each poll independently, producing N× the request volume and N slightly divergent snapshots. With a shared Redis cache, the load on Unleash is roughly constant. Metrics submission is fire-and-forget and batched. Queue workers and Octane processes need the same shared cache for consistency, or a flag flipped to 50% rollout can behave differently across a horizontally scaled fleet — an outcome that is very difficult to debug.

Failure modes

  • Unleash unreachable, cache warm — serves last known snapshot; behaviorally invisible. Verify a stale-snapshot alert exists so "invisible" does not become "invisible for three days."
  • Unreachable, cache cold — all flags return defaults. This is the scenario to design for: fail-open flags degrade gracefully, fail-closed flags take down functionality. Add a bootstrap file as a third-tier fallback so a cold start during an Unleash outage still serves a known-good snapshot.
  • Wrong version string in context — semver gte/lte evaluates against whatever we pass. A missing, stale, or build-metadata-inconsistent version silently mis-targets a rollout: some users get the new path, some do not, and nothing errors. Assert app version is non-empty at boot and log it on every evaluation that involves a semver constraint.
  • SDK upgrade without semantic review — post-2.11.084, versions that differ only by build metadata compare equal. A flag that previously split 2.4.1+a from 2.4.1+b no longer does. Gate SDK bumps behind a test that asserts current production flag outcomes are unchanged.
  • Bad snapshot deployed — a malformed strategy or an over-broad rollout can break everything at once, and flags bypass code review. Mitigate with Unleash-side change approval, per-environment tokens, and a documented kill switch that does not itself depend on a flag.
  • Token expiry / rotation — surfaces as silent degradation to defaults. Alert on sync failures, not just exceptions.
  • Octane memory — long-lived processes retain snapshots; a memory-leak-shaped report should include the SDK in the suspect list.

Ramp-up A mid-level Laravel engineer can consume the wrapper productively in under a day if the API is narrow and the fake() helper exists. The platform engineer building the wrapper needs ~3–5 days including Unleash setup, PSR adapters, Octane verification, semver constraint testing, and observability. Product and non-engineering stakeholders need a short primer on why flags exist, the difference between release/kill-switch/experiment/permission flags, and the expectation that every flag has an owner and an expiry — otherwise the tool's success becomes the reason the codebase degrades. Version-targeted flags add one more concept worth teaching: the flag's audience is defined by deployed versions, so a flag can quietly stop matching anyone after a release. Recommend a 30-minute brown-bag plus a written one-pager, and gate the first production flag on that having happened.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle