laravel pennant 是带上下文的可覆盖布尔决策引擎,非全局开关工具;feature::active() 总是 false 最常见原因是未传用户上下文导致闭包执行异常并静默返回 false。

直接上结论:Laravel Pennant 不是“开关一按就全开”的全局配置工具,它本质是「带上下文的、可覆盖的布尔决策引擎」。用错地方或忽略用户上下文,Feature::active('foo') 就会永远返回 false 或行为不可控。
为什么 Feature::active('new-ui') 总是 false?
最常见原因是没传用户上下文,而你的定义闭包依赖 $user 参数。
比如这样定义:
Feature::define('new-ui', function (User $user) {
return $user->hasRole('beta-tester');
});
但你在控制器里这么调用:
if (Feature::active('new-ui')) { ... }
——这时 Pennant 会用默认的「匿名用户」(null 或空实例)去执行闭包,$user->hasRole() 直接抛 Call to a member function hasRole() on null,异常被静默吞掉后返回 false。
正确做法是显式绑定用户:
- 登录用户:用
Feature::for($request->user())->active('new-ui') - 命令行或队列中:先查出用户再传入,别用
auth()->user()(可能为null) - 想对所有用户统一判断?改定义闭包,去掉
User类型提示,或给参数设默认值function ($user = null)
php artisan pennant:install 运行后没生成表?
不是命令失效,而是 Laravel 的迁移机制在作祟:它只运行「未记录在 migrations 表中」的迁移文件。
执行完 pennant:install 后,它会生成一个迁移文件(如 2026_05_14_100000_create_pennant_table.php),但不会自动运行 migrate。
必须手动补这一步:
-
php artisan migrate—— 生产环境必须走这个 - 开发环境偷懒?可以加
--force,但别在 CI/CD 里用 - 如果之前跑过迁移又删了表,记得先清
migrations表里对应记录,否则migrate会跳过
不建表的话,所有 activate()/deactivate() 操作都只存在内存或 Redis(如果你配了),重启即丢,且 forget() 无法恢复原始逻辑。
Blade 中用 @feature 指令却没渲染内容?
@feature 是语法糖,底层仍走 Feature::for($user)->active(),所以问题根源和第一个一样:缺用户上下文。
Blade 模板默认没有 $user 变量,除非你显式传入或在模板里调用 auth()->user()。
安全写法是:
@auth
@feature('beta-dashboard')
<div>Beta dashboard content</div>
@endfeature
@endauth
或者在控制器里把结果提前算好传进视图:
return view('dashboard', [
'show_beta' => Feature::for(auth()->user())->active('beta-dashboard'),
]);
再在 Blade 里用 @if($show_beta) —— 这样更可控,也方便单元测试。
Redis 驱动下 activate() 不生效?
Pennant 默认用数据库驱动,切 Redis 前必须确认两件事:
- config/pennant.php 中
'driver' => 'redis'已设置,且'connection'指向有效的 Redis 连接名(不是default就可能连错库) - Redis 中的 key 是带前缀的:
pennant:features:{feature_name}:for:{user_id},如果用户 ID 是对象或数组,序列化后 key 会变长且难调试
更隐蔽的坑:Redis 驱动下 forget() 不会清除原始规则缓存,可能导致后续 active() 仍返回上次强制设置的值。建议只在明确需要高性能读取的场景用 Redis,日常开发优先用数据库驱动。
真正容易被忽略的是「规则缓存生命周期」:Pennant 会对闭包结果做短时缓存(默认 1 秒),在高并发 AB 测试中,这会导致同一用户短时间内看到不同版本。如需严格实时,得在定义时加 cache(false),但代价是每次调用都重执行闭包逻辑。











