Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.12% covered (success)
94.12%
48 / 51
57.14% covered (warning)
57.14%
4 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Premium_Features
94.12% covered (success)
94.12%
48 / 51
57.14% covered (warning)
57.14%
4 / 7
21.09
0.00% covered (danger)
0.00%
0 / 1
 record_cloud_css_baseline
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
3.03
 remember_cloud_css_baseline
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 enable_cloud_css_after_upgrade
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
6
 has_feature
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 get_features
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
5
 has_any
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 clear_cache
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace Automattic\Jetpack_Boost\Lib;
4
5use Automattic\Jetpack\Boost_Core\Lib\Boost_API;
6use Automattic\Jetpack\Boost_Core\Lib\Transient;
7use Automattic\Jetpack_Boost\Data_Sync\Modules_State_Entry;
8use Automattic\Jetpack_Boost\Modules\Module;
9use Automattic\Jetpack_Boost\Modules\Optimizations\Cloud_CSS\Cloud_CSS;
10
11class Premium_Features {
12
13    const CLOUD_CSS             = 'cloud-critical-css';
14    const PERFORMANCE_HISTORY   = 'performance-history';
15    const IMAGE_CDN_LIAR        = 'image-cdn-liar';
16    const IMAGE_CDN_QUALITY     = 'image-cdn-quality';
17    const PRIORITY_SUPPORT      = 'support';
18    const PAGE_CACHE            = 'page-cache';
19    const CORNERSTONE_TEN_PAGES = 'cornerstone-10-pages';
20
21    const TRANSIENT_KEY              = 'premium_features';
22    const CLOUD_CSS_BASELINE_OPTION  = 'jetpack_boost_cloud_css_plan_baseline';
23    const CLOUD_CSS_ACTIVATED_OPTION = 'jetpack_boost_cloud_css_plan_activated';
24
25    /**
26     * Record a verified plan baseline when the site connects, without activating Cloud CSS.
27     *
28     * @since $$next-version$$
29     */
30    public static function record_cloud_css_baseline() {
31        if ( false !== get_option( self::CLOUD_CSS_BASELINE_OPTION ) ) {
32            return;
33        }
34        self::clear_cache();
35        $verified = null;
36        $features = self::get_features( $verified );
37        if ( $verified ) {
38            self::remember_cloud_css_baseline( $features );
39        }
40    }
41
42    private static function remember_cloud_css_baseline( $features ) {
43        // A stored Cloud CSS choice survives a plan that lapsed before the first observation.
44        $has_cloud_css_choice = null !== get_option( Status::get_option_name( Cloud_CSS::get_slug() ), null );
45        $baseline             = $has_cloud_css_choice || in_array( self::CLOUD_CSS, $features, true ) ? 'premium' : 'free';
46        add_option( self::CLOUD_CSS_BASELINE_OPTION, $baseline, '', false );
47    }
48
49    /**
50     * Enable Cloud CSS once when an observed free site gains the feature.
51     *
52     * @since $$next-version$$
53     */
54    public static function enable_cloud_css_after_upgrade() {
55        $verified = null;
56        $features = self::get_features( $verified );
57        if ( ! $verified ) {
58            return;
59        }
60        $has_cloud_css = in_array( self::CLOUD_CSS, $features, true );
61
62        self::remember_cloud_css_baseline( $features );
63        if ( ! $has_cloud_css || 'free' !== get_option( self::CLOUD_CSS_BASELINE_OPTION ) ) {
64            return;
65        }
66        if ( ! ( new Module( new Cloud_CSS() ) )->is_available() ) {
67            return;
68        }
69
70        // Claim the upgrade before activation can re-enter feature checks or another request can run it.
71        if ( ! add_option( self::CLOUD_CSS_ACTIVATED_OPTION, true, '', false ) ) {
72            return;
73        }
74
75        $entry = new Modules_State_Entry( array( Cloud_CSS::class ) );
76        $entry->set( array( Cloud_CSS::get_slug() => array( 'active' => true ) ) );
77    }
78
79    public static function has_feature( $feature ) {
80        $features = self::get_features();
81
82        if ( is_array( $features ) ) {
83            return in_array( $feature, $features, true );
84        }
85        return false;
86    }
87
88    /**
89     * Get features and report whether a valid response was fetched in this call.
90     *
91     * @param bool|null $verified Receives whether a valid unfiltered response was fetched in this call.
92     * @return string[]
93     */
94    public static function get_features( &$verified = null ) {
95        $available_features = Transient::get( self::TRANSIENT_KEY, false );
96        $verified           = false;
97        $all_features       = array(
98            self::CLOUD_CSS,
99            self::IMAGE_CDN_LIAR,
100            self::IMAGE_CDN_QUALITY,
101            self::PERFORMANCE_HISTORY,
102            self::PRIORITY_SUPPORT,
103            self::CORNERSTONE_TEN_PAGES,
104        );
105
106        if ( ! is_array( $available_features ) ) {
107            $available_features = Boost_API::get( 'features' );
108            $verified           = is_array( $available_features );
109            if ( ! is_array( $available_features ) ) {
110                $available_features = array();
111            }
112            Transient::set( self::TRANSIENT_KEY, $available_features, 3 * DAY_IN_SECONDS );
113        }
114
115        $features = array();
116        // Prepare a list of features after applying jetpack_boost_has_feature_* filter for each feature.
117        foreach ( $all_features as $feature ) {
118            /**
119             * Filter the availability of a feature
120             *
121             * @param bool $has_feature if the feature is available
122             *
123             * @since   1.0.0
124             */
125            if ( apply_filters( "jetpack_boost_has_feature_{$feature}", in_array( $feature, $available_features, true ) ) ) {
126                $features[] = $feature;
127            }
128        }
129
130        return $features;
131    }
132
133    public static function has_any() {
134        return count( self::get_features() ) > 0;
135    }
136
137    public static function clear_cache() {
138        Transient::delete( self::TRANSIENT_KEY );
139    }
140}