Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
Feature_Flags
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
6 / 6
7
100.00% covered (success)
100.00%
1 / 1
 register
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 get
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 all
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 is_enabled
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 reset
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 default_definition
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * Lightweight feature flag registry.
4 *
5 * @package automattic/jetpack-feature-flags
6 */
7
8namespace Automattic\Jetpack\Feature_Flags;
9
10/**
11 * Registers feature flag metadata and resolves flag state.
12 */
13class Feature_Flags {
14
15    const PACKAGE_VERSION = '0.2.0';
16
17    /**
18     * Registered feature flags.
19     *
20     * @var array<string, array>
21     */
22    private static $flags = array();
23
24    /**
25     * Register a feature flag.
26     *
27     * Supported definition keys:
28     * - default: bool Whether the flag is enabled by default.
29     * - description: string Human-readable description.
30     * - owner: string Owning package, plugin, or product area.
31     *
32     * Flag names must match /^[a-z0-9][a-z0-9_-]*$/. This is enforced at lint
33     * time by the `Jetpack.FeatureFlags.FeatureFlagName` PHPCS sniff rather than
34     * at runtime, so registration stays allocation-free on the hot path.
35     *
36     * @param string $name Flag name.
37     * @param array  $definition Flag definition.
38     * @return void
39     */
40    public static function register( $name, array $definition = array() ) {
41        $definition            = array_merge( self::default_definition( $name ), $definition );
42        $definition['default'] = (bool) $definition['default'];
43        $definition['name']    = $name;
44
45        self::$flags[ $name ] = $definition;
46    }
47
48    /**
49     * Return a registered flag definition.
50     *
51     * @param string $name Flag name.
52     * @return array|null Flag definition, or null when the flag is unknown.
53     */
54    public static function get( $name ) {
55        return self::$flags[ $name ] ?? null;
56    }
57
58    /**
59     * Return all registered flag definitions.
60     *
61     * @return array<string, array>
62     */
63    public static function all() {
64        $flags = self::$flags;
65        ksort( $flags );
66
67        return $flags;
68    }
69
70    /**
71     * Return whether a feature flag is enabled.
72     *
73     * Unknown flags default to false but still pass through the global filter.
74     *
75     * @param string $name Flag name.
76     * @return bool Whether the flag is enabled.
77     */
78    public static function is_enabled( $name ) {
79        $definition = self::get( $name );
80
81        if ( null === $definition ) {
82            $definition = self::default_definition( $name );
83        }
84
85        $default = (bool) $definition['default'];
86
87        /**
88         * Filters whether a Jetpack feature flag is enabled.
89         *
90         * @since 0.1.0
91         *
92         * @param bool   $enabled    Whether the flag is enabled. Defaults to the registered default.
93         * @param string $flag_name  Feature flag name.
94         * @param array  $definition Registered feature flag definition.
95         */
96        $enabled = (bool) apply_filters( 'jetpack_feature_flag_enabled', $default, $name, $definition );
97
98        /**
99         * Filters whether a specific Jetpack feature flag is enabled.
100         *
101         * The dynamic portion of the hook name, `$name`, refers to the feature flag name.
102         * This mirrors the WordPress `option_{$option}` convention so a single flag can be
103         * toggled with a `__return_true`/`__return_false` one-liner.
104         *
105         * @since 0.1.0
106         *
107         * @param bool  $enabled    Whether the flag is enabled.
108         * @param array $definition Registered feature flag definition.
109         */
110        return (bool) apply_filters( "jetpack_feature_flag_enabled_{$name}", $enabled, $definition );
111    }
112
113    /**
114     * Clear registered flags.
115     *
116     * Intended for tests.
117     *
118     * @return void
119     */
120    public static function reset() {
121        self::$flags = array();
122    }
123
124    /**
125     * Build the default definition for a flag.
126     *
127     * @param string $name Flag name.
128     * @return array Default flag definition.
129     */
130    private static function default_definition( $name ) {
131        return array(
132            'default'     => false,
133            'description' => '',
134            'owner'       => '',
135            'name'        => $name,
136        );
137    }
138}