PHP 怎么特性开关

wen PHP项目 2

本文目录导读:

PHP 怎么特性开关

  1. 配置文件方案(最简单)
  2. 环境变量方案(适合 DevOps)
  3. 数据库方案(动态控制)
  4. 第三方库方案(功能强大)
  5. 实时动态方案(Redis 缓存)
  6. 高级方案(包含实验分组)
  7. 最佳实践建议
  8. 简单封装示例

在 PHP 中实现"特性开关"(Feature Flags/Toggles)有几种常见方案,我来详细介绍:

配置文件方案(最简单)

使用 PHP 常量

// config.php
define('FEATURE_NEW_UI', true);
define('FEATURE_PAYMENT', false);
define('FEATURE_EXPORT', true);
// 使用
if (FEATURE_NEW_UI) {
    // 新UI代码
} else {
    // 旧UI代码
}

使用配置文件数组

// config.php
return [
    'features' => [
        'new_ui' => true,
        'payment' => false,
        'export' => ['status' => true, 'version' => '2.0']
    ]
];
// 使用
$config = require 'config.php';
if ($config['features']['new_ui']) {
    // 新功能代码
}

环境变量方案(适合 DevOps)

// .env 文件
FEATURE_NEW_UI=true
FEATURE_PAYMENT=false
// 使用
if (getenv('FEATURE_NEW_UI')) {
    // 新功能
}
// 或者用 $_ENV
if ($_ENV['FEATURE_NEW_UI'] ?? false) {
    // 新功能
}

数据库方案(动态控制)

class FeatureFlag {
    private $db;
    private $cache = [];
    public function __construct(PDO $db) {
        $this->db = $db;
    }
    public function isEnabled(string $feature): bool {
        // 使用缓存避免重复查询
        if (!isset($this->cache[$feature])) {
            $stmt = $this->db->prepare(
                "SELECT enabled FROM feature_flags WHERE name = ?"
            );
            $stmt->execute([$feature]);
            $this->cache[$feature] = (bool)$stmt->fetchColumn();
        }
        return $this->cache[$feature];
    }
    public function enable(string $feature): void {
        $stmt = $this->db->prepare(
            "INSERT INTO feature_flags (name, enabled) VALUES (?, 1) 
             ON DUPLICATE KEY UPDATE enabled = 1"
        );
        $stmt->execute([$feature]);
        unset($this->cache[$feature]);
    }
}
// 使用
$featureFlag = new FeatureFlag($db);
if ($featureFlag->isEnabled('new_ui')) {
    // 新功能
}

第三方库方案(功能强大)

使用 Laravel 的配置

// config/features.php
return [
    'stats' => env('FEATURE_STATS', true),
    'chat' => env('FEATURE_CHAT', false),
];
// 使用
if (config('features.stats')) {
    // 统计功能
}

使用 Symfony 的 Feature Flag Bundle

// services.yaml
services:
    feature_flags:
        class: App\Service\FeatureFlags
        arguments:
            - 'redis://localhost'
// 使用
if ($this->featureFlags->isEnabled('new_checkout')) {
    // 新结算流程
}

实时动态方案(Redis 缓存)

class DynamicFeatureFlag {
    private $redis;
    public function __construct(\Redis $redis) {
        $this->redis = $redis;
    }
    public function isEnabled(string $feature, ?string $userId = null): bool {
        // 支持用户级别
        if ($userId && $this->redis->sIsMember("feature:{$feature}:users", $userId)) {
            return true;
        }
        // 全局控制
        return (bool)$this->redis->get("feature:{$feature}");
    }
    public function enableForUser(string $feature, string $userId): void {
        $this->redis->sAdd("feature:{$feature}:users", $userId);
    }
    public function enableGlobally(string $feature): void {
        $this->redis->set("feature:{$feature}", 1);
    }
}
// 使用
$flag = new DynamicFeatureFlag($redis);
if ($flag->isEnabled('beta_features', $currentUser->id)) {
    // Beta功能
}

高级方案(包含实验分组)

class FeatureFlagService {
    private $customers = [];
    public function isEnabled(string $feature, ?int $userId = null): bool {
        $default = [
            'percentage' => 0,
            'users' => [],
            'enabled' => false
        ];
        $config = $this->getFeatureConfig($feature) ?? $default;
        // 特定用户开启
        if ($userId && in_array($userId, $config['users'])) {
            return true;
        }
        // 按百分比随机开启
        if ($config['percentage'] > 0 && $userId) {
            $hash = crc32("{$feature}:{$userId}");
            return ($hash % 100) < $config['percentage'];
        }
        return $config['enabled'];
    }
}
// 配置
$features = [
    'new_dashboard' => [
        'percentage' => 50,  // 50%的用户
        'users' => [1, 5, 10], // 指定用户
        'enabled' => false
    ]
];

最佳实践建议

  1. 分层控制:全局 → 环境 → 用户
  2. 及时清理:功能稳定后移除旧代码
  3. 日志记录:记录功能开关的使用情况
  4. 监控告警:对关键功能开关设置监控
  5. 安全第一:重要功能默认关闭

简单封装示例

class Feature {
    private static $config = [];
    public static function init(array $config): void {
        self::$config = $config;
    }
    public static function isEnabled(string $name): bool {
        // 支持嵌套配置
        $value = self::$config[$name] ?? null;
        if (is_array($value)) {
            return $value['enabled'] ?? false;
        }
        return (bool)$value;
    }
    public static function all(): array {
        return self::$config;
    }
}
// 初始化
Feature::init([
    'new_ui' => true,
    'payment_v2' => ['enabled' => false, 'version' => '2.0'],
    'export_csv' => true
]);
// 使用
if (Feature::isEnabled('new_ui')) {
    // 新UI代码
}

选择哪种方案取决于你的需求:

  • 简单项目:配置文件方案即可
  • 需要动态控制:数据库或 Redis 方案
  • 大型项目:第三方库或自研服务
  • 需要 A/B 测试:带百分比的高级方案

抱歉,评论功能暂时关闭!