PHP灰度发布与功能开关系统设计
PHP灰度发布与功能开关系统设计功能开关Feature Flag是一种在不部署新代码的情况下控制功能启用的技术。今天说说PHP中功能开关系统的完整设计。功能开关的核心是键值存储和条件评估。简单的开关只有开和关两种状态复杂的开关可以根据用户、环境等条件决定。phpclass FeatureFlagManager{private Redis $redis;private string $prefix ff:;private array $localCache [];public function __construct(Redis $redis){$this-redis $redis;}public function create(string $key, bool $defaultValue false, array $options []): void{$flag [key $key,enabled $defaultValue,description $options[description] ?? ,owner $options[owner] ?? ,created_at time(),updated_at time(),rules $options[rules] ?? [],expires_at $options[expires_at] ?? 0,];$this-redis-hSet({$this-prefix}flags, $key, json_encode($flag));}public function isEnabled(string $key, array $context []): bool{// 检查本地缓存if (isset($this-localCache[$key])) {$cached $this-localCache[$key];if ($cached[expires] time()) {return $this-evaluate($cached[flag], $context);}}$data $this-redis-hGet({$this-prefix}flags, $key);if ($data false) return false;$flag json_decode($data, true);// 缓存结果$this-localCache[$key] [flag $flag,expires time() 10,];return $this-evaluate($flag, $context);}private function evaluate(array $flag, array $context): bool{if (!$flag[enabled]) return false;// 检查过期时间if ($flag[expires_at] 0 $flag[expires_at] time()) {return false;}// 执行规则foreach ($flag[rules] as $rule) {if ($this-matchRule($rule, $context)) {return $rule[result] ?? true;}}return true;}private function matchRule(array $rule, array $context): bool{$field $rule[field] ?? ;$operator $rule[operator] ?? equals;$value $rule[value] ?? ;$contextValue $context[$field] ?? ;return match ($operator) {equals $contextValue $value,not_equals $contextValue ! $value,contains str_contains((string)$contextValue, (string)$value),in in_array($contextValue, (array)$value),not_in !in_array($contextValue, (array)$value),greater_than is_numeric($contextValue) $contextValue $value,less_than is_numeric($contextValue) $contextValue $value,percentage $this-percentageMatch($contextValue, $value),default false,};}private function percentageMatch(mixed $contextValue, int $percentage): bool{$hash crc32((string)$contextValue);return abs($hash) % 100 $percentage;}public function enable(string $key): void{$this-setEnabled($key, true);}public function disable(string $key): void{$this-setEnabled($key, false);}private function setEnabled(string $key, bool $enabled): void{$data json_decode($this-redis-hGet({$this-prefix}flags, $key), true);$data[enabled] $enabled;$data[updated_at] time();$this-redis-hSet({$this-prefix}flags, $key, json_encode($data));unset($this-localCache[$key]);}public function addRule(string $key, array $rule): void{$data json_decode($this-redis-hGet({$this-prefix}flags, $key), true);$data[rules][] $rule;$data[updated_at] time();$this-redis-hSet({$this-prefix}flags, $key, json_encode($data));unset($this-localCache[$key]);}public function delete(string $key): void{$this-redis-hDel({$this-prefix}flags, $key);unset($this-localCache[$key]);}public function getAll(): array{$flags $this-redis-hGetAll({$this-prefix}flags);$result [];foreach ($flags as $key $data) {$flag json_decode($data, true);$result[$key] [enabled $flag[enabled],description $flag[description],rules_count count($flag[rules] ?? []),updated_at date(Y-m-d H:i:s, $flag[updated_at] ?? 0),];}return $result;}}// 使用功能开关$redis new Redis();$redis-connect(127.0.0.1, 6379);$ff new FeatureFlagManager($redis);$ff-create(new_checkout_flow, false, [description 新版结算流程,owner 张三,rules [[field user_id, operator percentage, value 20, result true],[field user_type, operator equals, value internal, result true],],]);$ff-create(ai_recommendation, false, [description AI推荐功能,owner 李四,]);$testUsers [[user_id 1001, user_type normal],[user_id 2001, user_type internal],[user_id 3001, user_type normal],];foreach ($testUsers as $user) {$newCheckout $ff-isEnabled(new_checkout_flow, $user);$aiRec $ff-isEnabled(ai_recommendation, $user);echo 用户 {$user[user_id]} ({$user[user_type]}):\n;echo 新版结算: . ($newCheckout ? 开启 : 关闭) . \n;echo AI推荐: . ($aiRec ? 开启 : 关闭) . \n;}print_r($ff-getAll());?功能开关的管理面板APIphpclass FeatureFlagController{private FeatureFlagManager $ff;public function __construct(FeatureFlagManager $ff){$this-ff $ff;}public function list(): array{return [success true, data $this-ff-getAll()];}public function toggle(string $key): array{$flags $this-ff-getAll();if (!isset($flags[$key])) {return [success false, error 开关不存在];}if ($flags[$key][enabled]) {$this-ff-disable($key);$action disabled;} else {$this-ff-enable($key);$action enabled;}return [success true, action $action, key $key];}}?功能开关系统是持续交付的重要工具。它让功能发布和代码部署分离可以独立控制每个功能的开关状态。配合灰度发布和AB测试可以构建完善的发布管理体系。功能开关的关键是性能每次检查都要快速响应通常用Redis作为后端存储加上本地缓存降低延迟。