Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.44% covered (success)
94.44%
17 / 18
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
PCG_Rollout
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
4 / 4
9
100.00% covered (success)
100.00%
1 / 1
 init
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 gate
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 is_enabled_for_blog
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
4
 blog_bucket
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * Percentage-based rollout gate for the Plugin Conflicts Guardian.
4 *
5 * @package automattic/jetpack-mu-wpcom
6 */
7
8/**
9 * Percentage rollout gate for PCG.
10 */
11class PCG_Rollout {
12
13    /**
14     * Off by default; wpcomsh sets the live percentage.
15     */
16    const DEFAULT_PERCENTAGE = 0;
17
18    /**
19     * Priority 100 leaves room for emergency overrides at higher priorities.
20     */
21    public static function init() {
22        add_filter( 'pcg_guard_activation', array( __CLASS__, 'gate' ), 100 );
23        add_filter( 'pcg_guard_updates', array( __CLASS__, 'gate' ), 100 );
24    }
25
26    /**
27     * Only narrows.
28     *
29     * @param bool $enabled Previous filter value.
30     * @return bool
31     */
32    public static function gate( $enabled ) {
33        if ( ! $enabled ) {
34            return $enabled;
35        }
36        // Simple sites can't install plugins, so there's nothing to guard.
37        if ( ! \Automattic\Jetpack\Constants::is_true( 'IS_ATOMIC' ) ) {
38            return false;
39        }
40        return self::is_enabled_for_blog( (int) get_wpcom_blog_id() );
41    }
42
43    /**
44     * Bucketing is stable, so raising the percentage only ever adds blogs.
45     *
46     * @param int $blog_id WP.com blog ID under test.
47     * @return bool
48     */
49    public static function is_enabled_for_blog( $blog_id ) {
50        $percentage = (int) apply_filters( 'pcg_rollout_percentage', self::DEFAULT_PERCENTAGE );
51        if ( $percentage <= 0 ) {
52            return false;
53        }
54        if ( $percentage >= 100 ) {
55            return true;
56        }
57
58        // A partial rollout needs a real ID to bucket on.
59        $blog_id = (int) $blog_id;
60        if ( $blog_id <= 0 ) {
61            return false;
62        }
63
64        return self::blog_bucket( $blog_id ) < $percentage;
65    }
66
67    /**
68     * `abs()` on the modulo result (not raw `crc32`) — 32-bit PHP returns
69     * a signed int and `abs(PHP_INT_MIN)` overflows.
70     *
71     * @internal Exposed for tests.
72     * @param int $blog_id Blog ID.
73     * @return int
74     */
75    public static function blog_bucket( $blog_id ) {
76        return abs( crc32( (string) (int) $blog_id ) % 100 );
77    }
78}
79
80PCG_Rollout::init();