PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.3.2
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.3.2
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / includes / class-wpo-ajax.php

class-wpo-ajax.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 3.3.2, at includes/class-wpo-ajax.php

448 lines 11.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) die('Access denied.');
4
5 if (!class_exists('WPO_Ajax')) :
6
7 class WPO_Ajax {
8
9 private $nonce;
10
11 private $subaction;
12
13 private $data;
14
15 private $commands;
16
17 private $results;
18
19 const HEARTBEAT_INTERVAL = 15; // in seconds
20
21 /**
22 * Constructor
23 */
24 private function __construct() {
25 add_action('wp_ajax_wp_optimize_ajax', array($this, 'handle_ajax_requests'));
26
27 add_filter('wp_optimize_heartbeat', array($this, 'handle_heartbeat_requests'), 10, 1);
28 }
29
30 /**
31 * Return singleton instance
32 *
33 * @return WPO_Ajax Returns WPO_Ajax object
34 */
35 public static function get_instance() {
36 static $instance = null;
37 if (null === $instance) {
38 $instance = new self();
39 }
40 return $instance;
41 }
42
43 /**
44 * Handles heartbeat requests
45 *
46 * @param string $action The action we want to run
47 * @return mixed
48 */
49 public function handle_heartbeat_requests($action) {
50 $this->set_heartbeat_subaction($action);
51
52 if (!$this->is_user_capable()) {
53 return json_encode($this->send_user_capability_error_response(false));
54 }
55
56 if (is_multisite() && !current_user_can('manage_network_options')) {
57 if (!$this->is_valid_multisite_command()) {
58 return json_encode($this->send_invalid_multisite_command_error_response(false));
59 }
60 }
61
62 $this->set_commands();
63 if ($this->is_invalid_command()) {
64 $this->add_invalid_command_error_log_entry();
65 $this->set_invalid_command_error_response();
66 } else {
67 $this->execute_command();
68 $this->maybe_fix_status_box_content();
69 $this->set_error_response_on_wp_error();
70 $this->maybe_set_results_as_null();
71 }
72
73 $this->json_encode_results();
74
75 $json_last_error = json_last_error();
76 if ($json_last_error) {
77 $this->set_error_response_on_json_encode_error($json_last_error);
78 }
79
80 return $this->results;
81 }
82
83 /**
84 * Handles ajax requests
85 *
86 * @return void
87 */
88 public function handle_ajax_requests() {
89 $this->set_nonce();
90 $this->set_subaction();
91 $this->set_data();
92
93 if (!$this->is_valid_request()) {
94 $this->send_security_check_failed_error_response();
95 }
96
97 if (!$this->is_user_capable()) {
98 $this->send_user_capability_error_response();
99 }
100
101 if (is_multisite() && !current_user_can('manage_network_options')) {
102 if (!$this->is_valid_multisite_command()) {
103 $this->send_invalid_multisite_command_error_response();
104 }
105 }
106
107 if ($this->is_subaction_a_dismissed_notice()) {
108 $this->handle_notice_dismissals();
109 } else {
110 $this->set_commands();
111 if ($this->is_invalid_command()) {
112 $this->add_invalid_command_error_log_entry();
113 $this->set_invalid_command_error_response();
114 } else {
115 $this->execute_command();
116 $this->maybe_fix_status_box_content();
117 $this->set_error_response_on_wp_error();
118 $this->maybe_set_results_as_null();
119 }
120 }
121
122 $this->json_encode_results();
123
124 $json_last_error = json_last_error();
125 if ($json_last_error) {
126 $this->set_error_response_on_json_encode_error($json_last_error);
127 }
128
129 echo $this->results;
130 die;
131 }
132
133 /**
134 * Sets nonce property value
135 */
136 private function set_nonce() {
137 $this->nonce = empty($_POST['nonce']) ? '' : $_POST['nonce'];
138 }
139
140 /**
141 * Sets subaction property value
142 */
143 private function set_subaction() {
144 $this->subaction = empty($_POST['subaction']) ? '' : stripcslashes($_POST['subaction']);
145 }
146
147 /**
148 * Sets heartbeat subaction property value
149 *
150 * @param string $action_name The name of the heartbeat action to run
151 */
152 private function set_heartbeat_subaction($action_name) {
153 $this->subaction = $action_name;
154 }
155
156 /**
157 * Sets data property value
158 */
159 private function set_data() {
160 $this->data = isset($_POST['data']) ? stripslashes_deep($_POST['data']) : null;
161 }
162
163 /**
164 * Checks whether the request is valid or not
165 *
166 * @return bool
167 */
168 private function is_valid_request() {
169 return wp_verify_nonce($this->nonce, 'wp-optimize-ajax-nonce') && !empty($this->subaction);
170 }
171
172 /**
173 * Send security check failed error response to browser and die
174 */
175 private function send_security_check_failed_error_response() {
176 wp_send_json(array(
177 'result' => false,
178 'error_code' => 'security_check',
179 'error_message' => __('The security check failed; try refreshing the page.', 'wp-optimize')
180 ));
181 }
182
183
184 /**
185 * Checks whether current user capable of doing this action or not
186 *
187 * @return bool
188 */
189 private function is_user_capable() {
190 return current_user_can(WP_Optimize()->capability_required());
191 }
192
193 /**
194 * Send user capability check failed error response to browser and possibly die
195 *
196 * @param Boolean $send - if true, then the response is output; otherwise, it is returned
197 */
198 private function send_user_capability_error_response($send = true) {
199 $error = array(
200 'result' => false,
201 'error_code' => 'security_check',
202 'error_message' => __('You are not allowed to run this command.', 'wp-optimize')
203 );
204
205 if (true == $send) {
206 wp_send_json($error);
207 } else {
208 return $error;
209 }
210 }
211
212 /**
213 * Checks whether subaction is a valid multisite command
214 *
215 * @return bool
216 */
217 private function is_valid_multisite_command() {
218 /**
219 * Filters the commands allowed to the sub site admins. Other commands are only available to network admin. Only used in a multisite context.
220 */
221 $allowed_multisite_commands = apply_filters('wpo_multisite_allowed_commands', array('check_server_status', 'compress_single_image', 'restore_single_image'));
222 return in_array($this->subaction, $allowed_multisite_commands);
223 }
224
225 /**
226 * Send invalid multisite command error response to browser and die
227 */
228 private function send_invalid_multisite_command_error_response($send = true) {
229 $error = array(
230 'result' => false,
231 'error_code' => 'update_failed',
232 'error_message' => __('Options can only be saved by network admin', 'wp-optimize')
233 );
234
235 if (true == $send) {
236 wp_send_json($error);
237 } else {
238 return $error;
239 }
240 }
241
242 /**
243 * Checks if subaction is a notice dismissal or not
244 *
245 * @return bool True for notice dismiss actions, false otherwise
246 */
247 private function is_subaction_a_dismissed_notice() {
248 $dismiss_actions = $this->get_dismiss_actions();
249 return in_array($this->subaction, $dismiss_actions);
250 }
251
252 /**
253 * Returns an array of notice dismiss action names
254 *
255 * @return array An array of notice dismiss actions
256 */
257 private function get_dismiss_actions() {
258 return array(
259 'dismiss_dash_notice_until',
260 'dismiss_season',
261 'dismiss_page_notice_until',
262 'dismiss_notice',
263 'dismiss_review_notice',
264 );
265 }
266
267 /**
268 * Handles notice dismissals
269 */
270 private function handle_notice_dismissals() {
271 $options = WP_Optimize()->get_options();
272 // Some commands that are available via AJAX only.
273 if (in_array($this->subaction, array('dismiss_dash_notice_until', 'dismiss_season'))) {
274 $options->update_option($this->subaction, (time() + 366 * 86400));
275 } elseif (in_array($this->subaction, array('dismiss_page_notice_until', 'dismiss_notice'))) {
276 $options->update_option($this->subaction, (time() + 84 * 86400));
277 } elseif ('dismiss_review_notice' == $this->subaction) {
278 if (empty($this->data['dismiss_forever'])) {
279 $options->update_option($this->subaction, time() + 84 * 86400);
280 } else {
281 $options->update_option($this->subaction, 100 * (365.25 * 86400));
282 }
283 }
284 }
285
286 /**
287 * Sets commands property value
288 */
289 private function set_commands() {
290 $this->commands = new WP_Optimize_Commands();
291
292 $minify_commands = $this->get_minify_commands();
293 if ($this->is_subaction_a_minify_command($minify_commands)) {
294 $this->commands = $minify_commands;
295 }
296
297 $cache_commands = $this->get_cache_commands();
298 if ($this->is_subaction_a_cache_command($cache_commands)) {
299 $this->commands = $cache_commands;
300 }
301 }
302
303 /**
304 * Gets minify commands
305 *
306 * @return WP_Optimize_Minify_Commands
307 */
308 private function get_minify_commands() {
309 return new WP_Optimize_Minify_Commands();
310 }
311
312 /**
313 * Gets cache commands
314 *
315 * @return WP_Optimize_Cache_Commands|WP_Optimize_Cache_Commands_Premium
316 */
317 private function get_cache_commands() {
318 if (WP_Optimize::is_premium()) {
319 $cache_commands = new WP_Optimize_Cache_Commands_Premium();
320 } else {
321 $cache_commands = new WP_Optimize_Cache_Commands();
322 }
323 return $cache_commands;
324 }
325
326 /**
327 * Checks if applied ajax command is a minify command or not
328 *
329 * @param WP_Optimize_Minify_Commands $minify_commands an instance of minify commands class
330 *
331 * @return bool Returns true if ajax command is a minify command, false otherwise
332 */
333 private function is_subaction_a_minify_command($minify_commands) {
334 return !is_callable(array($this->commands, $this->subaction)) && is_callable(array($minify_commands, $this->subaction));
335 }
336
337 /**
338 * Checks if applied ajax command is a cache command or not
339 *
340 * @param WP_Optimize_Cache_Commands|WP_Optimize_Cache_Commands_Premium $cache_commands an instance of cache commands
341 *
342 * @return bool Returns true if ajax command is a cache command, false otherwise
343 */
344 private function is_subaction_a_cache_command($cache_commands) {
345 return !is_callable(array($this->commands, $this->subaction)) && is_callable(array($cache_commands, $this->subaction));
346 }
347
348 /**
349 * Checks if applied ajax command is an invalid command or not
350 *
351 * @return bool Returns true if ajax command is an invalid command, false otherwise
352 */
353 private function is_invalid_command() {
354 return !is_callable(array($this->commands, $this->subaction));
355 }
356
357 /**
358 * Log an error message for invalid ajax command
359 */
360 private function add_invalid_command_error_log_entry() {
361 error_log("WP-Optimize: ajax_handler: no such command (" . $this->subaction . ")");
362 }
363
364 /**
365 * Set `results` property with error response array for invalid ajax command
366 *
367 * @return void
368 */
369 private function set_invalid_command_error_response() {
370 $this->results = array(
371 'result' => false,
372 'error_code' => 'command_not_found',
373 'error_message' => sprintf(__('The command "%s" was not found', 'wp-optimize'), $this->subaction)
374 );
375 }
376
377 /**
378 * Execute the ajax command
379 */
380 private function execute_command() {
381 $this->results = call_user_func(array($this->commands, $this->subaction), $this->data);
382 }
383
384 /**
385 * If status box content is present, fix it.
386 */
387 private function maybe_fix_status_box_content() {
388 // clean status box content, it broke json sometimes.
389 // Git commit wp-optimize/-/commit/c05686b39959b863f4e168af3fa54421c4870470
390 if (isset($this->results['status_box_contents'])) {
391 $this->results['status_box_contents'] = str_replace(array("\n", "\t"), '', $this->results['status_box_contents']);
392 }
393 }
394
395 /**
396 * Set `results` property with error message
397 */
398 private function set_error_response_on_wp_error() {
399 if (is_wp_error($this->results)) {
400 $this->results = array(
401 'result' => false,
402 'error_code' => $this->results->get_error_code(),
403 'error_message' => $this->results->get_error_message(),
404 'error_data' => $this->results->get_error_data(),
405 );
406 }
407 }
408
409 /**
410 * Set `results` property to null, if it is not yet set
411 */
412 private function maybe_set_results_as_null() {
413 // if nothing was returned for some reason, set as result null.
414 if (empty($this->results)) {
415 $this->results = array(
416 'result' => null
417 );
418 }
419 }
420
421 /**
422 * Sets `results` property with json encode error
423 *
424 * @param int $json_last_error
425 *
426 * @return void
427 */
428 private function set_error_response_on_json_encode_error($json_last_error) {
429 $this->results = array(
430 'result' => false,
431 'error_code' => $json_last_error,
432 'error_message' => 'json_encode error : ' . $json_last_error,
433 'error_data' => '',
434 );
435
436 $this->results = json_encode($this->results);
437 }
438
439 /**
440 * Json encode the `results` property value
441 */
442 private function json_encode_results() {
443 $this->results = json_encode($this->results);
444 }
445 }
446
447 endif;
448