Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
10.00% covered (danger)
10.00%
4 / 40
25.00% covered (danger)
25.00%
2 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
Free_Domain_Upsell_Experiment
10.00% covered (danger)
10.00%
4 / 40
25.00% covered (danger)
25.00%
2 / 8
642.09
0.00% covered (danger)
0.00%
0 / 1
 get_upsell_source
9.52% covered (danger)
9.52%
2 / 21
0.00% covered (danger)
0.00%
0 / 1
159.16
 is_eligible
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 should_show_upsell
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 should_suppress_sidebar_notice
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 get_current_plan
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 get_variation
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 fetch_variation
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 normalize
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * Resolves the calypso_omnibar_free_domain_upsell_20260825 experiment variation server-side.
4 *
5 * @package automattic/jetpack-mu-wpcom
6 */
7
8namespace Automattic\Jetpack\Jetpack_Mu_Wpcom;
9
10use Automattic\Jetpack\Status\Host;
11
12/**
13 * Reads the omnibar free-domain upsell variation for the current user.
14 */
15class Free_Domain_Upsell_Experiment {
16
17    const EXPERIMENT_NAME = 'calypso_omnibar_upsell_202610';
18
19    /**
20     * Which sidebar JITM the current user/site would qualify for, or null.
21     *
22     * @return string|null 'free_to_paid_plan', 'monthly_to_annual_plan', or null.
23     */
24    public static function get_upsell_source() {
25        if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {
26            return null;
27        }
28
29        $host = new Host();
30        if ( ! $host->is_wpcom_simple() || $host->is_p2_site() ) {
31            return null;
32        }
33
34        if ( (bool) get_option( 'wpcom_is_staging_site' ) ) {
35            return null;
36        }
37
38        // Unlaunched sites get the "Launch site" button instead.
39        if ( 'unlaunched' === get_option( 'launch-status' ) ) {
40            return null;
41        }
42
43        $home_host = wp_parse_url( home_url(), PHP_URL_HOST );
44        if ( ! is_string( $home_host ) || ! str_ends_with( $home_host, '.wordpress.com' ) ) {
45            return null;
46        }
47
48        $current_plan = self::get_current_plan();
49        if ( ! is_array( $current_plan ) ) {
50            return null;
51        }
52
53        if ( ! empty( $current_plan['is_free'] ) && empty( get_option( 'options' )['is_domain_only'] ) ) {
54            return 'free_to_paid_plan';
55        }
56
57        $product_slug = (string) ( $current_plan['product_slug'] ?? '' );
58        if ( preg_match( '/[-_]monthly$/', $product_slug ) && ! str_contains( $product_slug, 'trial' ) ) {
59            return 'monthly_to_annual_plan';
60        }
61
62        return null;
63    }
64
65    /**
66     * Whether the current user/site is eligible for the omnibar free-domain upsell.
67     *
68     * @return bool
69     */
70    public static function is_eligible() {
71        return null !== self::get_upsell_source();
72    }
73
74    /**
75     * Whether the current user should see the omnibar free-domain upsell chip.
76     *
77     * @return bool
78     */
79    public static function should_show_upsell() {
80        return self::is_eligible() && 'treatment' === self::get_variation();
81    }
82
83    /**
84     * Whether the winning sidebar notice should be suppressed for this user.
85     *
86     * @param string $notice_id The id of the winning sidebar notice.
87     * @return bool
88     */
89    public static function should_suppress_sidebar_notice( $notice_id ) {
90        if ( ! in_array( $notice_id, array( 'free_to_paid_plan', 'monthly_to_annual_plan' ), true ) ) {
91            return false;
92        }
93
94        return self::should_show_upsell();
95    }
96
97    /**
98     * The current plan data for this blog, or null when unavailable.
99     *
100     * @return array|null
101     */
102    private static function get_current_plan() {
103        if ( ! class_exists( '\WPCOM_Store_API' ) ) {
104            return null;
105        }
106
107        $plan = \WPCOM_Store_API::get_current_plan( get_current_blog_id() );
108
109        return is_array( $plan ) ? $plan : null;
110    }
111
112    /**
113     * The current user's variation: 'control' or 'treatment'.
114     *
115     * @return string
116     */
117    public static function get_variation() {
118        $user_id = get_current_user_id();
119        if ( ! $user_id ) {
120            return 'control';
121        }
122
123        return self::normalize( self::fetch_variation() );
124    }
125
126    /**
127     * Fetch the raw variation name from ExPlat, or null.
128     *
129     * @return string|null
130     */
131    private static function fetch_variation() {
132        if ( ! ( new Host() )->is_wpcom_simple() ) {
133            return null;
134        }
135
136        if ( function_exists( '\ExPlat\assign_current_user' ) ) {
137            return \ExPlat\assign_current_user( self::EXPERIMENT_NAME );
138        }
139
140        return null;
141    }
142
143    /**
144     * Map any variation onto a known value; unknown/null becomes 'control'.
145     *
146     * @param string|null $variation The raw variation name.
147     * @return string
148     */
149    private static function normalize( $variation ) {
150        return 'treatment' === $variation ? 'treatment' : 'control';
151    }
152}