PluginProbe
ElasticPress / 4.7.2
ElasticPress v4.7.2
5.3.5 5.3.4 3.6.5 3.6.6 4.0.0 4.0.1 4.1.0 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.6.0 4.6.1 4.7.0 4.7.1 4.7.2 5.0.0 5.0.1 5.0.2 All 108 releases
elasticpress / includes / classes / Features.php

Features.php in ElasticPress 4.7.2, at includes/classes/Features.php

336 lines 8.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles registering and storing feature instances
4 *
5 * @since 2.1
6 * @package elasticpress
7 */
8
9 namespace ElasticPress;
10
11 use ElasticPress\Utils;
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit; // Exit if accessed directly.
15 }
16
17 /**
18 * Class for storing and managing features
19 */
20 class Features {
21
22 /**
23 * Stores all features that have been properly included (both active and inactive)
24 *
25 * @since 2.1
26 * @var array
27 */
28 public $registered_features = [];
29
30 /**
31 * Initiate class actions
32 *
33 * @since 2.1
34 */
35 public function setup() {
36 // hooks order matters, make sure feature activation goes before features setup
37 add_action( 'init', array( $this, 'handle_feature_activation' ), 0 );
38 add_action( 'init', array( $this, 'setup_features' ), 0 );
39 }
40
41 /**
42 * Activate a feature
43 *
44 * @param string $slug Feature slug
45 * @since 2.2
46 */
47 public function activate_feature( $slug ) {
48 $this->update_feature( $slug, array( 'active' => true ) );
49 }
50
51 /**
52 * Deactivate a feature
53 *
54 * @param string $slug Feature slug
55 * @param bool $force Whether to force deactivation
56 * @since 2.2
57 */
58 public function deactivate_feature( $slug, $force = true ) {
59 $this->update_feature( $slug, array( 'active' => false ), $force );
60 }
61
62 /**
63 * Registers a feature for use in ElasticPress
64 *
65 * @param Feature $feature An instance of the Feature class
66 * @since 3.0
67 * @return boolean
68 */
69 public function register_feature( Feature $feature ) {
70 $this->registered_features[ $feature->slug ] = $feature;
71 return true;
72 }
73
74 /**
75 * Easy access function to get a Feature object from a slug
76 *
77 * @param string $slug Feature slug
78 * @since 2.1
79 * @return Feature
80 */
81 public function get_registered_feature( $slug ) {
82 if ( empty( $this->registered_features[ $slug ] ) ) {
83 return false;
84 }
85
86 return $this->registered_features[ $slug ];
87 }
88
89 /**
90 * Activate or deactivate a feature
91 *
92 * @param string $slug Feature slug
93 * @param array $settings Array of settings
94 * @param bool $force Whether to force activate/deactivate
95 * @since 2.2
96 * @return array|bool
97 */
98 public function update_feature( $slug, $settings, $force = true ) {
99 /**
100 * Get the feature being saved.
101 */
102 $feature = $this->get_registered_feature( $slug );
103
104 if ( empty( $feature ) ) {
105 return false;
106 }
107
108 /**
109 * Get whether the feature was already active, and the value of the
110 * setting that requires a reindex, if it exists.
111 */
112 $was_active = $feature->is_active();
113 $setting_was = $feature->get_reindex_setting();
114
115 /**
116 * Prepare settings
117 */
118 $saved_settings = Utils\get_option( 'ep_feature_settings', [] );
119 $feature_settings = isset( $saved_settings[ $slug ] ) ? $saved_settings[ $slug ] : [ 'force_inactive' => false ];
120
121 $new_feature_settings = $feature->default_settings;
122 $new_feature_settings = wp_parse_args( $feature_settings, $new_feature_settings );
123 $new_feature_settings = wp_parse_args( $settings, $new_feature_settings );
124
125 $new_feature_settings['active'] = (bool) $new_feature_settings['active'];
126 $new_feature_settings['force_inactive'] = $new_feature_settings['active'] ? false : (bool) $new_feature_settings['force_inactive'];
127
128 /**
129 * Flag if the feature was deactivated by a forced update.
130 */
131 if ( $force && $was_active && ! $new_feature_settings['active'] ) {
132 $new_feature_settings['force_inactive'] = true;
133 }
134
135 /**
136 * Save the settings.
137 */
138 $new_settings = wp_parse_args( [ $slug => $new_feature_settings ], $saved_settings );
139 $new_settings = apply_filters( 'ep_sanitize_feature_settings', $new_settings, $feature );
140
141 Utils\update_option( 'ep_feature_settings', $new_settings );
142
143 /**
144 * Prepare response.
145 */
146 $is_active = $new_settings[ $slug ]['active'];
147
148 $data = array(
149 'active' => $is_active,
150 'reindex' => false,
151 'setting' => '',
152 );
153
154 /**
155 * If the feature requires reindexing on activation, return whether
156 * reindexing is required.
157 */
158 if ( $is_active && ! $was_active ) {
159 if ( ! empty( $feature->requires_install_reindex ) ) {
160 $data['reindex'] = true;
161 }
162
163 $feature->post_activation();
164 }
165
166 /**
167 * If the feature has a setting that requires reindexing, return
168 * whether reindexing is required and the new value of the setting.
169 */
170 $setting = $feature->setting_requires_install_reindex;
171
172 if ( $setting ) {
173 $setting_is = ! empty( $new_settings[ $slug ][ $setting ] )
174 ? $new_settings[ $slug ][ $setting ]
175 : '';
176
177 $data['setting'] = $setting_is;
178
179 /**
180 * If the setting has changed, a reindex is required.
181 */
182 if ( $is_active && $setting_is && $setting_is !== $setting_was ) {
183 $data['reindex'] = true;
184 }
185 }
186
187 /**
188 * Fires after activating, inactivating, or just updating a feature.
189 *
190 * @hook ep_after_update_feature
191 * @param {string} $feature Feature slug
192 * @param {array} $settings Feature settings
193 * @param {array} $data Feature activation data
194 *
195 * @since 3.5.5
196 */
197 do_action(
198 'ep_after_update_feature',
199 $slug,
200 $settings,
201 $data
202 );
203
204 return $data;
205 }
206
207 /**
208 * When plugins are adjusted, we need to determine how to activate/deactivate features
209 *
210 * @since 2.2
211 */
212 public function handle_feature_activation() {
213 /**
214 * Save our current requirement statuses for later
215 */
216
217 $old_requirement_statuses = Utils\get_option( 'ep_feature_requirement_statuses', false );
218
219 $new_requirement_statuses = [];
220
221 foreach ( $this->registered_features as $slug => $feature ) {
222 $status = $feature->requirements_status();
223 $new_requirement_statuses[ $slug ] = (int) $status->code;
224 }
225
226 $is_wp_cli = defined( 'WP_CLI' ) && \WP_CLI;
227
228 if ( $is_wp_cli || is_admin() ) {
229 Utils\update_option( 'ep_feature_requirement_statuses', $new_requirement_statuses );
230 }
231
232 /**
233 * If feature settings aren't created, let's create them and finish
234 */
235
236 $feature_settings = Utils\get_option( 'ep_feature_settings', false );
237
238 if ( false === $feature_settings ) {
239 $registered_features = $this->registered_features;
240
241 foreach ( $registered_features as $slug => $feature ) {
242 if ( 0 === $feature->requirements_status()->code ) {
243 $this->activate_feature( $slug );
244 }
245 }
246
247 /**
248 * Nothing else to do since we are doing initial activation
249 */
250 return;
251 }
252
253 /**
254 * If a requirement status changes, we need to handle that by activating/deactivating/showing notification
255 */
256
257 if ( ( $is_wp_cli || is_admin() ) && ! empty( $old_requirement_statuses ) ) {
258 foreach ( $new_requirement_statuses as $slug => $code ) {
259 $feature = $this->get_registered_feature( $slug );
260
261 // If a feature is forced inactive, do nothing
262 $feature_settings = $feature->get_settings();
263 if ( is_array( $feature_settings ) && ! empty( $feature_settings['force_inactive'] ) ) {
264 continue;
265 }
266
267 // This is a new feature
268 if ( ! isset( $old_requirement_statuses[ $slug ] ) ) {
269 if ( 0 === $code ) {
270 $this->activate_feature( $slug );
271
272 if ( $feature->requires_install_reindex ) {
273 Utils\update_option( 'ep_feature_auto_activated_sync', sanitize_text_field( $slug ) );
274 }
275 }
276 } else {
277 // This feature has a 0 "ok" code when it did not before
278 if ( $old_requirement_statuses[ $slug ] !== $code && ( 0 === $code || 2 === $code ) ) {
279 $active = ( 0 === $code );
280
281 if ( ! $feature->is_active() && $active ) {
282 $this->activate_feature( $slug );
283
284 // Need to activate and maybe set a sync notice
285 if ( $feature->requires_install_reindex ) {
286 Utils\update_option( 'ep_feature_auto_activated_sync', sanitize_text_field( $slug ) );
287 }
288 } elseif ( $feature->is_active() && ! $active ) {
289 // Just deactivate, don't force
290 $this->deactivate_feature( $slug, false );
291 }
292 }
293 }
294 }
295 }
296 }
297
298 /**
299 * Set up all active features
300 *
301 * @since 2.1
302 */
303 public function setup_features() {
304 /**
305 * Fires before features are setup
306 *
307 * @hook ep_setup_features
308 * @since 2.1
309 */
310 do_action( 'ep_setup_features' );
311
312 foreach ( $this->registered_features as $feature_slug => $feature ) {
313 if ( $feature->is_active() ) {
314 $feature->setup();
315 }
316 }
317 }
318
319 /**
320 * Return singleton instance of class
321 *
322 * @return object
323 * @since 2.1
324 */
325 public static function factory() {
326 static $instance = false;
327
328 if ( ! $instance ) {
329 $instance = new self();
330 $instance->setup();
331 }
332
333 return $instance;
334 }
335 }
336