· 6 years ago · Mar 14, 2019, 07:02 PM
1<?php
2class Swift_Performance_Cache {
3
4 // Current content
5 public $buffer;
6
7 // Current cache path (relative to cache folder)
8 public $path;
9
10 // Is dynamic or static page
11 public $is_dynamic_cache = false;
12
13 // No cache mode
14 public $no_cache = false;
15
16 // Run time disable caching
17 public $disabled_cache = false;
18
19 public function __construct(){
20 do_action('swift_performance_cache_before_init');
21
22 $device = (Swift_Performance_Lite::check_option('mobile-support', 1) && Swift_Performance_Lite::is_mobile() ? 'mobile' : 'desktop');
23
24 // Force cache to check js errors
25 if (isset($_GET['force-cache'])){
26 $_COOKIE = array();
27 Swift_Performance_Lite::set_option('optimize-prebuild-only',0);
28 Swift_Performance_Lite::set_option('merge-background-only',0);
29
30 // Check timeout and disable time consuming features if timeout is not extendable
31 $timeout = get_option('swift_performance_timeout');
32 if (empty($timeout)){
33 Swift_Performance_Lite::update_option('critical-css', 0);
34 Swift_Performance_Lite::update_option('merge-styles-exclude-3rd-party', 1);
35 Swift_Performance_Lite::update_option('merge-scripts-exlude-3rd-party', 1);
36 }
37 else {
38 Swift_Performance_Lite::set_option('critical-css',0);
39 }
40
41 add_filter('swift_performance_is_cacheable', '__return_true');
42 add_action('wp_head', function(){
43 echo '<script data-dont-merge>window.addEventListener("error", function(){if(parent){parent.postMessage("report-js-error", "*");}});</script>';
44 },6);
45 }
46
47 // Logged in path
48 if (!isset($_GET['force-cached']) && isset($_COOKIE[LOGGED_IN_COOKIE]) && Swift_Performance_Lite::check_option('shared-logged-in-cache', '1', '!=')){
49 @list($user_login,,,) = explode('|', $_COOKIE[LOGGED_IN_COOKIE]);
50 $hash = md5($user_login . NONCE_SALT);
51 $this->path = trailingslashit(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) . '/' . $device . '/authenticated/' . $hash . '/');
52 }
53 // Not logged in path
54 else {
55 $this->path = trailingslashit(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) . '/' . $device . '/unauthenticated/');
56 }
57
58 // Disable caching if shared logged in cache is set
59 add_action('init', function(){
60 if (is_user_logged_in() && Swift_Performance_Lite::check_option('shared-logged-in-cache', '1') && !is_admin()){
61 define('SWIFT_PERFORMANCE_DISABLE_CACHE', true);
62 }
63 }, PHP_INT_MAX);
64
65 // Clear cache on post save
66 add_action('save_post', array('Swift_Performance_Cache', 'clear_post_cache'));
67 add_action('delete_post', array('Swift_Performance_Cache', 'clear_post_cache'));
68 add_action('wp_trash_post', array('Swift_Performance_Cache', 'clear_post_cache'));
69 add_action('pre_post_update', array('Swift_Performance_Cache', 'clear_post_cache'));
70 add_action('delete_attachment', array('Swift_Performance_Cache', 'clear_post_cache'));
71 add_action('woocommerce_product_object_updated_props', array('Swift_Performance_Cache', 'clear_post_cache'));
72 add_action('woocommerce_product_set_stock', array('Swift_Performance_Cache', 'clear_post_cache'));
73 add_action('woocommerce_variation_set_stock', array('Swift_Performance_Cache', 'clear_post_cache'));
74 add_action('fl_builder_after_save_layout', array('Swift_Performance_Cache', 'clear_post_cache'));
75
76 // Clear pagecache after publish/update post
77 add_action('save_post', array('Swift_Performance_Cache', 'clear_cache_after_post'));
78 add_action('delete_post', array('Swift_Performance_Cache', 'clear_cache_after_post'));
79 add_action('wp_trash_post', array('Swift_Performance_Cache', 'clear_cache_after_post'));
80 add_action('delete_attachment', array('Swift_Performance_Cache', 'clear_cache_after_post'));
81 add_action('woocommerce_product_object_updated_props', array('Swift_Performance_Cache', 'clear_cache_after_post'));
82 add_action('woocommerce_product_set_stock', array('Swift_Performance_Cache', 'clear_cache_after_post'));
83 add_action('woocommerce_variation_set_stock', array('Swift_Performance_Cache', 'clear_cache_after_post'));
84 add_action('fl_builder_after_save_layout', array('Swift_Performance_Cache', 'clear_cache_after_post'));
85
86 // Elementor AJAX update
87 add_action('elementor/ajax/register_actions', function(){
88 Swift_Performance_Cache::clear_post_cache($_REQUEST['editor_post_id']);
89 });
90
91 // Scheduled posts
92 add_action('transition_post_status', function($new, $old = '', $post = NULL){
93 if (!empty($post)){
94 Swift_Performance_Cache::clear_post_cache($post->ID);
95 Swift_Performance_Cache::clear_cache_after_post();
96 }
97 });
98
99 // Scheduled sales
100 add_action('wc_after_products_starting_sales', array('Swift_Performance_Cache', 'clear_post_cache_array'));
101 add_action('wc_after_products_ending_sales', array('Swift_Performance_Cache', 'clear_post_cache_array'));
102
103 // Clear all cache actions
104 foreach (array('after_switch_theme','customize_save_after','update_option_permalink_structure','update_option_tag_base','update_option_category_base','wp_update_nav_menu', 'update_option_sidebars_widgets') as $action){
105 add_action($action, array('Swift_Performance_Cache', 'clear_all_cache'));
106 }
107
108 // Clear cache on plugin/update actions
109 add_action('activated_plugin', function($plugin){
110 if ($plugin !== basename(SWIFT_PERFORMANCE_DIR) . '/performance.php'){
111 $action = esc_html__('A plugin has been activated. Page cache should be cleared.', 'swift-performance');
112 ob_start();
113 include SWIFT_PERFORMANCE_DIR . 'templates/clear-cache-notice.php';
114 Swift_Performance_Lite::add_notice(ob_get_clean(), 'warning', 'plugin/update-action');
115 }
116 });
117 add_action('deactivated_plugin', function($plugin){
118 if ($plugin !== basename(SWIFT_PERFORMANCE_DIR) . '/performance.php'){
119 $action = esc_html__('A plugin has been activated. Page cache should be cleared.', 'swift-performance');
120 ob_start();
121 include SWIFT_PERFORMANCE_DIR . 'templates/clear-cache-notice.php';
122 Swift_Performance_Lite::add_notice(ob_get_clean(), 'warning', 'plugin/update-action');
123 }
124 });
125 add_action('upgrader_process_complete', function(){
126 $action = esc_html__('Updater process has been finished. Page cache probably should be cleared.', 'swift-performance');
127 ob_start();
128 include SWIFT_PERFORMANCE_DIR . 'templates/clear-cache-notice.php';
129 Swift_Performance_Lite::add_notice(ob_get_clean(), 'warning', 'plugin/update-action');
130 });
131
132 // Clear cache after comment approved
133 add_action('wp_set_comment_status', function($comment_id, $status){
134 if (in_array($status, array('approve', 'hold'))){
135 $comment = get_comment( $comment_id );
136 Swift_Performance_Cache::clear_post_cache($comment->comment_post_ID);
137 }
138 }, 10, 2);
139
140 // Clear intelligent cache based on request (Legacy)
141 if (Swift_Performance_Lite::check_option('cache-expiry-mode', 'intelligent') && Swift_Performance_Lite::check_option('resource-saving-mode', 1)){
142 add_action('shutdown', array($this, 'clear_intelligent_cache'));
143 }
144
145 // Clear user cache on delete user
146 add_action( 'delete_user', array('Swift_Performance_Cache', 'clear_user_cache'));
147
148 // Bypass Avatar
149 if (Swift_Performance_Lite::check_option('gravatar-cache', 1)){
150 add_filter('get_avatar_url', array($this, 'bypass_gravatar'));
151 }
152
153 // Cache WooCommerce empty minicart
154 if (Swift_Performance_Lite::check_option('cache-empty-minicart', 1) && isset($_GET['wc-ajax']) && $_GET['wc-ajax'] == 'get_refreshed_fragments' && (!isset($_COOKIE['woocommerce_cart_hash']) || empty($_COOKIE['woocommerce_cart_hash'])) && (!isset($_COOKIE['woocommerce_items_in_cart']) || empty($_COOKIE['woocommerce_items_in_cart']))){
155 Swift_Performance_Lite::set_option('optimize-prebuild-only', 0);
156 Swift_Performance_Lite::set_option('merge-background-only', 0);
157 add_filter('swift_performance_is_cacheable_dynamic', '__return_true');
158
159 $timestamp = gmdate("D, d M Y H:i:s") . " GMT";
160 header("Expires: $timestamp");
161 header("Last-Modified: $timestamp");
162 header("Pragma: no-cache");
163 header("Cache-Control: no-cache, must-revalidate");
164 }
165
166 // Clear post cache on user action
167 add_action('init', array('Swift_Performance_Cache', 'clear_on_user_action'));
168
169 // Force no cache mode
170 if (isset($_GET['swift-no-cache'])){
171 $this->no_cache = true;
172 unset($_GET['swift-no-cache']);
173 unset($_REQUEST['swift-no-cache']);
174 $_SERVER['REQUEST_URI'] = preg_replace('~(&|\?)swift-no-cache=(\d+)~','',$_SERVER['REQUEST_URI']);
175 }
176
177
178 // Load cache
179 if (!$this->no_cache){
180 $this->load_cache();
181 }
182
183 // Set cache
184 if (self::is_cacheable() || self::is_cacheable_dynamic()){
185 $this->is_dynamic_cache = (self::is_cacheable_dynamic() ? true : false);
186
187 ob_start(array($this, 'build_cache'));
188 add_action('shutdown', array($this, 'set_cache'), apply_filters('swift_performance_set_cache_hook_priority',PHP_INT_MAX));
189 }
190 else if (self::is_cacheable_ajax()){
191 ob_start(array($this, 'build_cache'));
192 add_action('shutdown', array($this, 'set_ajax_cache'), apply_filters('swift_performance_set_cache_hook_priority',PHP_INT_MAX));
193 }
194
195 // Print intelligent cache js in head
196 if (Swift_Performance_Lite::check_option('cache-expiry-mode', 'intelligent') && (self::is_cacheable() || self::is_cacheable_dynamic())){
197 add_action('wp_head', array('Swift_Performance_Cache', 'intelligent_cache_xhr'), PHP_INT_MAX);
198 }
199
200 do_action('swift_performance_cache_init');
201 }
202
203 /**
204 * Catch the content
205 */
206 public function build_cache($buffer){
207 $this->buffer = $buffer;
208
209 if (Swift_Performance_Lite::check_option('cache-expiry-mode', 'intelligent') && $this->no_cache){
210 $this->check_integrity(self::avoid_mixed_content($this->buffer));
211 }
212
213 return $buffer;
214 }
215
216
217 /**
218 * Save current page to cache
219 */
220 public function set_cache(){
221 global $wpdb;
222
223 // Return on CLI
224 if (defined('WP_CLI') && WP_CLI){
225 return;
226 }
227
228 // Don't write cache if there isn't free thread for optimizing assets
229 if ($this->disabled_cache || (defined('SWIFT_PERFORMANCE_DISABLE_CACHE') && SWIFT_PERFORMANCE_DISABLE_CACHE)){
230 return;
231 }
232
233 // We have wp_query, so we re-check is it cacheable
234 if (!self::is_cacheable() && !self::is_cacheable_dynamic()){
235 return;
236 }
237
238 // Don't write cache for common request if background assets merging enabled
239 if (Swift_Performance_Lite::check_option('merge-background-only', 1) && !isset($_SERVER['HTTP_X_MERGE_ASSETS'])){
240 return;
241 }
242
243 // Remove background loading from cache
244 if (Swift_Performance_Lite::check_option('merge-background-only', 1) && Swift_Performance_Lite::check_option('cache-expiry-mode', array('timebased', 'actionbased'), 'IN') ){
245 $this->buffer = str_replace("<script data-dont-merge>var xhr = new XMLHttpRequest();xhr.open('GET', document.location.href);xhr.setRequestHeader('X-merge-assets', 'true');xhr.send(null);</script>", '', $this->buffer);
246 }
247
248 $this->buffer = self::avoid_mixed_content($this->buffer);
249
250 // Fix output buffer conflict
251 if (strpos($this->buffer, '<!--SWIFT_PERFORMACE_OB_CONFLICT-->') !== false){
252 $this->buffer = $GLOBALS['swift_performance']->modules['asset-manager']->asset_manager_callback($this->buffer);
253 }
254
255 // Appcache
256 $device = Swift_Performance_Lite::is_mobile() ? '-mobile' : '-desktop';
257 if (Swift_Performance_Lite::check_option('appcache'.$device, 1)){
258 $this->buffer = preg_replace('~<html~', '<html manifest="'.Swift_Performance_Lite::home_url().SWIFT_PERFORMANCE_SLUG.'.appcache"', $this->buffer);
259 }
260
261
262 // Set charset
263 if (!Swift_Performance_Lite::is_amp($this->buffer) && apply_filters('swift_performance_character_encoding_meta', true)){
264 // Remove charset meta if exists
265 $this->buffer = preg_replace('~<meta charset([^>]+)>~', '', $this->buffer);
266
267 // Append charset to the top
268 $this->buffer = preg_replace('~<head([^>]*)?>~',"<head$1>\n<meta charset=\"".get_bloginfo('charset')."\">", $this->buffer, 1);
269 }
270
271 $this->buffer = str_replace('</body>',"<!--Cached with ".SWIFT_PERFORMANCE_PLUGIN_NAME."-->\n</body>", $this->buffer);
272
273 $this->buffer = apply_filters('swift_performance_buffer', $this->buffer);
274
275 $content_type = '';
276 foreach (headers_list() as $header) {
277 if (preg_match('~Content-type:\s?([a-z]*)/([a-z]*)~i', $header, $matches)){
278 $content_type = $matches[1].'/'.$matches[2];
279 }
280 }
281
282 // Disk cache
283 if (Swift_Performance_Lite::check_option('caching-mode', array('disk_cache_rewrite', 'disk_cache_php'), 'IN')){
284 // Set cached file basename
285 if (Swift_Performance_Lite::is_404()){
286 $basename = '404.html';
287 $type = '404';
288 }
289 else if ($content_type == 'text/xml' || Swift_Performance_Lite::is_feed()){
290 $basename = 'index.xml';
291 $type = 'xml';
292 }
293 else if ($content_type == 'application/json' || (defined('REST_REQUEST') && REST_REQUEST)){
294 $basename = 'index.json';
295 $type = 'json';
296 }
297 else {
298 $basename = 'index.html';
299 $type = 'html';
300 }
301
302 // Dynamic cache
303 if ($this->is_dynamic_cache){
304 set_transient('swift_performance_dynamic_' . Swift_Performance_Lite::get_unique_id(), array('time' => time(), 'content' => $this->buffer), 3600);
305 Swift_Performance_Lite::log('Dynamic cache (db) ' . $_SERVER['REQUEST_URI'] . serialize($_REQUEST), 9);
306 }
307 // General cache
308 else {
309 self::write_file($this->path . $basename, $this->buffer, true);
310 if (Swift_Performance_Lite::check_option('enable-gzip', 1) && function_exists('gzencode')){
311 self::write_file($this->path . $basename . '.gz', gzencode($this->buffer), true);
312 }
313
314 // Update warmup table
315 $id = Swift_Performance_Lite::get_warmup_id($_SERVER['HTTP_HOST'] . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
316 $priority = Swift_Performance_Lite::get_default_warmup_priority();
317 $url = (is_ssl() ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
318 $timestamp = time();
319
320 Swift_Performance_Lite::mysql_query("INSERT IGNORE INTO " . SWIFT_PERFORMANCE_TABLE_PREFIX . "warmup (id, url, timestamp, type, priority) VALUES ('".Swift_Performance_Lite::get_warmup_id($url)."', '".esc_url($url)."', '".$timestamp."', '".$type."', ".(int)$priority.") ON DUPLICATE KEY UPDATE timestamp = '{$timestamp}', type = '{$type}'");
321
322
323 Swift_Performance_Lite::log('General cache (file) ' . $this->path . $basename, 9);
324 }
325 }
326 // Memcached
327 else if(Swift_Performance_Lite::check_option('caching-mode', 'memcached_php')){
328 // Set cached file basename
329 if (Swift_Performance_Lite::is_404()){
330 $basename = '404.html';
331 $type = '404';
332 }
333 else if ($content_type == 'text/xml' || Swift_Performance_Lite::is_feed()){
334 $basename = 'index.xml';
335 $type = 'xml';
336 }
337 else if ($content_type == 'application/json' || (defined('REST_REQUEST') && REST_REQUEST)){
338 $basename = 'index.json';
339 $type = 'json';
340 }
341 else {
342 $basename = 'index.html';
343 $type = 'html';
344 }
345
346 // Dynamic cache
347 if ($this->is_dynamic_cache){
348 Swift_Performance_Cache::memcached_set(Swift_Performance_Lite::get_unique_id(), array('time' => time(), 'content' => $this->buffer));
349 Swift_Performance_Lite::log('Dynamic cache (memcached) ' . $_SERVER['REQUEST_URI'] . serialize($_REQUEST), 9);
350 }
351 // General cache
352 else {
353 Swift_Performance_Cache::memcached_set($this->path . $basename, array('time' => time(), 'content' => $this->buffer));
354 if (Swift_Performance_Lite::check_option('enable-gzip', 1) && function_exists('gzencode')){
355 Swift_Performance_Cache::memcached_set($this->path . $basename . '.gz', array('time' => time(), 'content' => gzencode($this->buffer)));
356 }
357
358 // Update warmup table
359 $id = Swift_Performance_Lite::get_warmup_id($_SERVER['HTTP_HOST'] . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
360 $priority = Swift_Performance_Lite::get_default_warmup_priority();
361 $url = (is_ssl() ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
362 $timestamp = time();
363
364 Swift_Performance_Lite::mysql_query("INSERT IGNORE INTO " . SWIFT_PERFORMANCE_TABLE_PREFIX . "warmup (id, url, timestamp, type, priority) VALUES ('".Swift_Performance_Lite::get_warmup_id($url)."', '".esc_url($url)."', '".$timestamp."', '".$type."', ".(int)$priority.") ON DUPLICATE KEY UPDATE timestamp = '{$timestamp}', type = '{$type}'");
365
366
367
368 Swift_Performance_Lite::log('General cache (memcached) ' . $this->path . $basename, 9);
369 }
370 }
371 }
372
373 /**
374 * Set AJAX object transient
375 */
376 public function set_ajax_cache(){
377 // Set AJAX object expiry
378 $expiry = Swift_Performance_Lite::get_option('ajax-cache-expiry-time');
379
380 // Store AJAX object in db if we are using disk cache
381 if (Swift_Performance_Lite::check_option('caching-mode', array('disk_cache_rewrite', 'disk_cache_php'), 'IN')){
382 set_transient('swift_performance_ajax_' . Swift_Performance_Lite::get_unique_id(), array('time' => time(), 'content' => $this->buffer), $expiry);
383 Swift_Performance_Lite::log('Ajax cache (db) ' . $_SERVER['REQUEST_URI'] . serialize($_REQUEST), 9);
384 }
385 // Memcached
386 else {
387 Swift_Performance_Cache::memcached_set(Swift_Performance_Lite::get_unique_id(), array('time' => time(), 'content' => $this->buffer));
388 Swift_Performance_Lite::log('Ajax cache (memcached) ' . $_SERVER['REQUEST_URI'] . serialize($_REQUEST), 9);
389 }
390
391 return $this->buffer;
392 }
393
394
395
396 /**
397 * Load cached file and stop if file exists
398 */
399 public function load_cache(){
400 // Return on CLI
401 if (defined('WP_CLI') && WP_CLI){
402 return;
403 }
404
405 // Set default content type
406 $content_type = 'text/html';
407
408 // Add Swift Performance header
409 if (!Swift_Performance_Lite::is_admin()){
410 header(SWIFT_PERFORMANCE_SLUG . ': MISS');
411 }
412
413 // Serve cached AJAX requests
414 if (self::is_cacheable_dynamic() || self::is_cacheable_ajax()){
415 if (Swift_Performance_Lite::check_option('caching-mode', 'memcached_php')){
416 $cached_request = Swift_Performance_Cache::memcached_get(Swift_Performance_Lite::get_unique_id());
417 }
418 else {
419 if (self::is_cacheable_dynamic()){
420 $cached_request = get_transient('swift_performance_dynamic_' . Swift_Performance_Lite::get_unique_id());
421 }
422 else {
423 $cached_request = get_transient('swift_performance_ajax_' . Swift_Performance_Lite::get_unique_id());
424 }
425 }
426
427 if ($cached_request !== false){
428 header(SWIFT_PERFORMANCE_SLUG . ': HIT');
429
430 if (isset($cached_request['basename'])){
431 switch ($cached_request['basename']) {
432 case 'index.xml':
433 $content_type = 'text/xml';
434 break;
435 case 'index.json':
436 $content_type = 'text/json';
437 break;
438 }
439 }
440
441 $content = $cached_request['content'];
442 $last_modified_time = $cached_request['time'];
443 $etag = md5($content);
444
445 // Send headers
446 if (Swift_Performance_Lite::check_option('304-header', 1) && (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified_time || @trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag)){
447 header("HTTP/1.1 304 Not Modified");
448 }
449
450 header("Last-Modified: ".gmdate("D, d M Y H:i:s", $last_modified_time)." GMT");
451 header("Etag: $etag");
452 header("Content-type: $content_type");
453 echo $content;
454
455 die;
456 }
457 }
458
459 // Skip if requested page isn't cacheable
460 if (!self::is_cacheable()){
461 return;
462 }
463
464 // Disk cache
465 $is_disk_cache = (strpos(Swift_Performance_Lite::get_option('caching-mode'), 'disk_cache') !== false);
466 $is_cached = false;
467 $is_404 = false;
468 if (file_exists(SWIFT_PERFORMANCE_CACHE_DIR . $this->path . 'index.html')){
469 $path = SWIFT_PERFORMANCE_CACHE_DIR . $this->path . 'index.html';
470 $is_cached = true;
471 }
472 else if (file_exists(SWIFT_PERFORMANCE_CACHE_DIR . $this->path . 'index.xml')){
473 $path = SWIFT_PERFORMANCE_CACHE_DIR . $this->path . 'index.xml';
474 $is_cached = true;
475 $content_type = 'text/xml';
476 }
477 else if (file_exists(SWIFT_PERFORMANCE_CACHE_DIR . $this->path . 'index.json')){
478 $path = SWIFT_PERFORMANCE_CACHE_DIR . $this->path . 'index.json';
479 $is_cached = true;
480 $content_type = 'application/json';
481 }
482 else if (file_exists(SWIFT_PERFORMANCE_CACHE_DIR . $this->path . '404.html')){
483 $path = SWIFT_PERFORMANCE_CACHE_DIR . $this->path . '404.html';
484 $is_cached = true;
485 $is_404 = true;
486 }
487 if ($is_disk_cache && $is_cached && filesize($path) > 0){
488 header(SWIFT_PERFORMANCE_SLUG . ': HIT');
489
490 $last_modified_time = filemtime($path);
491 $etag = md5_file($path);
492
493 if (!$is_404 && Swift_Performance_Lite::check_option('304-header', 1) && (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified_time || @trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag)){
494 status_header(304);
495 }
496 else if ($is_404){
497 status_header(404);
498 }
499
500 header("Last-Modified: ".gmdate("D, d M Y H:i:s", $last_modified_time)." GMT");
501 header("Etag: $etag");
502 header("Content-type: $content_type");
503
504 if ( file_exists($path . '.gz') && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip' ) !== false ) {
505 header('Content-Encoding: gzip');
506 readfile($path . '.gz');
507 }
508 else{
509 readfile($path);
510 }
511 exit;
512 }
513 // Memcache
514 else if (Swift_Performance_Lite::check_option('caching-mode', 'memcached_php')){
515 if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip' ) !== false ) {
516 $cached_request = Swift_Performance_Cache::memcached_get($this->path . 'index.html.gz');
517 if (empty($cached_request)) {
518 $cached_request = Swift_Performance_Cache::memcached_get($this->path . 'index.xml.gz');
519 $content_type = 'text/xml';
520 }
521 if (empty($cached_request)) {
522 $cached_request = Swift_Performance_Cache::memcached_get($this->path . 'index.json.gz');
523 $content_type = 'application/json';
524 }
525 if (empty($cached_request)) {
526 $cached_request = Swift_Performance_Cache::memcached_get($this->path . '404.html.gz');
527 }
528 }
529 else {
530 $cached_request = Swift_Performance_Cache::memcached_get($this->path . 'index.html');
531 if (empty($cached_request)) {
532 $cached_request = Swift_Performance_Cache::memcached_get($this->path . 'index.xml');
533 $content_type = 'text/xml';
534 }
535 if (empty($cached_request)) {
536 $cached_request = Swift_Performance_Cache::memcached_get($this->path . 'index.json');
537 $content_type = 'application/json';
538 }
539 if (empty($cached_request)) {
540 $cached_request = Swift_Performance_Cache::memcached_get($this->path . '404.html');
541 }
542 }
543
544 if ($cached_request !== false){
545 header(SWIFT_PERFORMANCE_SLUG . ': HIT');
546
547 $content = $cached_request['content'];
548 if (!empty($content)) {
549 $last_modified_time = $cached_request['time'];
550 $etag = md5($content);
551
552 // Send headers
553 if (Swift_Performance_Lite::check_option('304-header', 1) && (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified_time || @trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag)){
554 header("HTTP/1.1 304 Not Modified");
555 }
556
557 header("Last-Modified: ".gmdate("D, d M Y H:i:s", $last_modified_time)." GMT");
558 header("Etag: $etag");
559 header("Content-type: $content_type");
560 header("Content-Encoding: none");
561 header('Connection: close');
562
563 if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip' ) !== false ) {
564 header('Content-Encoding: gzip');
565 }
566
567 echo $content;
568
569 exit;
570 }
571 }
572 }
573 }
574
575 /**
576 * Check is dynamic content and cache identical, return dynamic content if not.
577 * @return string
578 */
579 public function check_integrity($buffer){
580 // Cache is identical
581 if (self::is_identical()){
582 header('X-Cache-Status: identical');
583 die;
584 }
585
586 $buffer = str_replace('</body>', "<!--Cached with ".SWIFT_PERFORMANCE_PLUGIN_NAME."-->\n</body>", $buffer);
587
588 $checksum = false;
589 // Disk cache
590 if(strpos(Swift_Performance_Lite::get_option('caching-mode'), 'disk_cache') !== false){
591 if (self::is_cacheable()){
592 if (!file_exists(SWIFT_PERFORMANCE_CACHE_DIR . $this->path . 'index.html')){
593 header('X-Cache-Status: miss');
594 return;
595 }
596
597 $clean_cached = self::clean_buffer(file_get_contents(SWIFT_PERFORMANCE_CACHE_DIR . $this->path . 'index.html'));
598 $clean_buffer = self::clean_buffer($buffer);
599
600 $checksum = (md5($clean_buffer) != md5($clean_cached));
601 }
602 else if (self::is_cacheable_dynamic()){
603 $cached = get_transient('swift_performance_dynamic_' . Swift_Performance_Lite::get_unique_id());
604 $clean_cached = preg_replace_callback('~([0-9abcdef]{10})~',array('Swift_Performance_Cache', 'clean_nonce_buffer'), $cached['content']);
605 $clean_buffer = preg_replace_callback('~([0-9abcdef]{10})~',array('Swift_Performance_Cache', 'clean_nonce_buffer'), $buffer);
606 $checksum = (md5($clean_buffer) != md5($clean_cached));
607 }
608
609 if ($checksum){
610 header("Last-Modified: ".gmdate("D, d M Y H:i:s", time())." GMT");
611 header("Etag: " . md5($buffer));
612 header('X-Cache-Status: changed');
613 return;
614 }
615 }
616 // Memcached
617 else {
618 if (self::is_cacheable()){
619 $cached_request = Swift_Performance_Cache::memcached_get($this->path . 'index.html');
620 if (empty($cached_request)){
621 header('X-Cache-Status: miss');
622 return;
623 }
624
625 $clean_cached = self::clean_buffer($cached_request['content']);
626 $clean_buffer = self::clean_buffer($buffer);
627
628 $checksum = (md5($clean_buffer) != md5($clean_cached));
629 }
630 else if (self::is_cacheable_dynamic()){
631 $cached = Swift_Performance_Cache::memcached_get(Swift_Performance_Lite::get_unique_id());
632 $clean_cached = preg_replace_callback('~([0-9abcdef]{10})~',array('Swift_Performance_Cache', 'clean_nonce_buffer'), $cached['content']);
633 $clean_buffer = preg_replace_callback('~([0-9abcdef]{10})~',array('Swift_Performance_Cache', 'clean_nonce_buffer'), $buffer);
634 $checksum = (md5($clean_buffer) != md5($clean_cached));
635 }
636
637 if ($checksum){
638 header("Last-Modified: ".gmdate("D, d M Y H:i:s", time())." GMT");
639 header("Etag: " . md5($buffer));
640 header('X-Cache-Status: changed');
641 return;
642 }
643 }
644
645 header('X-Cache-Status: not-modified');
646
647 }
648
649 /**
650 * Clear intelligent cache if request is POST or query string isn't empty
651 */
652 public function clear_intelligent_cache(){
653 // Exceptions
654
655 // Swift check cache
656 if ($this->no_cache){
657 return;
658 }
659
660 // Don't clear on AJAX request
661 if (defined('DOING_AJAX')){
662 return;
663 }
664
665 // Don't clear cache on login/register
666 if (@in_array( $GLOBALS['pagenow'], array( 'wp-login.php', 'wp-register.php' ))){
667 return;
668 }
669
670 // Remove empty elements
671 $_GET = array_filter($_GET);
672 $_POST = array_filter($_POST);
673
674 // Clear transients if necessary
675 if (!empty($_POST) || !empty($_GET)){
676 self::clear_transients('identical');
677 }
678 Swift_Performance_Lite::log('Clear Intelligent Cache', 9);
679 }
680
681 /**
682 * Bypass Gravatar
683 * @param string url
684 * @return string
685 */
686 public function bypass_gravatar($url){
687 // Bypass gravatar only
688 if (!preg_match('~gravatar.com$~', parse_url($url, PHP_URL_HOST))){
689 return $url;
690 }
691
692 // Serve gravatar if it is exists and not expired
693 $filename = apply_filters('swift_gravatar_cache_filename', md5($url));
694 $gravatar = SWIFT_PERFORMANCE_CACHE_DIR . 'garvatar-cache/' . $filename;
695
696 if (file_exists($gravatar) && filemtime($gravatar) + (int)Swift_Performance_Lite::get_option('gravatar-cache-expiry') > time()){
697 return SWIFT_PERFORMANCE_CACHE_URL . 'garvatar-cache/' . $filename;
698 }
699
700 // We are here, so gravatar is not exists, or expired. Check is the gravatar-cache folder exists at all
701 if (!file_exists(SWIFT_PERFORMANCE_CACHE_DIR . 'garvatar-cache')){
702 // No it isn't exists, so we try to create it
703 if (!is_writable(SWIFT_PERFORMANCE_CACHE_DIR . 'garvatar-cache')){
704 @mkdir(SWIFT_PERFORMANCE_CACHE_DIR . 'garvatar-cache', 0777, true);
705 }
706 else {
707 // No luck, no cache
708 return $url;
709 }
710 }
711
712 // Gravatar isn't exists, or expired, and gravatar-cache folder is exists
713 if (!file_exists($gravatar) || is_writable($gravatar)){
714 // Download the image
715 $response = wp_safe_remote_get( $url, array( 'timeout' => 300, 'stream' => true, 'filename' => $gravatar ) );
716
717 // Something went wrong :-/
718 if (is_wp_error($response)) {
719 @unlink($gravatar);
720 Swift_Performance_Lite::log('Gravatar bypass failed: ' . $response->get_error_message(), 1);
721 return $url;
722 }
723 else {
724 list($width, $height) = getimagesize($gravatar);
725 if (empty($width)){
726 @file_put_contents($gravatar, base64_decode('/9j/4AAQSkZJRgABAQAAAQABAAD//gA7Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcgSlBFRyB2NjIpLCBxdWFsaXR5ID0gOTAK/9sAQwADAgIDAgIDAwMDBAMDBAUIBQUEBAUKBwcGCAwKDAwLCgsLDQ4SEA0OEQ4LCxAWEBETFBUVFQwPFxgWFBgSFBUU/9sAQwEDBAQFBAUJBQUJFA0LDRQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU/8AAEQgAIAAgAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A+uK0tH8O6lr7sthaPcbfvMMBR9ScCs2voDwXaQWfhbTFgACvAsjEd2YZY/maAPEdY8OaloDKL+0eAN91shlP0IyKza+gfGVpBeeF9TS4AKLA8gJ7MoyD+Yr5+oAfFE88qRxoXkchVVRkknoBXufgPQ9R0HRlt7+4WQH5kgAz5Oeo3d/881wnwj0lL3XJ7yRQwtIxsB7M2QD+QavYKAOa8d6JqOvaM1vYXCx/xPCRgy45A3dv6+teGSxPBK8ciFJEJVlYYII6g19MV4/8XNJSy1yC8jUKLuM7wO7LgE/kVoA//9k='));
727 }
728 // Yay!
729 return SWIFT_PERFORMANCE_CACHE_URL . 'garvatar-cache/' . apply_filters('swift_gravatar_cache_filename', md5($url));
730 }
731 }
732
733 // No cache
734 return $url;
735 }
736
737 /**
738 Â * Clean buffer to be able to compare integrity for logged in users
739 * @param string buffer
740 * @return string
741 */
742 public static function clean_buffer($buffer){
743 $buffer = preg_replace('~swift-no-cache(=|%3D)(\d*)~', '', $buffer);
744 $buffer = preg_replace_callback('~([0-9abcdef]{10})~',array('Swift_Performance_Cache', 'clean_nonce_buffer'), $buffer);
745 return $buffer;
746 }
747
748 /**
749 Â * Remove valid nonces from the cache/buffer to be able to compare integrity for logged in users
750 * @param array $matches
751 * @return string
752 */
753 public static function clean_nonce_buffer($matches){
754 if (wp_verify_nonce($matches[0])){
755 return '';
756 }
757 return $matches[0];
758 }
759
760 /**
761 * Print cache check XHR request in head
762 */
763 public static function intelligent_cache_xhr(){
764 echo "\n<script data-dont-merge='1'>setTimeout(function(){ function fire(){ window.removeEventListener('touchstart',fire); window.removeEventListener('scroll',fire); document.removeEventListener('mousemove',fire); var request = new XMLHttpRequest(); request.open('GET', document.location.href + (document.location.href.match(/\?/) ? '&' : '?') + 'swift-no-cache=' + parseInt(Math.random() * 100000000), true); request.onreadystatechange = function() { if (request.readyState === 4 && request.getResponseHeader('X-Cache-Status') == 'changed' && '1' != '".Swift_Performance_Lite::get_option(' disable - instant - reload ')."') { document.open(); document.write(request.responseText.replace('<html', '<html data-swift-performance-refreshed')); document.close(); } }; if (document.querySelector('[data-swift-performance-refreshed]') == null) { request.send(); }; document.getElementsByTagName('html')[0].addEventListener('click', function() { request.abort() }, false); document.getElementsByTagName('html')[0].addEventListener('keypress', function() { request.abort() }, false); } window.addEventListener('load', function() { window.addEventListener('touchstart',fire); window.addEventListener('scroll',fire); document.addEventListener('mousemove',fire); }); },10); </script>";
765 }
766
767 /**
768 * Check known user actions when we should clear post cache
769 */
770 public static function clear_on_user_action(){
771 // Comment
772 if (isset($_POST['comment_post_ID']) && !empty($_POST['comment_post_ID'])){
773 self::clear_post_cache($_POST['comment_post_ID']);
774 Swift_Performance_Lite::log('Clear post cache triggered (new comment) ', 9);
775 }
776
777 // bbPress comment
778 else if (isset($_POST['bbp_reply_content']) && isset($_POST['bbp_topic_id']) && !empty($_POST['bbp_topic_id'])){
779 self::clear_post_cache($_POST['bbp_topic_id']);
780 Swift_Performance_Lite::log('Clear post cache triggered (new bbPress comment) ', 9);
781 }
782
783 // Buddypress
784 else if (function_exists('bp_get_root_domain') && isset($_POST['action']) && $_POST['action'] == 'post_update'){
785 $bb_permalink = '';
786 if (isset($_POST['object']) && $_POST['object'] == 'group'){
787 if (isset($_POST['item_id']) && !empty($_POST['item_id'])){
788 $group = groups_get_group( array( 'group_id' => $_POST['item_id'] ) );
789 $bb_permalink = trailingslashit( bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/' . $group->slug . '/' );
790 }
791 }
792 else if ((!isset($_POST['object']) || empty($_POST['object'])) && (!isset($_POST['item_id']) || empty($_POST['item_id']))){
793 $bb_permalink = str_replace(home_url(), '', bp_get_root_domain() .'/'. bp_get_root_slug());
794 }
795
796 if (!empty($bb_permalink)){
797 if (Swift_Performance_Lite::check_option('caching-mode', array('disk_cache_rewrite', 'disk_cache_php'), 'IN')){
798 // Disk cache
799 self::recursive_rmdir($bb_permalink);
800 Swift_Performance_Lite::log('Clear permalink cache (disk) triggered (buddypress activity) ', 9);
801 }
802 else {
803 // Memcached
804 $memcached = self::get_memcache_instance();
805 $keys = $memcached->getAllKeys();
806 foreach($keys as $item) {
807 if(preg_match('~^swift-performance' . str_replace(home_url(), '', $bb_permalink) .'~', $item)) {
808 $memcached->delete($item);
809 }
810 }
811 Swift_Performance_Lite::log('Clear permalink cache (memcached) triggered (buddypress activity) ', 9);
812 }
813
814 // Update warmup table
815 $id = Swift_Performance_Lite::get_warmup_id($bb_permalink);
816 Swift_Performance_Lite::mysql_query("UPDATE " . SWIFT_PERFORMANCE_TABLE_PREFIX . "warmup SET timestamp = 0, type = '' WHERE id = '{$id}' LIMIT 1");
817
818 // Cloudflare
819 if(Swift_Performance_Lite::check_option('cloudflare-auto-purge',1) && Swift_Performance_Lite::check_option('cloudflare-email', '', '!=') && Swift_Performance_Lite::check_option('cloudflare-api-key', '', '!=')){
820 self::purge_cloudflare_zones($bb_permalink);
821 }
822
823 // Varnish
824 if(Swift_Performance_Lite::check_option('varnish-auto-purge',1)){
825 self::purge_varnish_url($bb_permalink);
826 }
827
828 // Prebuild cache
829 if (Swift_Performance_Lite::check_option('automated_prebuild_cache',1)){
830 wp_remote_get($bb_permalink);
831 Swift_Performance_Lite::log('Prebuild cache for buddypress activity: ' . $bb_permalink, 9);
832 }
833 }
834 }
835 }
836
837 /**
838 * Clear all cache
839 */
840 public static function clear_all_cache(){
841 do_action('swift_performance_before_clear_all_cache');
842
843 // Delete prebuild booster
844 delete_option('swift_performance_prebuild_booster');
845
846 // Clear cache
847 if (Swift_Performance_Lite::check_option('caching-mode', array('disk_cache_rewrite', 'disk_cache_php'), 'IN')){
848 // Disk cache
849 self::recursive_rmdir();
850 Swift_Performance_Lite::log('Clear all cache (disk)', 9);
851 }
852 else {
853 // Memcached
854 $memcached = self::get_memcache_instance();
855 $memcached->flush();
856 Swift_Performance_Lite::log('Clear all cache (memcached)', 9);
857 }
858
859 // Cloudflare
860 if(Swift_Performance_Lite::check_option('cloudflare-auto-purge',1) && Swift_Performance_Lite::check_option('cloudflare-email', '', '!=') && Swift_Performance_Lite::check_option('cloudflare-api-key', '', '!=')){
861 self::purge_cloudflare_zones();
862 }
863
864 // Varnish
865 if(Swift_Performance_Lite::check_option('varnish-auto-purge',1)){
866 self::purge_varnish_url(Swift_Performance_Lite::home_url(), true);
867 }
868
869 Swift_Performance_Third_Party::clear_cache();
870
871 // Prebuild cache
872 Swift_Performance_Lite::stop_prebuild();
873 if (Swift_Performance_Lite::check_option('automated_prebuild_cache',1)){
874 wp_schedule_single_event(time(), 'swift_performance_prebuild_cache');
875 Swift_Performance_Lite::log('Prebuild cache scheduled', 9);
876 }
877
878 // Clear object cache
879 self::clear_transients();
880
881 // MaxCDN
882 if (Swift_Performance_Lite::check_option('enable-cdn', 1) && Swift_Performance_Lite::check_option('maxcdn-alias', '','!=') && Swift_Performance_Lite::check_option('maxcdn-key', '','!=') && Swift_Performance_Lite::check_option('maxcdn-secret', '','!=')){
883 Swift_Performance_CDN_Manager::purge_cdn();
884 }
885
886 // Update warmup table
887 Swift_Performance_Lite::mysql_query("UPDATE " . SWIFT_PERFORMANCE_TABLE_PREFIX . "warmup SET timestamp = 0, type = ''");
888
889 do_action('swift_performance_after_clear_all_cache');
890 }
891
892 /**
893 * Clear expired cache
894 */
895 public static function clear_expired(){
896 global $wpdb;
897 do_action('swift_performance_before_clear_expired_cache');
898
899 $timestamp = time() - Swift_Performance_Lite::get_option('cache-expiry-time');
900 $expired = $wpdb->get_col("SELECT url FROM " . SWIFT_PERFORMANCE_TABLE_PREFIX . "warmup WHERE timestamp <= '{$timestamp}'");
901 foreach ($expired as $permalink) {
902 self::clear_permalink_cache($permalink);
903 }
904
905 do_action('swift_performance_after_clear_expired_cache');
906 }
907
908 /**
909 * Clear cache based on permalink
910 * @param string $permalink
911 */
912 public static function clear_permalink_cache($permalink){
913 do_action('swift_performance_before_clear_permalink_cache', $permalink);
914
915 // Try to get the page/post id
916 $maybe_id = url_to_postid($permalink);
917 if (!empty($maybe_id)){
918 self::clear_post_cache($maybe_id);
919 return;
920 }
921
922 // Cloudflare
923 if(Swift_Performance_Lite::check_option('cloudflare-auto-purge',1) && Swift_Performance_Lite::check_option('cloudflare-email', '', '!=') && Swift_Performance_Lite::check_option('cloudflare-api-key', '', '!=')){
924 self::purge_cloudflare_zones($permalink);
925 }
926
927 // Clear permalink
928 self::clear_single_cached_item($permalink);
929
930 // Prebuild cache
931 if (Swift_Performance_Lite::check_option('automated_prebuild_cache',1)){
932 wp_schedule_single_event(time(), 'swift_performance_prebuild_page_cache', array($permalink));
933 }
934
935 do_action('swift_performance_after_clear_permalink_cache', $permalink);
936 }
937
938 /**
939 * Delete cache multiple posts
940 * @param array $post_ids
941 */
942 public static function clear_post_cache_array($post_ids){
943 foreach ((array)$post_ids as $post_id) {
944 clear_post_cache($post_id);
945 }
946 }
947
948 /**
949 * Delete cached post on save
950 * @param int $post_id
951 */
952 public static function clear_post_cache($post_id){
953 do_action('swift_performance_before_clear_post_cache', $post_id);
954
955 // Don't clear cache on autosave
956 if (defined('DOING_AUTOSAVE')){
957 return;
958 }
959
960 // WooCommerce product variation
961 if (get_post_type($post_id) == 'product_variation'){
962 $parent_id = apply_filters('swift_peformance_get_product_variation_parent', wp_get_post_parent_id($post_id), $post_id);
963 $post_id = (!empty($parent_id) ? $parent_id : $post_id);
964 $maybe_comments_feed = false;
965 }
966
967 $permalink = get_permalink($post_id);
968
969 if (!Swift_Performance_Cache::is_object_cacheable($permalink, $post_id)){
970 return;
971 }
972
973 $permalinks = array();
974
975 // Permalink
976 $permalinks[] = $permalink;
977
978 // Comments feed
979 if (Swift_Performance_Lite::check_option('cache-feed',1)){
980 @$permalinks[] = get_post_comments_feed_link($post_id);
981 }
982
983 // AMP
984 if (function_exists('amp_get_permalink')) {
985 $amp_url = amp_get_permalink($post_id);
986 if (!empty($amp_url)){
987 $permalinks[] = $amp_url;
988 }
989 }
990
991 // REST API
992 if (Swift_Performance_Lite::check_option('cache-rest',1)){
993 $rest_url = self::get_rest_url($post_id);
994 if (!empty($rest_url)){
995 $permalinks[] = $rest_url;
996 }
997 }
998
999 // Archive URLs
1000 if (Swift_Performance_Lite::check_option('cache-archive',1)){
1001 @$archive_urls = self::get_archive_urls($post_id);
1002 if (!empty($archive_urls)){
1003 $permalinks = array_merge($permalinks, $archive_urls);
1004 }
1005 }
1006
1007 // Author
1008 if (Swift_Performance_Lite::check_option('cache-author',1)){
1009 @$author_urls = self::get_author_urls($post_id);
1010 if (!empty($author_urls)){
1011 $permalinks = array_merge($permalinks, $author_urls);
1012 }
1013 }
1014
1015 // Feeds
1016 if (Swift_Performance_Lite::check_option('cache-feed',1)){
1017 $permalinks[] = get_bloginfo_rss('rdf_url');
1018 $permalinks[] = get_bloginfo_rss('rss_url');
1019 $permalinks[] = get_bloginfo_rss('rss2_url');
1020 $permalinks[] = get_bloginfo_rss('atom_url');
1021 $permalinks[] = get_bloginfo_rss('comments_rss2_url');
1022 }
1023
1024
1025 // Cloudflare
1026 if(Swift_Performance_Lite::check_option('cloudflare-auto-purge',1) && Swift_Performance_Lite::check_option('cloudflare-email', '', '!=') && Swift_Performance_Lite::check_option('cloudflare-api-key', '', '!=')){
1027 self::purge_cloudflare_zones($permalinks);
1028 }
1029
1030 // WP Engine
1031 if (method_exists('WpeCommon', 'purge_varnish_cache')){
1032 WpeCommon::purge_varnish_cache($post_id);
1033 }
1034
1035 // Clear cached permalinks
1036 foreach ($permalinks as $permalink) {
1037 self::clear_single_cached_item($permalink);
1038 }
1039
1040 // Prebuild cache
1041 if (Swift_Performance_Lite::check_option('automated_prebuild_cache',1)){
1042 wp_schedule_single_event(time(), 'swift_performance_prebuild_page_cache', array($permalinks));
1043 }
1044
1045 do_action('swift_performance_after_clear_post_cache', $post_id);
1046 }
1047
1048 /**
1049 * Clear pagecache after publish/update post
1050 */
1051 public static function clear_cache_after_post(){
1052 $pages = Swift_Performance_Lite::get_option('clear-page-cache-after-post');
1053 if (!empty($pages)){
1054 foreach ((array)$pages as $page){
1055 self::clear_post_cache($page);
1056 }
1057 }
1058 }
1059
1060 /**
1061 * Clear single item from cache
1062 * @param string $permalink
1063 * @param int $post_id
1064 */
1065 public static function clear_single_cached_item($permalink, $post_id = 0){
1066 do_action('swift_performance_before_clear_single_cached_item', $permalink, $post_id);
1067 global $wp_rewrite, $wpdb;
1068
1069 if (empty($permalink)){
1070 return;
1071 }
1072
1073 if (Swift_Performance_Lite::check_option('caching-mode', array('disk_cache_rewrite', 'disk_cache_php'), 'IN')){
1074 $base_path = trailingslashit(parse_url($permalink, PHP_URL_PATH));
1075 $css_dir = $js_dir = '';
1076 $desktop_cache_path = $base_path . 'desktop/';
1077 $mobile_cache_path = $base_path . 'mobile/';
1078 $paginate_cache_path = $base_path . trailingslashit($wp_rewrite->pagination_base);
1079
1080 if (Swift_Performance_Lite::check_option('separate-css', 1)){
1081 $css_dir = apply_filters('swift_performance_css_dir', trailingslashit(trim(parse_url($permalink, PHP_URL_PATH),'/')) . 'css');
1082 }
1083 if (Swift_Performance_Lite::check_option('separate-js', 1)){
1084 $js_dir = apply_filters('swift_performance_css_dir', trailingslashit(trim(parse_url($permalink, PHP_URL_PATH),'/')) . 'js');
1085 }
1086
1087 self::recursive_rmdir($desktop_cache_path);
1088 self::recursive_rmdir($mobile_cache_path);
1089 self::recursive_rmdir($paginate_cache_path);
1090
1091 if (!empty($css_dir)){
1092 self::recursive_rmdir($css_dir);
1093 }
1094
1095 if (!empty($js_dir)){
1096 self::recursive_rmdir($js_dir);
1097 }
1098
1099 Swift_Performance_Lite::log('Clear post cache (disk) ID: ' . $post_id . ', permalink: ' . $permalink, 9);
1100 }
1101 else {
1102 // Memcached
1103 $path = trailingslashit(parse_url($permalink, PHP_URL_PATH) . '/(desktop|mobile|'.preg_quote($wp_rewrite->pagination_base).')/');
1104 $memcached = self::get_memcache_instance();
1105 $keys = $memcached->getAllKeys();
1106 foreach($keys as $item) {
1107 if(preg_match('~^swift-performance_' . $path .'~', $item)) {
1108 $memcached->delete($item);
1109 Swift_Performance_Lite::log('Clear post cache (memcached) ID: ' . $post_id . ', permalink: ' . $permalink, 9);
1110 }
1111 }
1112 }
1113
1114 // Varnish
1115 if(Swift_Performance_Lite::check_option('varnish-auto-purge',1)){
1116 self::purge_varnish_url($permalink);
1117 }
1118
1119 // Dynamic cache
1120 $url_path = hash('crc32', parse_url($permalink, PHP_URL_PATH));
1121 $transients = $wpdb->get_col("SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE '_transient_swift_performance_dynamic_{$url_path}_%'");
1122 foreach ($transients as $transient) {
1123 delete_transient(str_replace('_transient_','',$transient));
1124 }
1125
1126 // Update Warmup Table
1127 $id = Swift_Performance_Lite::get_warmup_id($permalink);
1128 Swift_Performance_Lite::mysql_query("UPDATE " . SWIFT_PERFORMANCE_TABLE_PREFIX . "warmup SET timestamp = 0, type = '' WHERE id = '{$id}'");
1129
1130 do_action('swift_performance_after_clear_single_cached_item', $permalink, $post_id);
1131 }
1132
1133 /**
1134 * Clear user cache
1135 * @param int $user_id
1136 */
1137 public static function clear_user_cache($user_id){
1138 $user = get_userdata($user_id);
1139 $hash = md5($user->user_login . NONCE_SALT);
1140
1141 do_action('swift_performance_before_clear_user_cache', $user_id);
1142 if (Swift_Performance_Lite::check_option('caching-mode', array('disk_cache_rewrite', 'disk_cache_php'), 'IN') && file_exists(SWIFT_PERFORMANCE_CACHE_DIR)){
1143
1144 $Directory = new RecursiveDirectoryIterator(SWIFT_PERFORMANCE_CACHE_DIR);
1145 $Iterator = new RecursiveIteratorIterator($Directory);
1146 $Regex = new RegexIterator($Iterator, '#'.$hash.'#i', RecursiveRegexIterator::GET_MATCH);
1147
1148 foreach ($Regex as $filename => $file){
1149 $filename = rtrim($filename, '.');
1150 if (file_exists($filename) && is_dir($filename)){
1151 self::recursive_rmdir(str_replace(SWIFT_PERFORMANCE_CACHE_DIR, '', $filename));
1152 }
1153 }
1154
1155 Swift_Performance_Lite::log('Clear user cache (disk) ID: ' . $user_id, 9);
1156 }
1157 else {
1158 // Memcached
1159 $memcached = self::get_memcache_instance();
1160 $keys = $memcached->getAllKeys();
1161 foreach($keys as $item) {
1162 if(preg_match('~'.$user->user_login.'~', $item)) {
1163 $memcached->delete($item);
1164 Swift_Performance_Lite::log('Clear user cache (memcached) ID: ' . $user_id, 9);
1165 }
1166 }
1167 }
1168
1169 do_action('swift_performance_after_clear_user_cache', $user_id);
1170 }
1171
1172 /**
1173 * Clear object cache transients
1174 * @param $type string
1175 */
1176 public static function clear_transients($type = 'all'){
1177 do_action('swift_performance_before_clear_transients', $type);
1178 global $wpdb;
1179 switch ($type) {
1180 case 'ajax':
1181 $wpdb->query('DELETE FROM ' . $wpdb->options . ' WHERE option_name LIKE "%swift_performance_ajax_%"');
1182 Swift_Performance_Lite::log('Clear all transients', 9);
1183 break;
1184 case 'dynamic':
1185 $wpdb->query('DELETE FROM ' . $wpdb->options . ' WHERE option_name LIKE "%swift_performance_dynamic_%"');
1186 Swift_Performance_Lite::log('Clear all transients', 9);
1187 break;
1188 case 'identical':
1189 $wpdb->query('DELETE FROM ' . $wpdb->options . ' WHERE option_name LIKE "%swift_performance_is_identical_%"');
1190 Swift_Performance_Lite::log('Clear all transients', 9);
1191 break;
1192 case 'all':
1193 default:
1194 $wpdb->query('DELETE FROM ' . $wpdb->options . ' WHERE option_name LIKE "%swift_performance_is_identical_%" OR option_name LIKE "%swift_performance_ajax_%" OR option_name LIKE "%swift_performance_dynamic_%"');
1195 Swift_Performance_Lite::log('Clear all transients', 9);
1196 break;
1197 }
1198 do_action('swift_performance_after_clear_transients', $type);
1199 }
1200
1201 /**
1202 * Is current page cacheable
1203 * @return boolean
1204 */
1205 public static function is_cacheable(){
1206 $ajax_cache = !defined('DOING_AJAX');
1207 $xmlrpc = !defined('XMLRPC_REQUEST');
1208 $logged_in = (!Swift_Performance_Lite::is_user_logged_in() || Swift_Performance_Lite::check_option('enable-caching-logged-in-users', 1));
1209 $is_404 = (Swift_Performance_Lite::check_option('cache-404',1) || !Swift_Performance_Lite::is_404());
1210 $query_string = (isset($_SERVER['HTTP_X_PREBUILD']) || Swift_Performance_Lite::check_option('ignore-query-string', 1) || empty($_GET));
1211 $post = (isset($_SERVER['HTTP_X_PREBUILD']) || empty($_POST));
1212 $archive = (Swift_Performance_Lite::check_option('exclude-archive',1) && Swift_Performance_Lite::is_archive() ? false : true);
1213 $author = (Swift_Performance_Lite::check_option('exclude-author',1) && Swift_Performance_Lite::is_author() ? false : true);
1214 $rest = (Swift_Performance_Lite::check_option('exclude-rest',1) && Swift_Performance_Lite::is_rest() ? false : true);
1215 $feed = (Swift_Performance_Lite::check_option('exclude-feed',1) && Swift_Performance_Lite::is_feed() ? false : true);
1216 $maintenance = !self::is_maintenance();
1217 return apply_filters('swift_performance_is_cacheable', !self::is_exluded() && $archive && $author && $rest && $feed && $is_404 && !Swift_Performance_Lite::is_admin() && $post && $query_string && $logged_in && $ajax_cache && $xmlrpc && $maintenance && !self::is_crawler() && !Swift_Performance_Lite::is_password_protected());
1218 }
1219
1220 /**
1221 * Is current AJAX request cacheable
1222 * @return boolean
1223 */
1224 public static function is_cacheable_dynamic(){
1225 // Is there any dynamic parameter?
1226 if (empty($_GET) && empty($_POST)){
1227 return apply_filters('swift_performance_is_cacheable_dynamic', false);
1228 }
1229
1230 if (Swift_Performance_Lite::check_option('dynamic-caching', 1)){
1231 $cacheable = false;
1232 foreach (array_filter((array)Swift_Performance_Lite::get_option('cacheable-dynamic-requests')) as $key){
1233 if (isset($_REQUEST[$key])){
1234 $cacheable = true;
1235 break;
1236 }
1237 }
1238 $xmlrpc = !defined('XMLRPC_REQUEST');
1239 $logged_in = (!Swift_Performance_Lite::is_user_logged_in() || Swift_Performance_Lite::check_option('enable-caching-logged-in-users', 1));
1240 $is_404 = (Swift_Performance_Lite::check_option('cache-404',1) || !Swift_Performance_Lite::is_404());
1241 $archive = (Swift_Performance_Lite::check_option('exclude-archive',1) && Swift_Performance_Lite::is_archive() ? false : true);
1242 $author = (Swift_Performance_Lite::check_option('exclude-author',1) && Swift_Performance_Lite::is_author() ? false : true);
1243 $rest = (Swift_Performance_Lite::check_option('exclude-rest',1) && Swift_Performance_Lite::is_rest() ? false : true);
1244 $feed = (Swift_Performance_Lite::check_option('exclude-feed',1) && Swift_Performance_Lite::is_feed() ? false : true);
1245 $maintenance = !self::is_maintenance();
1246 return apply_filters('swift_performance_is_cacheable_dynamic', !self::is_exluded() && $is_404 && $cacheable && $logged_in && $archive && $author && $rest && $feed && $maintenance && !Swift_Performance_Lite::is_admin() && !self::is_crawler() && !Swift_Performance_Lite::is_password_protected());
1247 }
1248 return apply_filters('swift_performance_is_cacheable_dynamic', false);
1249 }
1250
1251 /**
1252 * Is current AJAX request cacheable
1253 * @return boolean
1254 */
1255 public static function is_cacheable_ajax(){
1256 return apply_filters('swift_performance_is_cacheable_ajax', ((!Swift_Performance_Lite::is_user_logged_in() || Swift_Performance_Lite::check_option('enable-caching-logged-in-users', 1)) && defined('DOING_AJAX') && DOING_AJAX && isset($_REQUEST['action']) && in_array($_REQUEST['action'], array_filter((array)Swift_Performance_Lite::get_option('cacheable-ajax-actions')))));
1257 }
1258
1259 /**
1260 * Is given URL and/or id for prebuild
1261 * @param string $url
1262 * @param int $id
1263 * @return boolean
1264 */
1265 public static function is_object_cacheable($url, $id = 0){
1266 global $wpdb;
1267
1268 $cacheable = true;
1269 $excluded = false;
1270
1271 // Check dynamic parameters
1272 $query_string = parse_url($url, PHP_URL_QUERY);
1273 parse_str($query_string, $query);
1274 if (!empty($query)){
1275 if (Swift_Performance_Lite::check_option('dynamic-caching', 1)){
1276 $cacheable_dynamic = false;
1277 foreach (array_filter((array)Swift_Performance_Lite::get_option('cacheable-dynamic-requests')) as $key){
1278 if (isset($query[$key])){
1279 $cacheable_dynamic = true;
1280 break;
1281 }
1282 }
1283 if (!$cacheable_dynamic){
1284 return false;
1285 }
1286 }
1287 else {
1288 return false;
1289 }
1290 }
1291
1292 // Excluded strings
1293 foreach (array_filter((array)Swift_Performance_Lite::get_option('exclude-strings')) as $exclude_string){
1294 if (!empty($exclude_string)){
1295 if (substr($exclude_string,0,1) == '#' && substr($exclude_string,strlen($exclude_string)-1) == '#'){
1296 if (preg_match($exclude_string, parse_url($url, PHP_URL_PATH))){
1297 return false;
1298 }
1299 }
1300 else {
1301 if (strpos(parse_url($url, PHP_URL_PATH), $exclude_string) !== false){
1302 return false;
1303 }
1304 }
1305 }
1306 }
1307
1308 // Excluded pages
1309
1310 // First get the id if we don't have it yet
1311 if (empty($id)){
1312 $id = url_to_postid($url);
1313 }
1314
1315 // If id is still empty is not a post, walk away
1316 if (empty($id)){
1317 return true;
1318 }
1319
1320 // Manually excluded pages
1321 if(in_array($id, (array)Swift_Performance_Lite::get_option('exclude-pages'))){
1322 return false;
1323 }
1324
1325 // Excluded post types
1326 if (in_array(get_post_type($id), (array)Swift_Performance_Lite::get_option('exclude-post-types'))){
1327 return false;
1328 }
1329
1330 // Password protected
1331 if (Swift_Performance_Lite::is_password_protected($id)){
1332 return false;
1333 }
1334
1335 // WooCommerce
1336 if(class_exists('woocommerce')){
1337 $results = $wpdb->get_results("SELECT option_value FROM {$wpdb->options} WHERE option_name LIKE 'woocommerce_%_page_id' AND option_name NOT IN ('woocommerce_shop_page_id', 'woocommerce_terms_page_id')", ARRAY_A);
1338 foreach((array)$results as $result){
1339 if (!empty($result['option_value']) && $result['option_value'] == $id){
1340 return false;
1341 }
1342 }
1343 }
1344
1345 return true;
1346 }
1347
1348 /**
1349 * Is current request excluded from cache
1350 * @return boolean
1351 */
1352 public static function is_exluded(){
1353 $excluded = false;
1354
1355 // Excluded strings
1356 foreach (array_filter((array)Swift_Performance_Lite::get_option('exclude-strings')) as $exclude_string){
1357 if (!empty($exclude_string)){
1358 if(substr($exclude_string,0,1) == '#' && substr($exclude_string,strlen($exclude_string)-1) == '#'){
1359 $excluded = preg_match($exclude_string, parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
1360 }
1361 else {
1362 $excluded = strpos(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), $exclude_string) !== false;
1363 }
1364 }
1365 }
1366
1367 // Excluded content parts
1368 if (!$excluded){
1369 foreach (array_filter((array)Swift_Performance_Lite::get_option('exclude-content-parts')) as $exclude_content_part){
1370 if (!empty($exclude_content_part)){
1371 if(substr($exclude_content_part,0,1) == '#' && substr($exclude_content_part,strlen($exclude_content_part)-1) == '#'){
1372 $excluded = preg_match($exclude_content_part, $GLOBALS['swift_performance']->modules['cache']->buffer);
1373 }
1374 else {
1375 $excluded = strpos($GLOBALS['swift_performance']->modules['cache']->buffer, $exclude_content_part) !== false;
1376 }
1377 }
1378 }
1379 }
1380
1381 // Excluded user agents
1382 if (!$excluded){
1383 foreach (array_filter((array)Swift_Performance_Lite::get_option('exclude-useragents')) as $exclude_ua){
1384 if (!empty($exclude_ua)){
1385 if(substr($exclude_ua,0,1) == '#' && substr($exclude_ua,strlen($exclude_ua)-1) == '#'){
1386 $excluded = preg_match($exclude_ua, $_SERVER['HTTP_USER_AGENT']);
1387 }
1388 else {
1389 $excluded = strpos($_SERVER['HTTP_USER_AGENT'], $exclude_ua) !== false;
1390 }
1391 }
1392 }
1393 }
1394
1395 // Excluded pages
1396 if (!$excluded){
1397 // Manually excluded pages
1398 $excluded = (get_the_ID() != 0 && in_array(get_the_ID(), (array)Swift_Performance_Lite::get_option('exclude-pages')) );
1399
1400 // WooCommerce
1401 if(class_exists('woocommerce')){
1402 global $wpdb;
1403 $results = $wpdb->get_results("SELECT option_value FROM {$wpdb->options} WHERE option_name LIKE 'woocommerce_%_page_id' AND option_name NOT IN ('woocommerce_shop_page_id', 'woocommerce_terms_page_id')", ARRAY_A);
1404 foreach((array)$results as $result){
1405 if (!empty($result['option_value']) && $result['option_value'] == get_the_ID()){
1406 $excluded = true;
1407 }
1408 }
1409 }
1410
1411 }
1412
1413 // Excluded post types
1414 if (!$excluded){
1415 $post_type = get_post_type();
1416 if (!empty($post_type) && in_array($post_type, (array)Swift_Performance_Lite::get_option('exclude-post-types'))){
1417 $excluded = true;
1418 }
1419 }
1420
1421 return apply_filters('swift_performance_is_excluded', $excluded);
1422
1423 }
1424
1425 /**
1426 * Detect crawlers
1427 */
1428 public static function is_crawler(){
1429 $crawlers = array( '.*Java.*outbrain', '008\/', '192\.comAgent', '2ip\.ru', '404checker', '^bluefish ', '^Calypso v\/', '^COMODO DCV', '^DangDang', '^DavClnt', '^FDM ', '^git\/', '^Goose\/', '^HTTPClient\/', '^Java\/', '^Jeode\/', '^Jetty\/', '^Mget', '^Microsoft URL Control', '^NG\/[0-9\.]', '^NING\/', '^PHP\/[0-9]', '^RMA\/', '^Ruby|Ruby\/[0-9]', '^scrutiny\/', '^VSE\/[0-9]', '^WordPress\.com', '^XRL\/[0-9]', '^ZmEu', 'a3logics\.in', 'A6-Indexer', 'a\.pr-cy\.ru', 'Aboundex', 'aboutthedomain', 'Accoona-AI-Agent', 'acoon', 'acrylicapps\.com\/pulp', 'adbeat', 'AddThis', 'ADmantX', 'adressendeutschland', 'Advanced Email Extractor v', 'agentslug', 'AHC', 'aihit', 'aiohttp\/', 'Airmail', 'akula\/', 'alertra', 'alexa site audit', 'Alibaba\.Security\.Heimdall', 'alyze\.info', 'amagit', 'AndroidDownloadManager', 'Anemone', 'Ant\.com', 'Anturis Agent', 'AnyEvent-HTTP\/', 'Apache-HttpClient\/', 'AportWorm\/[0-9]', 'AppEngine-Google', 'Arachmo', 'arachnode', 'Arachnophilia', 'aria2', 'asafaweb.com', 'AskQuickly', 'Astute', 'asynchttp', 'autocite', 'Autonomy', 'B-l-i-t-z-B-O-T', 'Backlink-Ceck\.de', 'Bad-Neighborhood', 'baidu\.com', 'baypup\/[0-9]', 'baypup\/colbert', 'BazQux', 'BCKLINKS', 'BDFetch', 'BegunAdvertising\/', 'BigBozz', 'biglotron', 'BingLocalSearch', 'BingPreview', 'binlar', 'biNu image cacher', 'biz_Directory', 'Blackboard Safeassign', 'Bloglovin', 'BlogPulseLive', 'BlogSearch', 'Blogtrottr', 'boitho\.com-dc', 'BPImageWalker', 'Braintree-Webhooks', 'Branch Metrics API', 'Branch-Passthrough', 'Browsershots', 'BUbiNG', 'Butterfly\/', 'BuzzSumo', 'CAAM\/[0-9]', 'CakePHP', 'CapsuleChecker', 'CaretNail', 'catexplorador', 'cb crawl', 'CC Metadata Scaper', 'Cerberian Drtrs', 'CERT\.at-Statistics-Survey', 'cg-eye', 'changedetection', 'Charlotte', 'CheckHost', 'checkprivacy', 'chkme\.com', 'CirrusExplorer\/', 'CISPA Vulnerability Notification', 'CJNetworkQuality', 'clips\.ua\.ac\.be', 'Cloud mapping experiment', 'CloudFlare-AlwaysOnline', 'Cloudinary\/[0-9]', 'cmcm\.com', 'coccoc', 'CommaFeed', 'Commons-HttpClient', 'Comodo SSL Checker', 'contactbigdatafr', 'convera', 'copyright sheriff', 'Covario-IDS', 'CrawlForMe\/[0-9]', 'cron-job\.org', 'Crowsnest', 'curb', 'Curious George', 'curl', 'cuwhois\/[0-9]', 'cybo\.com', 'DareBoost', 'DataparkSearch', 'dataprovider', 'Daum(oa)?[ \/][0-9]', 'DeuSu', 'developers\.google\.com\/\+\/web\/snippet\/', 'Digg', 'Dispatch\/', 'dlvr', 'DMBrowser-UV', 'DNS-Tools Header-Analyzer', 'DNSPod-reporting', 'docoloc', 'Dolphin http client\/', 'DomainAppender', 'dotSemantic', 'downforeveryoneorjustme', 'downnotifier\.com', 'DowntimeDetector', 'Dragonfly File Reader', 'drupact', 'Drupal \(\+http:\/\/drupal\.org\/\)', 'dubaiindex', 'EARTHCOM', 'Easy-Thumb', 'ec2linkfinder', 'eCairn-Grabber', 'ECCP', 'ElectricMonk', 'elefent', 'EMail Exractor', 'EmailWolf', 'Embed PHP Library', 'Embedly', 'europarchive\.org', 'evc-batch\/[0-9]', 'EventMachine HttpClient', 'Evidon', 'Evrinid', 'ExactSearch', 'ExaleadCloudview', 'Excel\/', 'Exif Viewer', 'Exploratodo', 'ezooms', 'facebookexternalhit', 'facebookplatform', 'fairshare', 'Faraday v', 'Faveeo', 'Favicon downloader', 'FavOrg', 'Feed Wrangler', 'Feedbin', 'FeedBooster', 'FeedBucket', 'FeedBunch\/[0-9]', 'FeedBurner', 'FeedChecker', 'Feedly', 'Feedspot', 'Feedwind\/[0-9]', 'feeltiptop', 'Fetch API', 'Fetch\/[0-9]', 'Fever\/[0-9]', 'findlink', 'findthatfile', 'FlipboardBrowserProxy', 'FlipboardProxy', 'FlipboardRSS', 'fluffy', 'flynxapp', 'forensiq', 'FoundSeoTool\/[0-9]', 'free thumbnails', 'FreeWebMonitoring SiteChecker', 'Funnelback', 'g00g1e\.net', 'GAChecker', 'ganarvisitas\/[0-9]', 'geek-tools', 'Genderanalyzer', 'Genieo', 'GentleSource', 'GetLinkInfo', 'getprismatic\.com', 'GetURLInfo\/[0-9]', 'GigablastOpenSource', 'github\.com\/', 'Go [\d\.]* package http', 'Go-http-client', 'gofetch', 'GomezAgent', 'gooblog', 'Goodzer\/[0-9]', 'Google favicon', 'Google Keyword Suggestion', 'Google Keyword Tool', 'Google PP Default', 'Google Search Console', 'Google Web Preview', 'Google-Adwords', 'Google-Apps-Script', 'Google-Calendar-Importer', 'Google-HTTP-Java-Client', 'Google-Publisher-Plugin', 'Google-SearchByImage', 'Google-Site-Verification', 'Google-Structured-Data-Testing-Tool', 'Google-Youtube-Links', 'google_partner_monitoring', 'GoogleDocs', 'GoogleHC\/', 'GoogleProducer', 'GoScraper', 'GoSpotCheck', 'GoSquared-Status-Checker', 'gosquared-thumbnailer', 'GotSiteMonitor', 'grabify', 'Grammarly', 'grouphigh', 'grub-client', 'GTmetrix', 'gvfs\/', 'HAA(A)?RTLAND http client', 'Hatena', 'hawkReader', 'HEADMasterSEO', 'HeartRails_Capture', 'heritrix', 'hledejLevne\.cz\/[0-9]', 'Holmes', 'HootSuite Image proxy', 'Hootsuite-WebFeed\/[0-9]', 'HostTracker', 'ht:\/\/check', 'htdig', 'HTMLParser\/', 'HTTP-Header-Abfrage', 'http-kit', 'HTTP-Tiny', 'HTTP_Compression_Test', 'http_request2', 'http_requester', 'HttpComponents', 'httphr', 'HTTPMon', 'PEAR HTTPRequest', 'httpscheck', 'httpssites_power', 'httpunit', 'HttpUrlConnection', 'httrack', 'hosterstats', 'huaweisymantec', 'HubPages.*crawlingpolicy', 'HubSpot Connect', 'HubSpot Marketing Grader', 'HyperZbozi.cz Feeder', 'i2kconnect\/', 'ichiro', 'IdeelaborPlagiaat', 'IDG Twitter Links Resolver', 'IDwhois\/[0-9]', 'Iframely', 'igdeSpyder', 'IlTrovatore', 'ImageEngine\/', 'Imagga', 'InAGist', 'inbound\.li parser', 'InDesign%20CC', 'infegy', 'infohelfer', 'InfoWizards Reciprocal Link System PRO', 'Instapaper', 'inpwrd\.com', 'Integrity', 'integromedb', 'internet_archive', 'InternetSeer', 'internetVista monitor', 'IODC', 'IOI', 'iplabel', 'IPS\/[0-9]', 'ips-agent', 'IPWorks HTTP\/S Component', 'iqdb\/', 'Irokez', 'isitup\.org', 'iskanie', 'iZSearch', 'janforman', 'Jigsaw', 'Jobboerse', 'jobo', 'Jobrapido', 'JS-Kit', 'KeepRight OpenStreetMap Checker', 'KeyCDN Perf Test', 'Keywords Research', 'KickFire', 'KimonoLabs\/', 'Kml-Google', 'knows\.is', 'kouio', 'KrOWLer', 'kulturarw3', 'KumKie', 'L\.webis', 'Larbin', 'LayeredExtractor', 'LibVLC', 'libwww', 'Licorne Image Snapshot', 'Liferea\/', 'link checker', 'Link Valet', 'link_thumbnailer', 'LinkAlarm\/', 'linkCheck', 'linkdex', 'LinkExaminer', 'linkfluence', 'linkpeek', 'LinkTiger', 'LinkWalker', 'Lipperhey', 'livedoor ScreenShot', 'LoadImpactPageAnalyzer', 'LoadImpactRload', 'LongURL API', 'looksystems\.net', 'ltx71', 'lwp-trivial', 'lycos', 'LYT\.SR', 'mabontland', 'MagpieRSS', 'Mail.Ru', 'MailChimp\.com', 'Mandrill', 'MapperCmd', 'marketinggrader', 'Mediapartners-Google', 'MegaIndex\.ru', 'Melvil Rawi\/', 'MergeFlow-PageReader', 'Metaspinner', 'MetaURI', 'Microsearch', 'Microsoft-WebDAV-MiniRedir', 'Microsoft Data Access Internet Publishing Provider Protocol', 'Microsoft Office ', 'Microsoft Windows Network Diagnostics', 'Mindjet', 'Miniflux', 'mixdata dot com', 'mixed-content-scan', 'Mnogosearch', 'mogimogi', 'Mojolicious \(Perl\)', 'monitis', 'Monitority\/[0-9]', 'montastic', 'MonTools', 'Moreover', 'Morning Paper', 'mowser', 'Mrcgiguy', 'mShots', 'MVAClient', 'nagios', 'Najdi\.si\/', 'Needle\/', 'NETCRAFT', 'NetLyzer FastProbe', 'netresearch', 'NetShelter ContentScan', 'NetTrack', 'Netvibes', 'Neustar WPM', 'NeutrinoAPI', 'NewsBlur .*Finder', 'NewsGator', 'newsme', 'newspaper\/', 'NG-Search', 'nineconnections\.com', 'NLNZ_IAHarvester', 'Nmap Scripting Engine', 'node-superagent', 'node\.io', 'nominet\.org\.uk', 'Norton-Safeweb', 'Notifixious', 'notifyninja', 'nuhk', 'nutch', 'Nuzzel', 'nWormFeedFinder', 'Nymesis', 'Ocelli\/[0-9]', 'oegp', 'okhttp', 'Omea Reader', 'omgili', 'Online Domain Tools', 'OpenCalaisSemanticProxy', 'Openstat\/', 'OpenVAS', 'Optimizer', 'Orbiter', 'OrgProbe\/[0-9]', 'ow\.ly', 'ownCloud News', 'OxfordCloudService\/[0-9]', 'Page Analyzer', 'Page Valet', 'page2rss', 'page_verifier', 'PagePeeker', 'Pagespeed\/[0-9]', 'Panopta', 'panscient', 'parsijoo', 'PayPal IPN', 'Pcore-HTTP', 'Pearltrees', 'peerindex', 'Peew', 'PhantomJS\/', 'Photon\/', 'phpcrawl', 'phpservermon', 'Pi-Monster', 'ping\.blo\.gs\/', 'Pingdom', 'Pingoscope', 'PingSpot', 'pinterest\.com', 'Pizilla', 'Ploetz \+ Zeller', 'Plukkie', 'PocketParser', 'Pompos', 'Porkbun', 'Port Monitor', 'postano', 'PostPost', 'postrank', 'PowerPoint\/', 'Priceonomics Analysis Engine', 'PritTorrent\/[0-9]', 'Prlog', 'probethenet', 'Project 25499', 'Promotion_Tools_www.searchenginepromotionhelp.com', 'prospectb2b', 'Protopage', 'proximic', 'pshtt, https scanning', 'PTST ', 'PTST\/[0-9]+', 'Pulsepoint XT3 web scraper', 'Python-httplib2', 'python-requests', 'Python-urllib', 'Qirina Hurdler', 'QQDownload', 'Qseero', 'Qualidator.com SiteAnalyzer', 'Quora Link Preview', 'Qwantify', 'Radian6', 'RankSonicSiteAuditor', 'Readability', 'RealPlayer%20Downloader', 'RebelMouse', 'redback\/', 'Redirect Checker Tool', 'ReederForMac', 'request\.js', 'ResponseCodeTest\/[0-9]', 'RestSharp', 'RetrevoPageAnalyzer', 'Riddler', 'Rival IQ', 'Robosourcer', 'Robozilla\/[0-9]', 'ROI Hunter', 'RPT-HTTPClient', 'RSSOwl', 'safe-agent-scanner', 'SalesIntelligent', 'SauceNAO', 'SBIder', 'Scoop', 'scooter', 'ScoutJet', 'ScoutURLMonitor', 'Scrapy', 'ScreenShotService\/[0-9]', 'Scrubby', 'search\.thunderstone', 'SearchSight', 'Seeker', 'semanticdiscovery', 'semanticjuice', 'Semiocast HTTP client', 'SEO Browser', 'Seo Servis', 'seo-nastroj.cz', 'Seobility', 'SEOCentro', 'SeoCheck', 'SeopultContentAnalyzer', 'Server Density Service Monitoring', 'servernfo\.com', 'Seznam screenshot-generator', 'Shelob', 'Shoppimon Analyzer', 'ShoppimonAgent\/[0-9]', 'ShopWiki', 'ShortLinkTranslate', 'shrinktheweb', 'SilverReader', 'SimplePie', 'SimplyFast', 'Site-Shot\/', 'Site24x7', 'SiteBar', 'SiteCondor', 'siteexplorer\.info', 'SiteGuardian', 'Siteimprove\.com', 'Sitemap(s)? Generator', 'Siteshooter B0t', 'SiteTruth', 'sitexy\.com', 'SkypeUriPreview', 'slider\.com', 'slurp', 'SMRF URL Expander', 'SMUrlExpander', 'Snappy', 'SniffRSS', 'sniptracker', 'Snoopy', 'sogou web', 'SortSite', 'spaziodati', 'Specificfeeds', 'speedy', 'SPEng', 'Spinn3r', 'spray-can', 'Sprinklr ', 'spyonweb', 'Sqworm', 'SSL Labs', 'StackRambler', 'Statastico\/', 'StatusCake', 'Stratagems Kumo', 'Stroke.cz', 'StudioFACA', 'suchen', 'summify', 'Super Monitoring', 'Surphace Scout', 'SwiteScraper', 'Symfony2 BrowserKit', 'SynHttpClient-Built', 'Sysomos', 'T0PHackTeam', 'Tarantula\/', 'Taringa UGC', 'teoma', 'terrainformatica\.com', 'Test Certificate Info', 'Tetrahedron\/[0-9]', 'The Drop Reaper', 'The Expert HTML Source Viewer', 'theinternetrules', 'theoldreader\.com', 'Thumbshots', 'ThumbSniper', 'TinEye', 'Tiny Tiny RSS', 'topster', 'touche.com', 'Traackr.com', 'truwoGPS', 'tweetedtimes\.com', 'Tweetminster', 'Tweezler\/', 'Twikle', 'Twingly', 'ubermetrics-technologies', 'uclassify', 'UdmSearch', 'Untiny', 'UnwindFetchor', 'updated', 'Upflow', 'URLChecker', 'URLitor.com', 'urlresolver', 'Urlstat', 'UrlTrends Ranking Updater', 'Vagabondo', 'vBSEO', 'via ggpht\.com GoogleImageProxy', 'VidibleScraper\/', 'visionutils', 'vkShare', 'voltron', 'voyager\/', 'VSAgent\/[0-9]', 'VSB-TUO\/[0-9]', 'VYU2', 'w3af\.org', 'W3C-checklink', 'W3C-mobileOK', 'W3C_I18n-Checker', 'W3C_Unicorn', 'wangling', 'WatchMouse', 'WbSrch\/', 'web-capture\.net', 'Web-Monitoring', 'Web-sniffer', 'Webauskunft', 'WebCapture', 'WebClient\/', 'webcollage', 'WebCookies', 'WebCorp', 'WebDoc', 'WebFetch', 'WebImages', 'WebIndex', 'webkit2png', 'webmastercoffee', 'webmon ', 'webscreenie', 'Webshot', 'Website Analyzer\/', 'websitepulse[+ ]checker', 'Websnapr\/', 'Webthumb\/[0-9]', 'WebThumbnail', 'WeCrawlForThePeace', 'WeLikeLinks', 'WEPA', 'WeSEE', 'wf84', 'wget', 'WhatsApp', 'WhatsMyIP', 'WhatWeb', 'WhereGoes\?', 'Whibse', 'Whynder Magnet', 'Windows-RSS-Platform', 'WinHttpRequest', 'wkhtmlto', 'wmtips', 'Woko', 'Word\/', 'WordPress\/', 'wotbox', 'WP Engine Install Performance API', 'wprecon\.com survey', 'WPScan', 'wscheck', 'WWW-Mechanize', 'www\.monitor\.us', 'XaxisSemanticsClassifier', 'Xenu Link Sleuth', 'XING-contenttabreceiver\/[0-9]', 'XmlSitemapGenerator', 'xpymep([0-9]?)\.exe', 'Y!J-(ASR|BSC)', 'Yaanb', 'yacy', 'Yahoo Ad monitoring', 'Yahoo Link Preview', 'YahooCacheSystem', 'YahooYSMcm', 'YandeG', 'yandex', 'yanga', 'yeti', ' YLT', 'Yo-yo', 'Yoleo Consumer', 'yoogliFetchAgent', 'YottaaMonitor', 'yourls\.org', 'Zao', 'Zemanta Aggregator', 'Zend\\\\Http\\\\Client', 'Zend_Http_Client', 'zgrab', 'ZnajdzFoto', 'ZyBorg', '[a-z0-9\-_]*((?<!cu)bot|crawler|archiver|transcoder|spider|uptime|validator|fetcher)');
1430 if (Swift_Performance_Lite::check_option('exclude-crawlers',1, '!=')){
1431 return false;
1432 }
1433
1434 return preg_match('~('.implode('|', $crawlers).')~', $_SERVER['HTTP_USER_AGENT']);
1435 }
1436
1437 /**
1438 * Is cached version identical for the request
1439 * @return boolean
1440 */
1441 public static function is_identical($key = ''){
1442 if (Swift_Performance_Lite::check_option('resource-saving-mode', 1)){
1443 $request_uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
1444 $key = (empty($key) && (self::is_cacheable_ajax() || self::is_cacheable_dynamic()) ? Swift_Performance_Lite::get_unique_id() : (empty($key) ? $request_uri : $key));
1445 $identical = get_transient('swift_performance_is_identical_' . md5($key));
1446 if ($identical !== false){
1447 return true;
1448 }
1449 else {
1450 set_transient('swift_performance_is_identical_' . md5($key), true, 3600);
1451 }
1452 }
1453
1454 return false;
1455 }
1456
1457 /**
1458 * Check is maintenance mode active
1459 * @return boolean
1460 */
1461 public static function is_maintenance(){
1462 if (!file_exists( ABSPATH . '.maintenance' )){
1463 return false;
1464 }
1465
1466 global $upgrading;
1467
1468 include( ABSPATH . '.maintenance' );
1469 // If the $upgrading timestamp is older than 10 minutes, don't die.
1470 if ( ( time() - $upgrading ) >= 600 ){
1471 return false;
1472 }
1473 return true;
1474 }
1475
1476 /**
1477 * Locate cache file and return the first match if exists
1478 * @return string|boolean
1479 */
1480 public static function locate_cache_file($permalink){
1481 $cache_file = false;
1482
1483 $basedir = trailingslashit(Swift_Performance_Lite::get_option('cache-path')) . SWIFT_PERFORMANCE_CACHE_BASE_DIR;
1484 $basedir_regex = trailingslashit(Swift_Performance_Lite::get_option('cache-path')) . SWIFT_PERFORMANCE_CACHE_BASE_DIR . trailingslashit('('.implode('|', apply_filters('swift_performance_enabled_hosts', array(parse_url(Swift_Performance_Lite::home_url(), PHP_URL_HOST)))).')/' . trim(preg_quote(parse_url($permalink, PHP_URL_PATH)), '/'));
1485
1486 if (@file_exists($basedir)){
1487 $Directory = new RecursiveDirectoryIterator($basedir);
1488 $Iterator = new RecursiveIteratorIterator($Directory);
1489 $Regex = new RegexIterator($Iterator, '#'.$basedir_regex.'(@prefix/([_a-z0-9]+)/)?(desktop|mobile)/(unauthenticated|(authenticated/(a-z0-9+)))/((index|404)\.html|index\.xml|index\.json)$#i', RecursiveRegexIterator::GET_MATCH);
1490
1491 foreach ($Regex as $filename=>$file){
1492 $cache_file = $filename;
1493 break;
1494 }
1495 }
1496
1497 return $cache_file;
1498 }
1499
1500 /**
1501 * Check if given URL is already in cache
1502 * @param string $permalink
1503 * @return boolean
1504 */
1505 public static function is_cached($permalink){
1506 return (self::get_cache_type($permalink) !== false);
1507 }
1508
1509 /**
1510 * Get cached URL type
1511 * @param string $permalink
1512 * @return boolean|string (html|json|xml|404|false)
1513 */
1514 public static function get_cache_type($permalink){
1515 if (Swift_Performance_Lite::check_option('caching-mode', array('disk_cache_rewrite', 'disk_cache_php'), 'IN')){
1516 $cache_file = Swift_Performance_Cache::locate_cache_file($permalink);
1517 if (empty($cache_file)){
1518 return false;
1519 }
1520
1521 $file = trailingslashit(dirname($cache_file));
1522
1523 if (!file_exists($file)){
1524 return false;
1525 }
1526 if (file_exists($file . 'index.html')){
1527 return 'html';
1528 }
1529 if (file_exists($file . 'index.json')){
1530 return 'json';
1531 }
1532 if (file_exists($file . 'index.xml')){
1533 return 'xml';
1534 }
1535 if (file_exists($file . '404.html')){
1536 return '404';
1537 }
1538 }
1539 else if (Swift_Performance_Lite::check_option('caching-mode', 'memcached_php')){
1540 $path = dirname(Swift_Performance_Cache::locate_cache_file($permalink));
1541
1542 $cached_request = Swift_Performance_Cache::memcached_get($path . 'index.html');
1543 if (!empty($cached_request)){
1544 return 'html';
1545 }
1546
1547 $cached_request = Swift_Performance_Cache::memcached_get($path . 'index.xml');
1548 if (!empty($cached_request)) {
1549 return 'xml';
1550 }
1551
1552 $cached_request = Swift_Performance_Cache::memcached_get($path . 'index.json');
1553 if (!empty($cached_request)) {
1554 return 'json';
1555 }
1556
1557 $cached_request = Swift_Performance_Cache::memcached_get($path . '404.html');
1558 if (!empty($cached_request)) {
1559 return '404';
1560 }
1561 }
1562 return false;
1563 }
1564
1565 /**
1566 * Get cache creation time
1567 * @param string $permalink
1568 * @return int
1569 */
1570 public static function get_cache_time($permalink){
1571 $time = 0;
1572 $path = trailingslashit(dirname(Swift_Performance_Cache::locate_cache_file($permalink)));
1573 if (self::is_cached($permalink)){
1574 if (Swift_Performance_Lite::check_option('caching-mode', array('disk_cache_rewrite', 'disk_cache_php'), 'IN')){
1575 @$time = filectime($path);
1576 }
1577 else {
1578 $cached_request = Swift_Performance_Cache::memcached_get($path . 'index.html');
1579 if (empty($cached_request)) {
1580 $cached_request = Swift_Performance_Cache::memcached_get($path . 'index.xml');
1581 }
1582 if (empty($cached_request)) {
1583 $cached_request = Swift_Performance_Cache::memcached_get($path . 'index.json');
1584 }
1585 if (empty($cached_request)) {
1586 $cached_request = Swift_Performance_Cache::memcached_get($path . '404.html');
1587 }
1588 if (!empty($cached_request) && isset($cached_request['time'])){
1589 $time = $cached_request['time'];
1590 }
1591 }
1592 }
1593 return (int)$time;
1594 }
1595
1596 /**
1597 * Write cached file
1598 * @param string $file file name
1599 * @param string $content file content (default empty)
1600 * @param boolean $force override existing (default false)
1601 * @return string public URL
1602 */
1603 public static function write_file($file, $content = '', $force = false){
1604 // Play in the sandbox only...
1605 if (strpos($file, './')){
1606 return;
1607 }
1608
1609 // Don't write empty content
1610 if (empty($content)){
1611 return;
1612 }
1613
1614 $dir = SWIFT_PERFORMANCE_CACHE_DIR . dirname($file);
1615 // Write cached file if file doesn't exists or we use force mode
1616 if ($force || !file_exists(SWIFT_PERFORMANCE_CACHE_DIR . $file)){
1617 if (!file_exists($dir)){
1618 @mkdir($dir, 0777, true);
1619 }
1620 @file_put_contents(SWIFT_PERFORMANCE_CACHE_DIR . $file, $content);
1621
1622 }
1623
1624 // Logged in hash
1625 $hash = '';
1626 if (isset($_COOKIE[LOGGED_IN_COOKIE])){
1627 list(,,, $hash) = explode('|', $_COOKIE[LOGGED_IN_COOKIE]);
1628 }
1629
1630 // Create empty index.html in every folder recursively to prevent directory index in cache
1631 $current = SWIFT_PERFORMANCE_CACHE_DIR;
1632 $folders = explode('/', dirname($file));
1633 for ($i=0;$i<count($folders);$i++){
1634 $current .= $folders[$i] . '/';
1635 if (($current == SWIFT_PERFORMANCE_CACHE_DIR . 'js/' || ($folders[$i] == 'authenticated' && isset($folders[$i+1]) && $folders[$i+1] == md5($hash))) && !file_exists($current . 'index.html')){
1636 @touch($current . 'index.html');
1637 }
1638 }
1639
1640 Swift_Performance_Lite::log('Write file ' . SWIFT_PERFORMANCE_CACHE_URL . $file, 9);
1641
1642 // Return with cached file URL
1643 return SWIFT_PERFORMANCE_CACHE_URL . $file;
1644 }
1645
1646 /**
1647 * Clear cache folder recursively
1648 * @param string $dir
1649 * @return boolean
1650 */
1651 public static function recursive_rmdir($dir = '', $check_filetime = false){
1652 $basedir = trailingslashit(Swift_Performance_Lite::get_option('cache-path')) . SWIFT_PERFORMANCE_CACHE_BASE_DIR;
1653
1654 foreach (apply_filters('swift_performance_enabled_hosts', array(parse_url(Swift_Performance_Lite::home_url(), PHP_URL_HOST))) as $host){
1655 $cache_dir = trailingslashit($basedir . $host);
1656 if (!file_exists($cache_dir . $dir)){
1657 continue;
1658 }
1659
1660 if (strpos(realpath($cache_dir . $dir), realpath($cache_dir)) === false){
1661 continue;
1662 }
1663
1664 $files = array_diff(scandir($cache_dir . $dir), array('.','..'));
1665 foreach ((array)$files as $file) {
1666 if (!$check_filetime || (time() - filectime($cache_dir . $dir . '/'. $file) >= Swift_Performance_Lite::get_option('cache-expiry-time') && !in_array($dir, array('/css','/js'))) ){
1667 is_dir($cache_dir . $dir . '/'. $file) ? self::recursive_rmdir($dir . '/'. $file, $check_filetime) : @unlink($cache_dir . $dir . '/'. $file);
1668 }
1669 }
1670
1671 @rmdir($cache_dir . $dir);
1672 }
1673 }
1674
1675 /**
1676 * Send PURGE request to varnish
1677 * @param string $url
1678 * @param boolean $wildcard (default false)
1679 */
1680 public static function purge_varnish_url($url, $wildcard = false){
1681 $varnish_host = Swift_Performance_Lite::get_option('custom-varnish-host');
1682 if (empty($varnish_host)){
1683 $varnish_host = 'http://' . parse_url(Swift_Performance_Lite::home_url(), PHP_URL_HOST);
1684 }
1685
1686 $parsed_url = parse_url($url);
1687 $purge_url = $varnish_host . (isset($parsed_url['path']) ? $parsed_url['path'] : '') . ($wildcard ? '.*' : '');
1688
1689 $response = wp_remote_request($purge_url, array( 'method' => 'PURGE', 'headers' => array( 'host' => parse_url(home_url(), PHP_URL_HOST), 'X-Purge-Method' => ($wildcard ? 'regex' : 'exact'))));
1690 Swift_Performance_Lite::log('Purge Varnish URL: ' . $purge_url, 9);
1691 }
1692
1693 /**
1694 * Get first 20 CF zones
1695 * @return boolean|array
1696 */
1697 public static function get_cloudflare_zones(){
1698 if (Swift_Performance_Lite::check_option('cloudflare-email', '') || Swift_Performance_Lite::check_option('cloudflare-api-key', '')){
1699 return false;
1700 }
1701
1702 $host = Swift_Performance_Lite::get_option('cloudflare-host');
1703 $host = (empty($host) ? parse_url(Swift_Performance_Lite::home_url(), PHP_URL_HOST) : $host);
1704 $zones = get_transient('swift_performance_'.$host.'_cf_zones');
1705 if (empty($zones)){
1706 $response = wp_remote_get('https://api.cloudflare.com/client/v4/zones?name='.$host.'&status=active&page=1&per_page=20&order=status&direction=desc&match=all',
1707 array(
1708 'headers' => array(
1709 'X-Auth-Email' => Swift_Performance_Lite::get_option('cloudflare-email'),
1710 'X-Auth-Key' => Swift_Performance_Lite::get_option('cloudflare-api-key'),
1711 'Content-Type' => 'application/json'
1712 )
1713 )
1714 );
1715 if (is_wp_error($response)){
1716 Swift_Performance_Lite::log('Cloudflare get zones failed: ' . $response->get_error_message(), 1);
1717 }
1718 else {
1719 $decoded = json_decode($response['body']);
1720 if (isset($decoded->errors) && !empty($decoded->errors)){
1721 Swift_Performance_Lite::log('Cloudflare API error: ' . $decoded->errors[0]->message, 1);
1722 }
1723 else if (isset($decoded->result)){
1724 $zones = $decoded->result;
1725 // Cache it for 60 sec
1726 set_transient('swift_performance_'.$host.'_cf_zones', $zones, 60);
1727 }
1728 else {
1729 Swift_Performance_Lite::log('Cloudflare zones are missing', 6);
1730 }
1731 }
1732
1733 }
1734
1735 return $zones;
1736 }
1737
1738 /**
1739 * Get CF zones and cache it
1740 * @param string|array $files
1741 * @return boolean|array
1742 */
1743 public static function purge_cloudflare_zones($files = array()){
1744 if (empty($files)){
1745 $body = '{"purge_everything":true}';
1746 }
1747 else {
1748 $body = '{"files":' . json_encode((array)$files) . '}';
1749 }
1750
1751 $zones = self::get_cloudflare_zones();
1752 if (!empty($zones) && is_array($zones)){
1753 foreach ($zones as $zone) {
1754 $response = wp_remote_request('https://api.cloudflare.com/client/v4/zones/'.$zone->id.'/purge_cache', array(
1755 'method' => 'DELETE',
1756 'body' => $body,
1757 'headers' => array(
1758 'X-Auth-Email' => Swift_Performance_Lite::get_option('cloudflare-email'),
1759 'X-Auth-Key' => Swift_Performance_Lite::get_option('cloudflare-api-key'),
1760 'Content-Type' => 'application/json'
1761 )
1762 ));
1763 if (is_wp_error($response)){
1764 Swift_Performance_Lite::log('Cloudflare purge zone failed. Zonde ID: ' . $zone->id . ' Error:' . $response->get_error_message(), 1);
1765 }
1766 else{
1767 $decoded = json_decode($response['body']);
1768
1769 if (isset($decoded->success) && $decoded->success == 'true'){
1770 if (empty($files)){
1771 Swift_Performance_Lite::log('Cloudflare zone ' . $zone->id . ' all files were purged.', 9);
1772 }
1773 else {
1774 Swift_Performance_Lite::log('Cloudflare zone ' . $zone->id . ' were purged. Files: ' . implode(', ', (array)$files), 9);
1775 }
1776 }
1777 else {
1778 Swift_Performance_Lite::log('Cloudflare purge zone failed. Zonde ID: ' . $zone->id . ' Error:' . json_encode($decoded->errors));
1779 }
1780 }
1781 }
1782 }
1783 }
1784
1785 /**
1786 * Get REST API url for specific post (if exists)
1787 * @return string
1788 */
1789 public static function get_rest_url($post_id){
1790 $post = get_post_type_object($post_id);
1791 $post_type = get_post_type($post_id);
1792 $namespace = 'wp/v2/';
1793 $url = '';
1794 if (function_exists('get_rest_url')){
1795 if (isset($post->rest_base)){
1796 $url = get_rest_url() . $namespace . $post->rest_base . '/' . $post_id . '/';
1797 }
1798 else if ($post_type == 'post') {
1799 $url = get_rest_url() . $namespace . 'posts/' . $post_id . '/';
1800 }
1801 else if ($post_type == 'page') {
1802 $url = get_rest_url() . $namespace . 'pages/' . $post_id . '/';
1803 }
1804 }
1805 return $url;
1806 }
1807
1808 /**
1809 * Get archive page urls for specific post
1810 * @return array
1811 */
1812 public static function get_archive_urls($post_id){
1813 $namespace = 'wp/v2/';
1814 $urls = array();
1815
1816 // Categories
1817 $categories = get_the_category($post_id);
1818 if (!empty($categories)){
1819 foreach ($categories as $category){
1820 $urls[] = get_category_link($category->term_id);
1821 $urls[] = get_rest_url() . $namespace . 'categories/' . $category->term_id . '/';
1822 }
1823 }
1824
1825 // Tags
1826 $tags = get_the_category($post_id);
1827 if (!empty($tags)){
1828 foreach ($tags as $tag){
1829 $urls[] = get_tag_link($tag->term_id);
1830 $urls[] = get_rest_url() . $namespace . 'tags/' . $tag->term_id . '/';
1831 }
1832 }
1833
1834 // Custom taxonomies
1835 $taxonomies = get_post_taxonomies($post_id);
1836 if (!empty($taxonomies)){
1837 foreach ($taxonomies as $taxonomy){
1838 $terms = wp_get_post_terms($post_id, $taxonomy);
1839 foreach ($terms as $term) {
1840 $urls[] = get_term_link($term);
1841 $urls[] = get_rest_url() . $namespace. trailingslashit($term->taxonomy) . $term->slug . '/';
1842 }
1843 }
1844 }
1845
1846 // WooCommerce product
1847 if (get_post_type($post_id) == 'product' || get_post_type($post_id) == 'product_variation'){
1848 $urls[] = get_permalink(get_option('woocommerce_shop_page_id'));
1849 }
1850
1851 return $urls;
1852 }
1853
1854 /**
1855 * Get author page urls for specific post
1856 * @return array
1857 */
1858 public static function get_author_urls($post_id){
1859 $urls = array();
1860 $namespace = 'wp/v2/';
1861 $author_id = get_post_field('post_author', $post_id);
1862 $urls[] = get_author_posts_url($author_id);
1863 if (Swift_Performance_Lite::check_option('cache-feed',1)){
1864 $urls[] = get_author_feed_link($author_id);
1865 }
1866 if (function_exists('get_rest_url')){
1867 $urls[] = get_rest_url() . $namespace . 'users/' . $author_id . '/';
1868 }
1869
1870 return $urls;
1871 }
1872
1873 /**
1874 * Get an instance of Memcached API
1875 * @return Memcached
1876 */
1877 public static function get_memcache_instance(){
1878 if (!isset($GLOBALS['swift_performance_memcached']) || !is_object($GLOBALS['swift_performance_memcached'])){
1879 $result = false;
1880 // Create Memcached instance
1881 if (class_exists('Memcached')){
1882 $GLOBALS['swift_performance_memcached'] = new Memcached();
1883 $result = $GLOBALS['swift_performance_memcached']->addServer(Swift_Performance_Lite::get_option('memcached-host'), (int)Swift_Performance_Lite::get_option('memcached-port'));
1884 }
1885 // Memcached isn't exists
1886 else {
1887 $fail = true;
1888 }
1889
1890 // Fallback if init was failed
1891 if ($result === false){
1892 Swift_Performance_Lite::set_option('caching-mode', 'disk_cache_php');
1893 Swift_Performance_Lite::update_option('caching-mode', 'disk_cache_php');
1894 }
1895 }
1896 return @$GLOBALS['swift_performance_memcached'];
1897 }
1898
1899 /**
1900 * Set content in memcached
1901 * @param string $path
1902 * @param string $content
1903 */
1904 public static function memcached_set($path, $content){
1905 $memcached = Swift_Performance_Cache::get_memcache_instance();
1906
1907 $memcached->set('swift-performance_' . $path, $content, Swift_Performance_Lite::get_option('cache-expiry-time'));
1908 }
1909
1910 /**
1911 * Get content from memcached
1912 * @param string $path
1913 */
1914 public static function memcached_get($path){
1915 $memcached = Swift_Performance_Cache::get_memcache_instance();
1916
1917 return $memcached->get('swift-performance_' . $path);
1918 }
1919
1920 /**
1921 * Proxy the current request and return the results
1922 * @return string
1923 */
1924 public static function proxy_request(){
1925 // Headers
1926 $headers = array('X-Proxy' => 'Swift_Performance');
1927 foreach ($_SERVER as $key => $value) {
1928 $headers[preg_replace('~^http_~i', '', $key)] = $value;
1929 }
1930
1931 // Cookies
1932 $cookies = array();
1933 foreach ( $_COOKIE as $name => $value ) {
1934 $cookies[] = new WP_Http_Cookie( array( 'name' => $name, 'value' => $value ) );
1935 }
1936
1937 // Proxy the original request
1938 $response = wp_remote_post(home_url($_SERVER['REQUEST_URI']), array(
1939 'method' => 'POST',
1940 'timeout' => 45,
1941 'blocking' => true,
1942 'headers' => $headers,
1943 'body' => $_POST,
1944 'cookies' => $cookies
1945 )
1946 );
1947
1948 if (!is_wp_error($response)){
1949 return $response['body'];
1950 }
1951
1952 return false;
1953 }
1954
1955 /**
1956 * Remove http to avoid mixed content on cached pages
1957 */
1958 public static function avoid_mixed_content($html){
1959 if (Swift_Performance_Lite::check_option('avoid-mixed-content',1)){
1960 // Avoid mixed content issues
1961 $html = preg_replace('~https?://([^"\'\s]*)\.(jpe?g|png|gif|swf|flv|mpeg|mpg|mpe|3gp|mov|avi|wav|flac|mp2|mp3|m4a|mp4|m4p|aac)~i', "//$1.$2", $html);
1962 $html = preg_replace('~src=(\'|")https?:~', 'src=$1', $html);
1963 $html = preg_replace('~<link rel=\'stylesheet\'((?!href=).)*href=(\'|")https?:~', '<link rel=\'stylesheet\'$1href=$2', $html);
1964 }
1965 return $html;
1966 }
1967
1968
1969}
1970
1971// Create instance
1972if (Swift_Performance_Lite::check_option('enable-caching', 1)){
1973 return new Swift_Performance_Cache();
1974}
1975else {
1976 return false;
1977}
1978
1979?>