Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
58.21% |
39 / 67 |
|
33.33% |
5 / 15 |
CRAP | |
0.00% |
0 / 1 |
| Module | |
58.21% |
39 / 67 |
|
33.33% |
5 / 15 |
200.44 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
| on_activate | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
12 | |||
| on_deactivate | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
20 | |||
| indicate_page_output_changed | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| get_slug | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| get_submodules | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
| get_available_submodules | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
30 | |||
| is_disabled_dev_feature | |
66.67% |
4 / 6 |
|
0.00% |
0 / 1 |
3.33 | |||
| get_active_parent_modules | |
88.89% |
8 / 9 |
|
0.00% |
0 / 1 |
4.02 | |||
| update | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| is_enabled | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
| is_always_on | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| is_available | |
87.50% |
7 / 8 |
|
0.00% |
0 / 1 |
7.10 | |||
| is_force_disabled | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
| is_optimizing | |
72.73% |
8 / 11 |
|
0.00% |
0 / 1 |
9.30 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace Automattic\Jetpack_Boost\Modules; |
| 4 | |
| 5 | use Automattic\Jetpack\Boost\App\Contracts\Is_Dev_Feature; |
| 6 | use Automattic\Jetpack_Boost\Contracts\Changes_Output_After_Activation; |
| 7 | use Automattic\Jetpack_Boost\Contracts\Changes_Output_On_Activation; |
| 8 | use Automattic\Jetpack_Boost\Contracts\Feature; |
| 9 | use Automattic\Jetpack_Boost\Contracts\Has_Activate; |
| 10 | use Automattic\Jetpack_Boost\Contracts\Has_Deactivate; |
| 11 | use Automattic\Jetpack_Boost\Contracts\Needs_To_Be_Ready; |
| 12 | use Automattic\Jetpack_Boost\Contracts\Optimization; |
| 13 | use Automattic\Jetpack_Boost\Contracts\Sub_Feature; |
| 14 | use Automattic\Jetpack_Boost\Lib\Status; |
| 15 | |
| 16 | class Module { |
| 17 | const DISABLE_MODULE_QUERY_VAR = 'jb-disable-modules'; |
| 18 | |
| 19 | /** |
| 20 | * @var Status |
| 21 | */ |
| 22 | private $status; |
| 23 | |
| 24 | /** |
| 25 | * @var Feature |
| 26 | */ |
| 27 | public $feature; |
| 28 | |
| 29 | public function __construct( Feature $feature ) { |
| 30 | $this->feature = $feature; |
| 31 | $this->status = new Status( $feature::get_slug() ); |
| 32 | } |
| 33 | |
| 34 | public function on_activate() { |
| 35 | if ( $this->feature instanceof Changes_Output_On_Activation ) { |
| 36 | $this->indicate_page_output_changed(); |
| 37 | } |
| 38 | |
| 39 | return $this->feature instanceof Has_Activate ? $this->feature::activate() : true; |
| 40 | } |
| 41 | |
| 42 | public function on_deactivate() { |
| 43 | // If the module changes the page output, with or without preparation, deactivating the module should indicate a page output change. |
| 44 | if ( $this->feature instanceof Changes_Output_On_Activation || $this->feature instanceof Changes_Output_After_Activation ) { |
| 45 | $this->indicate_page_output_changed(); |
| 46 | } |
| 47 | |
| 48 | return $this->feature instanceof Has_Deactivate ? $this->feature::deactivate() : true; |
| 49 | } |
| 50 | |
| 51 | public function indicate_page_output_changed() { |
| 52 | /** |
| 53 | * Indicate that the HTML output of front-end has changed. |
| 54 | * |
| 55 | * If there is any page cache, it should be invalidated when this action is triggered. |
| 56 | */ |
| 57 | do_action( 'jetpack_boost_page_output_changed' ); |
| 58 | } |
| 59 | |
| 60 | public function get_slug() { |
| 61 | return $this->feature::get_slug(); |
| 62 | } |
| 63 | |
| 64 | /** |
| 65 | * If the module has any submodules, this method will return an array of Module instances for each submodule. |
| 66 | */ |
| 67 | public function get_submodules() { |
| 68 | $subfeatures = Features_Index::get_sub_features_of( $this->feature ); |
| 69 | |
| 70 | $modules = array(); |
| 71 | foreach ( $subfeatures as $subfeature ) { |
| 72 | $modules[ $subfeature::get_slug() ] = new Module( new $subfeature() ); |
| 73 | } |
| 74 | |
| 75 | return $modules; |
| 76 | } |
| 77 | |
| 78 | public function get_available_submodules() { |
| 79 | $submodules = $this->get_submodules(); |
| 80 | if ( empty( $submodules ) ) { |
| 81 | return array(); |
| 82 | } |
| 83 | |
| 84 | $available_submodules = array(); |
| 85 | foreach ( $submodules as $slug => $submodule ) { |
| 86 | if ( $submodule->is_available() && ! $this->is_disabled_dev_feature( $submodule->feature ) ) { |
| 87 | $available_submodules[ $slug ] = $submodule; |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | return $available_submodules; |
| 92 | } |
| 93 | |
| 94 | /** |
| 95 | * Check if the feature is disabled in development. |
| 96 | * |
| 97 | * Returns true if the feature is a dev feature and the dev features should be disabled. |
| 98 | * |
| 99 | * @param Feature $feature The feature to check. |
| 100 | * @return bool True if the feature is available, false otherwise. |
| 101 | */ |
| 102 | private function is_disabled_dev_feature( $feature ) { |
| 103 | // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.ValidatedSanitizedInput.MissingUnslash |
| 104 | $is_disabled_dev_feature = false === strpos( $_SERVER['HTTP_HOST'] ?? '', 'jurassic.ninja' ); |
| 105 | if ( defined( 'JETPACK_BOOST_DEVELOPMENT_FEATURES' ) ) { |
| 106 | $is_disabled_dev_feature = ! JETPACK_BOOST_DEVELOPMENT_FEATURES; |
| 107 | } |
| 108 | |
| 109 | if ( $feature instanceof Is_Dev_Feature ) { |
| 110 | return $is_disabled_dev_feature; |
| 111 | } |
| 112 | |
| 113 | return false; |
| 114 | } |
| 115 | |
| 116 | /** |
| 117 | * Get the active parent modules. |
| 118 | * |
| 119 | * @return Module[] The active parent modules. |
| 120 | */ |
| 121 | public function get_active_parent_modules() { |
| 122 | if ( ! $this->feature instanceof Sub_Feature ) { |
| 123 | return array(); |
| 124 | } |
| 125 | |
| 126 | $parent_features = $this->feature->get_parent_features(); |
| 127 | $modules = array(); |
| 128 | foreach ( $parent_features as $parent_feature ) { |
| 129 | $parent_module = new Module( new $parent_feature() ); |
| 130 | if ( $parent_module->is_enabled() ) { |
| 131 | $modules[ $parent_module->get_slug() ] = $parent_module; |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | return $modules; |
| 136 | } |
| 137 | |
| 138 | public function update( $new_status ) { |
| 139 | return $this->status->set( $new_status ); |
| 140 | } |
| 141 | |
| 142 | /** |
| 143 | * Check if the module is enabled. |
| 144 | * |
| 145 | * If the module is always on, it is enabled. Otherwise, check database for the module status. |
| 146 | * If it's a submodule, the status is only about the submodule itself, not its parent modules. |
| 147 | * |
| 148 | * @return bool True if the module is enabled, false otherwise. |
| 149 | */ |
| 150 | public function is_enabled() { |
| 151 | if ( $this->is_always_on() ) { |
| 152 | return true; |
| 153 | } |
| 154 | |
| 155 | return $this->status->get(); |
| 156 | } |
| 157 | |
| 158 | /** |
| 159 | * Check if the module's feature implements Is_Always_On. |
| 160 | * |
| 161 | * Always-on modules cannot be disabled: is_enabled() short-circuits to true |
| 162 | * for them regardless of the persisted option, so writes via update() are |
| 163 | * silently overridden. Callers writing module state should bail before the |
| 164 | * write rather than letting on-disk state diverge from runtime state. |
| 165 | * |
| 166 | * @return bool |
| 167 | */ |
| 168 | public function is_always_on(): bool { |
| 169 | return is_subclass_of( $this->feature, 'Automattic\Jetpack_Boost\Contracts\Is_Always_On' ); |
| 170 | } |
| 171 | |
| 172 | /** |
| 173 | * Check if the module is available. |
| 174 | * |
| 175 | * If the module is not available, it cannot be enabled. |
| 176 | */ |
| 177 | public function is_available() { |
| 178 | if ( ! $this->feature::is_available() || $this->is_disabled_dev_feature( $this->feature ) || $this->is_force_disabled() ) { |
| 179 | return false; |
| 180 | } |
| 181 | |
| 182 | // If the module is not a sub-module, and it already passed the availability check, it is available. |
| 183 | if ( ! $this->feature instanceof Sub_Feature ) { |
| 184 | return true; |
| 185 | } |
| 186 | |
| 187 | // If the module is a sub-module, it is available if at least one of its parent modules is available. |
| 188 | foreach ( $this->feature::get_parent_features() as $parent_feature ) { |
| 189 | if ( ( new Module( new $parent_feature() ) )->is_available() ) { |
| 190 | return true; |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | return false; |
| 195 | } |
| 196 | |
| 197 | private function is_force_disabled() { |
| 198 | $slug = $this->feature::get_slug(); |
| 199 | |
| 200 | // phpcs:disable WordPress.Security.NonceVerification.Recommended |
| 201 | if ( ! empty( $_GET[ self::DISABLE_MODULE_QUERY_VAR ] ) ) { |
| 202 | // phpcs:disable WordPress.Security.NonceVerification.Recommended |
| 203 | // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash |
| 204 | // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized |
| 205 | $disabled_modules = array_map( 'sanitize_key', explode( ',', $_GET[ self::DISABLE_MODULE_QUERY_VAR ] ) ); |
| 206 | return in_array( $slug, $disabled_modules, true ) || in_array( 'all', $disabled_modules, true ); |
| 207 | } |
| 208 | |
| 209 | return false; |
| 210 | } |
| 211 | |
| 212 | /** |
| 213 | * Check if the module is active and ready to serve optimized output. |
| 214 | */ |
| 215 | public function is_optimizing() { |
| 216 | if ( ! $this->is_available() ) { |
| 217 | return false; |
| 218 | } |
| 219 | |
| 220 | if ( ! ( $this->feature instanceof Optimization ) || ! $this->is_enabled() ) { |
| 221 | return false; |
| 222 | } |
| 223 | |
| 224 | if ( $this->feature instanceof Needs_To_Be_Ready && ! $this->feature->is_ready() ) { |
| 225 | return false; |
| 226 | } |
| 227 | |
| 228 | if ( $this->feature instanceof Sub_Feature ) { |
| 229 | $parent_modules = $this->get_active_parent_modules(); |
| 230 | if ( empty( $parent_modules ) ) { |
| 231 | return false; |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | return true; |
| 236 | } |
| 237 | } |