PHPackages                             henzeb/laravel-pennant-gated - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. henzeb/laravel-pennant-gated

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

henzeb/laravel-pennant-gated
============================

A Pennant driver that gates per-scope features behind a global switch

v1.0.0(3d ago)01↑2900%MITPHPPHP ^8.3CI passing

Since Jul 5Pushed yesterdayCompare

[ Source](https://github.com/henzeb/laravel-pennant-gated)[ Packagist](https://packagist.org/packages/henzeb/laravel-pennant-gated)[ Docs](https://github.com/henzeb/laravel-pennant-gated)[ RSS](/packages/henzeb-laravel-pennant-gated/feed)WikiDiscussions main Synced today

READMEChangelog (1)Dependencies (6)Versions (2)Used By (0)

Gated driver for Laravel Pennant
================================

[](#gated-driver-for-laravel-pennant)

[![Build Status](https://github.com/henzeb/laravel-pennant-gated/workflows/tests/badge.svg)](https://github.com/henzeb/laravel-pennant-gated/actions)[![Latest Version on Packagist](https://camo.githubusercontent.com/4cb1059113d4c84ac23f97c8d5a4e41d0488d5dea699b021deed5e13a16cd647/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f68656e7a65622f6c61726176656c2d70656e6e616e742d67617465642e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/henzeb/laravel-pennant-gated)[![Total Downloads](https://camo.githubusercontent.com/9da7a3f1953d30f353c7b1c9852c0050075c61db425f50d98f445016c7797912/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f68656e7a65622f6c61726176656c2d70656e6e616e742d67617465642e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/henzeb/laravel-pennant-gated)[![License](https://camo.githubusercontent.com/fe29596955c215870981a5db6ca61a39f066d33674e8f4dc5cf98dfb8c7e8bc0/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f68656e7a65622f6c61726176656c2d70656e6e616e742d6761746564)](https://packagist.org/packages/henzeb/laravel-pennant-gated)

This package adds a `gated` driver for [Laravel Pennant](https://laravel.com/docs/master/pennant) that wraps any other Pennant store and turns its global (unscoped) value into a switch that governs every scope at once, without ever touching their stored values.

Why
---

[](#why)

When you activate a Pennant feature for a scope, it becomes active for that scope immediately. There is no way to configure who a feature is intended for without also making it live for them right away, and no single switch that governs every scope a feature has ever been activated for:

```
Feature::for($betaUser)->activate('new-checkout');

Feature::for($betaUser)->active('new-checkout'); // true, live immediately
```

Pennant does provide `Feature::deactivateForEveryone()` to turn a feature off in a single call, but it does so by overwriting the stored value for every scope with `false`. The information about which scopes it was active for is gone. Turning the feature back on for the same scopes means activating each of them again.

The `gated` driver separates these two concerns. A scoped activation records who the feature is *intended* for, while the global (unscoped) value determines whether that targeting is currently in effect. A scoped check is only `true` when the feature is active both for that scope and globally:

```
// Configure who the feature is for. It is not active yet, because the
// global value has not been activated.
Feature::store('gated')->for($betaUser)->activate('new-checkout');
Feature::store('gated')->for($otherUser)->activate('new-checkout');

Feature::store('gated')->for($betaUser)->active('new-checkout'); // false

// Activate the feature globally. Every scope that was already configured
// becomes active immediately, without being activated individually.
Feature::store('gated')->activate('new-checkout');

Feature::store('gated')->for($betaUser)->active('new-checkout');  // true
Feature::store('gated')->for($otherUser)->active('new-checkout'); // true

// Deactivate the feature globally. The scoped configuration is left
// untouched.
Feature::store('gated')->deactivate('new-checkout');

Feature::store('gated')->for($betaUser)->active('new-checkout'); // false

// Activate it globally again. Both scopes are active again, with no
// further changes required.
Feature::store('gated')->activate('new-checkout');

Feature::store('gated')->for($betaUser)->active('new-checkout'); // true
```

Because the `gated` driver is a thin wrapper, it works with whichever store you already use for the feature's underlying values, such as `database` or `array`, or a custom driver of your own.

Installation
------------

[](#installation)

```
composer require henzeb/laravel-pennant-gated
```

Add a `gated` entry to the `stores` section of `config/pennant.php`, pointing `store` at whichever other store should be gated:

```
'stores' => [
    'gated' => [
        'driver' => 'gated',
        'store' => 'database',
    ],

    // Pennant's built-in stores, e.g.:
    'database' => [
        'driver' => 'database',
    ],
],
```

How it works
------------

[](#how-it-works)

`get($feature, $scope)` on the underlying driver is checked twice:

```
get(feature, scope):
    if scope is null:
        return underlying->get(feature, null)   // the global value, unchanged

    if not underlying->get(feature, null):
        return false                            // global kill-switch is off

    return underlying->get(feature, scope)       // otherwise, defer to the scoped value
```

So:

- `Feature::active()` (no scope) checks the global switch itself.
- `Feature::for($user)->active()` is `true` only when the feature is active **both** globally and for `$user`.

This applies to `value()` too, since it shares the same gating: if the feature carries a payload rather than a plain boolean, the scoped payload is only returned once the global gate is open.

```
Feature::store('gated')->activate('discount', ['percentage' => 10]);
Feature::store('gated')->for($user)->activate('discount', ['percentage' => 25]);

Feature::store('gated')->for($user)->value('discount'); // ['percentage' => 25]

Feature::store('gated')->deactivate('discount');
Feature::store('gated')->for($user)->value('discount'); // false, the gate is closed
```

Everything else — `define`, `set`, `setForAllScopes`, `delete`, `purge`, and, where supported by the underlying driver, `definedFeaturesForScope`, `stored`, `setAll` and `flushCache` — is delegated straight through to the underlying store.

### A note on the default scope

[](#a-note-on-the-default-scope)

By default, Pennant treats an unscoped call (e.g. `Feature::active('discount')`, no `->for(...)`) as shorthand for "check it for the currently authenticated user". For the gated store, that would be wrong: an unscoped check is meant to ask about the global switch itself, not about whichever user happens to be logged in. If it fell back to the authenticated user like every other store does, the global switch would effectively become untestable on its own.

To prevent that, this package registers its own `Feature::resolveScopeUsing()` callback, so an unscoped check on the gated store resolves to the global scope instead of the authenticated user. If your application (or another package) had already registered a scope resolver before this package's service provider boots, that resolver is respected and used as-is instead.

Because Pennant only allows one such callback at a time, if your application (or another package) registers its own `Feature::resolveScopeUsing()` *after* this package's provider has booted, it will replace ours entirely, and the gated store would go back to falling through to the authenticated user for unscoped checks. If you ever see the gated store's unscoped checks behaving as if they were scoped to the current user, this is the first thing to look for.

Testing this package
--------------------

[](#testing-this-package)

```
composer test
```

Security
--------

[](#security)

If you discover any security related issues, please email  instead of using the issue tracker.

Credits
-------

[](#credits)

- [Henze Berkheij](https://github.com/henzeb)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance100

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

3d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/15928532?v=4)[henzeb](/maintainers/henzeb)[@henzeb](https://github.com/henzeb)

---

Top Contributors

[![henzeb](https://avatars.githubusercontent.com/u/15928532?v=4)](https://github.com/henzeb "henzeb (2 commits)")

---

Tags

laravelpennantfeature-flagshenzebgate

###  Code Quality

TestsPest

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/henzeb-laravel-pennant-gated/health.svg)

```
[![Health](https://phpackages.com/badges/henzeb-laravel-pennant-gated/health.svg)](https://phpackages.com/packages/henzeb-laravel-pennant-gated)
```

###  Alternatives

[stephenjude/filament-feature-flags

Filament implementation of feature flags and segmentation with Laravel Pennant.

122177.8k1](/packages/stephenjude-filament-feature-flags)[worksome/feature-flags

A package to manage feature flags in your application

11536.2k](/packages/worksome-feature-flags)[defstudio/enum-features

A simple trait to enable a feature system using Enums and Laravel Pennant

162.2k](/packages/defstudio-enum-features)[offload-project/laravel-toggle

A simple and flexible feature toggle (feature flag) package for Laravel

251.6k](/packages/offload-project-laravel-toggle)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
