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

Toggle Laravel Package

qandidate/toggle

Qandidate Toggle is a PHP library for managing feature toggles (feature flags) in your applications. Define toggles, activate them conditionally with strategies, and roll features out safely without redeploying code.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the core library (and companion packages only if you need them):

composer require qandidate/toggle

The mental model is three objects:

  • Toggle — something that can answer isActive().
  • ToggleProvider — the registry that hands toggles back by name (InMemoryProvider, MongoDB, YAML, or your own).
  • ToggleManager — the façade you inject into application code.

Minimal wiring:

use Qandidate\Toggle\ToggleManager;
use Qandidate\Toggle\ToggleFactory;
use Qandidate\Toggle\InMemoryProvider;

$provider = new InMemoryProvider();

$provider->add('new-checkout', ToggleFactory::create('new-checkout', [
    'strategy'   => 'operator',
    'operator'   => 'OneOf',
    'conditions' => [
        ['name' => 'userId', 'operator' => 'Equals', 'value' => 42],
    ],
]));

$manager = new ToggleManager($provider);
$manager->isActive('new-checkout');

Where to look first, in this order:

  1. README.md — the strategies section is the whole API surface in one page.
  2. src/Qandidate/Toggle/ToggleFactory.php — tells you exactly which strategy / operator strings are legal.
  3. src/Qandidate/Toggle/Operator/ — the condition vocabulary (Equals, InSet, Percentage, AllOf, AnyOf, OneOf, NoneOf, …).
  4. src/Qandidate/Toggle/Context.php and ContextValue.php — how runtime data reaches your conditions.

First real use case: a release toggle. Wrap one new code path behind a simple strategy toggle (always on/off) so you can deploy dark and flip it from config without a redeploy. Don't start with targeting rules — get the plumbing (provider → manager → injected service) working first.

Implementation Patterns

Inject the narrow interface, not the manager. If your code only calls isActive(), depend on Qandidate\Toggle\Toggle or a tiny FeatureFlags wrapper around the manager. That keeps tests trivial and lets you swap in a static stub.

Strategy taxonomy you'll actually use:

Need Definition
Kill switch / always on ['strategy' => 'simple'] (or flip the value on the manager)
Internal users only operator => 'OneOf', condition userId InSet [1,2,3]
Gradual rollout operator => 'Percentage' (or an operator with a Percentage condition)
Per-tenant beta condition on tenantId with InSet
Composite rules nest AllOf / AnyOf operators as conditions

Build a context factory per-request. Don't sprinkle new Context(...) through controllers. Add one service that reads the current user/tenant/request and returns a Context. Every toggle then gets the same population of values, and you only extend one class when a new dimension appears.

Cache the context, not just the toggle. Operators are cheap to evaluate; provider round-trips (Mongo, HTTP) are not. A request-scoped memoizer that keys on (toggleName, contextHash) removes almost all the cost in loops and templates.

Remote/centralized flags. Use qandidate/toggle-client when flags must be managed by a separate service rather than shipped in config. Treat every remote call as fallible — wrap it in a local default so a timeout degrades to "feature off" rather than a 500.

Symfony / framework integration. qandidate/toggle-bundle wires the manager as a service and gives you config/YAML-driven definitions. Register your context provider there and the rest of the app only sees an injected Toggle.

Rollout workflow that works:

  1. Deploy the toggle definition (off, simple strategy).
  2. Deploy the code behind it.
  3. Enable for InSet of internal user IDs; watch metrics for a day.
  4. Move to Percentage, ramp 5 → 25 → 100.
  5. Delete the toggle definition and the dead branch in the same PR.

Testing pattern. Seed an InMemoryProvider with the toggle forced on and off, and assert both code paths in the same test file. Keep a small helper (withToggles(['new-checkout' => false])) so tests read as intent, not as object graph construction.

Custom behaviour. Two extension points cover most needs: implement Toggle for entirely bespoke logic, or implement the operator interface for a new condition type (e.g. DuringBusinessHours). Prefer the operator — it composes with everything else and stays declarative.

Gotchas and Tips

Missing toggles will bite you. Providers typically throw when asked for a name that doesn't exist. Decide once, globally: either register a catch-all default or wrap the lookup in a try/catch that logs and returns false. Silent 500s on an unknown flag are the most common production incident with this class of library.

Operator semantics are easy to invert. AllOf = every condition matches, AnyOf = at least one, OneOf = exactly one, NoneOf = none. Getting AnyOf where you meant OneOf (or vice versa) is the classic "the toggle is on for everyone" bug. Write a unit test per operator you introduce.

Missing context values are not an exception — they're a silent false. If you condition on tenantId and the context factory doesn't populate it on background jobs, the toggle is permanently off. Add an assertion or a test that the context factory produces the full key set on every entry point (HTTP, CLI, queue worker).

Percentage needs a stable input. Bucketing is only consistent if it's derived from something stable per user (an ID), not from a request ID, timestamp, or randomly ordered array. If users flap in and out of a rollout, look at what you're hashing.

No caching, no invalidation. The core library evaluates the tree on every call. If you're checking a flag inside a loop or a hot rendering path, memoize it yourself. There's no built-in push/invalidation mechanism either — remote providers are poll-on-read.

Provider registration signatures vary. InMemoryProvider, the YAML provider, and third-party providers each have their own constructor/add shape. Confirm against the specific provider's source rather than assuming $provider->add($name, $toggle).

Static state and containers. Constructing a ToggleManager (with a Mongo/HTTP provider behind it) per call is a real performance trap. Register it as a singleton in the container and inject it.

Debugging a toggle that "doesn't work": dump the context object first, then the toggle definition, then evaluate the operator tree by hand. Ninety percent of the time the definition is right and the context is missing a key.

Toggles are technical debt with a half-life. Name them with a domain prefix (checkout.new-flow), keep a removal ticket in the definition's comment or config, and delete the branch when you hit 100%. A repo full of stale toggles is worse than no toggles.

Pin the version and check maintenance activity. This is a mature, low-churn library with a small ecosystem — read the changelog before bumping, and be prepared to vendor or fork if you lean on it heavily. The core is small enough that forking is a realistic option.

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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource