PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.7.0
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.7.0
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 / minify / class-wp-optimize-minify-cache-functions.php

class-wp-optimize-minify-cache-functions.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 4.7.0, at minify/class-wp-optimize-minify-cache-functions.php

595 lines 18.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) die('No direct access allowed');
3
4 if (!defined('WP_OPTIMIZE_MINIFY_DIR')) {
5 die('No direct access.');
6 }
7
8 if (!function_exists('wpo_delete_files')) {
9 include WPO_PLUGIN_MAIN_PATH.'cache/file-based-page-cache-functions.php';
10 }
11
12 class WP_Optimize_Minify_Cache_Functions {
13
14 /**
15 * Fix the permission bits on generated files
16 *
17 * @param string $file - full path to a file
18 * @return bool
19 */
20 public static function fix_permission_bits($file) {
21 if (function_exists('stat')) {
22 if ($stat = stat(dirname($file))) {
23 $perms = $stat['mode'] & 0007777;
24 chmod($file, $perms); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_chmod -- N/A
25 clearstatcache();
26 return true;
27 }
28 }
29
30 // Get permissions from parent directory
31 $perms = 0777;
32 if (function_exists('stat')) {
33 if ($stat = stat(dirname($file))) {
34 $perms = $stat['mode'] & 0007777;
35 }
36 }
37
38 if (file_exists($file)) {
39 if (($perms & ~umask() !== $perms)) {
40 $folder_parts = explode('/', substr($file, strlen(dirname($file)) + 1));
41 for ($i = 1, $c = count($folder_parts); $i <= $c; $i++) {
42 chmod(dirname($file) . '/' . implode('/', array_slice($folder_parts, 0, $i)), $perms); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_chmod -- N/A
43 }
44 }
45 }
46 return true;
47 }
48
49 /**
50 * Get cache directories and urls
51 *
52 * @return array
53 */
54 public static function cache_path() {
55 // get latest time stamp
56 $cache_time = wp_optimize_minify_config()->get('last-cache-update');
57
58 $cache_base_dir = WPO_CACHE_MIN_FILES_DIR . "/$cache_time";
59 $cache_dir_url = WPO_CACHE_MIN_FILES_URL . "/$cache_time/assets";
60 $tmp_dir = WPO_CACHE_MIN_FILES_DIR . "/tmp";
61 $header_dir = WPO_CACHE_MIN_FILES_DIR . "/$cache_time/header";
62 $cache_dir = WPO_CACHE_MIN_FILES_DIR . "/$cache_time/assets";
63
64 // Create directories
65 $dirs = array($cache_dir, $tmp_dir, $header_dir);
66 foreach ($dirs as $target) {
67 $enabled = (bool) wp_optimize_minify_config()->get('enabled');
68 if (false === $enabled) break;
69
70 if (!is_dir($target) && !wp_mkdir_p($target)) {
71 error_log('WP_Optimize_Minify_Cache_Functions::cache_path(): The folder "'.$target.'" could not be created.'); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Used for debugging
72 }
73 }
74 return array(
75 'tmpdir' => $tmp_dir,
76 'cachedir' => $cache_dir,
77 'cachedirurl' => $cache_dir_url,
78 'headerdir' => $header_dir,
79 'cachebasedir' => $cache_base_dir
80 );
81 }
82
83 /**
84 * Increment file names
85 *
86 * @return int
87 */
88 public static function cache_increment() {
89 $stamp = time();
90 wp_optimize_minify_config()->update(array(
91 'last-cache-update' => $stamp
92 ));
93 return $stamp;
94 }
95
96 /**
97 * Reset the cache (Increment + purge temp files)
98 */
99 public static function reset() {
100 self::cache_increment();
101 self::purge_temp_files();
102 }
103
104 /**
105 * Will delete temporary intermediate stuff but leave final css/js alone for compatibility
106 *
107 * @return array
108 */
109 public static function purge_temp_files() {
110 // get cache directories and urls
111 $cache_path = self::cache_path();
112 $tmp_dir = $cache_path['tmpdir'];
113 $header_dir = $cache_path['headerdir'];
114
115 // delete temporary directories only
116 if (is_dir($tmp_dir)) {
117 wpo_delete_files($tmp_dir);
118 }
119 if (is_dir($header_dir)) {
120 wpo_delete_files($header_dir);
121 }
122
123 /**
124 * Action triggered after purging temporary files
125 */
126 do_action('wpo_min_after_purge_temp_files');
127 return array(
128 'tmpdir' => $tmp_dir,
129 'headerdir' => $header_dir,
130 );
131 }
132
133 /**
134 * Purge supported hosting and plugins
135 *
136 * @return array An array of caches purged message
137 */
138 public static function purge_others() {
139 /**
140 * Action triggered before purging other plugins cache
141 */
142 do_action('wpo_min_before_purge_others');
143
144 // WordPress default cache
145 if (function_exists('wp_cache_flush')) {
146 wp_cache_flush();
147 }
148
149 // Purge WP-Optimize
150 $is_cache_purged = WP_Optimize()->get_page_cache()->purge();
151 if ($is_cache_purged) WP_Optimize()->get_page_cache()->file_log("Full Cache Purge triggered by: ". __METHOD__);
152
153 // Store the messages of purged cache if it was successful
154 $result = array();
155
156 // When plugins have a simple method, add them to the array ('Plugin Name' => 'method_name')
157 $others = array(
158 'WP Super Cache' => 'wp_cache_clear_cache',
159 'W3 Total Cache' => 'w3tc_pgcache_flush',
160 'WP Fastest Cache' => 'wpfc_clear_all_cache',
161 'WP Rocket' => 'rocket_clean_domain',
162 'Cachify' => 'cachify_flush_cache',
163 'Comet Cache' => array('comet_cache', 'clear'),
164 'SG Optimizer' => 'sg_cachepress_purge_cache',
165 'Pantheon' => 'pantheon_wp_clear_edge_all',
166 'Zen Cache' => array('zencache', 'clear'),
167 'Breeze' => array('Breeze_PurgeCache', 'breeze_cache_flush'),
168 'Swift Performance' => array('Swift_Performance_Cache', 'clear_all_cache'),
169 );
170
171 foreach ($others as $plugin => $method) {
172 if (is_callable($method)) {
173 call_user_func($method);
174
175 $result[] = self::get_caches_purged_message($plugin);
176 }
177 }
178
179 // Purge LiteSpeed Cache
180 if (is_callable(array('LiteSpeed_Cache_Tags', 'add_purge_tag'))) {
181 LiteSpeed_Cache_Tags::add_purge_tag('*');
182
183 $result[] = self::get_caches_purged_message('LiteSpeed Cache');
184 }
185
186 // Purge Hyper Cache
187 if (class_exists('HyperCache')) {
188 do_action('autoptimize_action_cachepurged');
189
190 $result[] = self::get_caches_purged_message('Hyper Cache');
191 }
192
193 // Purge Godaddy Managed WordPress Hosting (Varnish + APC)
194 if (class_exists('WPaaS\Plugin')) {
195 self::godaddy_request('BAN');
196 // translators: %s is a remote cache system name `Go Daddy Varnish`
197 $result[] = sprintf(__('A cache purge request has been sent to %s.', 'wp-optimize'), '<strong>Go Daddy Varnish</strong>') . ' ' . __('Please note that it may not work every time, due to cache rate limiting by your host.', 'wp-optimize');
198 }
199
200 // purge cache enabler
201 if (has_action('ce_clear_cache')) {
202 do_action('ce_clear_cache');
203
204 $result[] = self::get_caches_purged_message('Cache Enabler');
205 }
206
207 // Purge WP Engine
208 if (class_exists("WpeCommon")) {
209 if (method_exists('WpeCommon', 'purge_memcached')) {
210 WpeCommon::purge_memcached();
211 }
212 if (method_exists('WpeCommon', 'clear_maxcdn_cache')) {
213 WpeCommon::clear_maxcdn_cache();
214 }
215 if (method_exists('WpeCommon', 'purge_varnish_cache')) {
216 WpeCommon::purge_varnish_cache();
217 }
218
219 if (method_exists('WpeCommon', 'purge_memcached') || method_exists('WpeCommon', 'clear_maxcdn_cache') || method_exists('WpeCommon', 'purge_varnish_cache')) {
220 // translators: %s is a remote cache system name `WP Engine`
221 $result[] = sprintf(__('A cache purge request has been sent to %s.', 'wp-optimize'), '<strong>WP Engine</strong>') . ' ' . __('Please note that it may not work every time, due to cache rate limiting by your host.', 'wp-optimize');
222 }
223 }
224
225 // Purge Kinsta
226 global $kinsta_cache;
227 if (isset($kinsta_cache) && class_exists('\\Kinsta\\CDN_Enabler')) {
228 if (!empty($kinsta_cache->kinsta_cache_purge) && is_callable(array($kinsta_cache->kinsta_cache_purge, 'purge_complete_caches'))) {
229 $kinsta_cache->kinsta_cache_purge->purge_complete_caches();
230
231 $result[] = self::get_remote_caches_purged_message('Kinsta');
232 }
233 }
234
235 // Purge Pagely
236 if (class_exists('PagelyCachePurge')) {
237 $purge_pagely = new PagelyCachePurge();
238 if (is_callable(array($purge_pagely, 'purgeAll'))) {
239 $purge_pagely->purgeAll();
240
241 $result[] = self::get_remote_caches_purged_message('Pagely');
242 }
243 }
244
245 // Purge Pressidium
246 if (defined('WP_NINUKIS_WP_NAME') && class_exists('Ninukis_Plugin') && is_callable(array('Ninukis_Plugin', 'get_instance'))) {
247 $purge_pressidum = Ninukis_Plugin::get_instance();
248 if (is_callable(array($purge_pressidum, 'purgeAllCaches'))) {
249 $purge_pressidum->purgeAllCaches();
250
251 $result[] = self::get_remote_caches_purged_message('Pressidium');
252 }
253 }
254
255 // Purge Savvii
256 if (defined('\Savvii\CacheFlusherPlugin::NAME_DOMAINFLUSH_NOW')) {
257 $purge_savvii = new \Savvii\CacheFlusherPlugin();
258 if (is_callable(array($purge_savvii, 'domainflush'))) {
259 $purge_savvii->domainflush();
260
261 $result[] = self::get_remote_caches_purged_message('Savvii');
262 }
263 }
264
265 /**
266 * Action triggered when purging other plugins cache, and nothing was triggered
267 */
268 do_action('wpo_min_after_purge_others');
269
270 return $result;
271 }
272
273 /**
274 * Returns the purged cache message for the given plugin name
275 *
276 * @param string $plugin_name Name of the plugin
277 *
278 * @return string
279 */
280 public static function get_caches_purged_message($plugin_name) {
281 $message = sprintf(
282 // translators: %s is a plugin name
283 __('All caches from %s have also been purged.', 'wp-optimize'),
284 '<strong>' . esc_html($plugin_name) . '</strong>'
285 );
286
287 return $message;
288 }
289
290 /**
291 * Returns remote purged cache message for given host name
292 *
293 * @param string $host_name
294 *
295 * @return string
296 */
297 public static function get_remote_caches_purged_message($host_name) {
298 $message = sprintf(
299 // translators: %s is a remote cache system name
300 __('A cache purge request has been sent to %s.', 'wp-optimize'),
301 '<strong>' . esc_html($host_name) . '</strong>'
302 );
303
304 return $message;
305 }
306
307 /**
308 * Purge all public files on uninstallation
309 * This will break cached pages that ref minified JS/CSS
310 *
311 * @return Boolean
312 */
313 public static function purge() {
314 $log = '';
315 if (is_dir(WPO_CACHE_MIN_FILES_DIR)) {
316 if (wpo_delete_files(WPO_CACHE_MIN_FILES_DIR)) {
317 $log = "[Minify] files and folders are deleted recursively";
318 } else {
319 $log = "[Minify] recursive files and folders deletion unsuccessful";
320 }
321 if (wp_optimize_minify_config()->get('debug')) {
322 error_log($log); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Used for debugging
323 }
324 }
325 return true;
326 }
327
328 /**
329 * Purge cache files older than 30 days
330 *
331 * @return array
332 */
333 public static function purge_old() {
334 if (!class_exists('WP_Optimize_Minify_Config')) {
335 include_once WPO_PLUGIN_MAIN_PATH . 'minify/class-wp-optimize-minify-config.php';
336 }
337 $cache_time = wp_optimize_minify_config()->get('last-cache-update');
338 $cache_lifespan = wp_optimize_minify_config()->get('cache_lifespan');
339
340 /**
341 * Minify cache lifespan
342 *
343 * @param int The minify cache expiry timestamp
344 */
345 $expires = apply_filters('wp_optimize_minify_cache_expiry_time', time() - 86400 * $cache_lifespan);
346 $log = array();
347
348 // get all directories that are a direct child of current directory
349 if (is_dir(WPO_CACHE_MIN_FILES_DIR) && wp_is_writable(dirname(WPO_CACHE_MIN_FILES_DIR))) {
350 if ($handle = opendir(WPO_CACHE_MIN_FILES_DIR)) {
351 while (false !== ($d = readdir($handle))) {
352 if ('.' === $d || '..' === $d) {
353 continue;
354 }
355 $log[] = "cache expiration time - $expires";
356 $log[] = "checking if cache has expired - $d";
357
358 $dir = WPO_CACHE_MIN_FILES_DIR.'/'.$d;
359 $is_empty_dir = self::is_empty_minify_subdir($dir);
360
361 if ($d !== (string) $cache_time && (is_numeric($d) && $d <= $expires || $is_empty_dir)) {
362 if (is_dir($dir)) {
363 $log[] = "deleting cache in $dir";
364 if (wpo_delete_files($dir)) {
365 $log[] = "files and folders are deleted recursively - $dir";
366 } else {
367 $log[] = "recursive files and folders deletion unsuccessful - $dir";
368 }
369 if (file_exists($dir)) {
370 if (rmdir($dir)) { // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_rmdir -- N/A
371 $log[] = "folder deleted successfully - $dir";
372 } else {
373 $log[] = "folder deletion unsuccessful - $dir";
374 }
375 }
376 }
377 }
378 }
379 closedir($handle);
380 }
381 }
382 if (wp_optimize_minify_config()->get('debug')) {
383 foreach ($log as $message) {
384 error_log($message); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Used for debugging
385 }
386 }
387
388 return $log;
389 }
390
391 /**
392 * Checks whether the minify cache subdirectory (/ddddddddd/) is empty, i.e. whether the 'assets' and 'header' subdirectories contain no files.
393 *
394 * @param string $dir
395 * @return boolean
396 */
397 private static function is_empty_minify_subdir($dir) {
398 return wpo_is_empty_dir($dir . '/assets') && wpo_is_empty_dir($dir . '/header');
399 }
400
401 /**
402 * Get transients from the disk
403 *
404 * @param string $key
405 * @return string|boolean
406 */
407 public static function get_transient($key) {
408 $cache_path = self::cache_path();
409 $tmp_dir = $cache_path['tmpdir'];
410 $f = $tmp_dir.'/'.$key.'.transient';
411 clearstatcache();
412 if (file_exists($f)) {
413 return file_get_contents($f);
414 } else {
415 return false;
416 }
417 }
418
419 /**
420 * Set cache on disk
421 *
422 * @param string $key
423 * @param mixed $code
424 *
425 * @return boolean
426 */
427 public static function set_transient($key, $code) {
428 if (is_null($code) || empty($code)) {
429 return false;
430 }
431 $cache_path = self::cache_path();
432 $tmp_dir = $cache_path['tmpdir'];
433 $f = $tmp_dir.'/'.$key.'.transient';
434 file_put_contents($f, $code);
435 self::fix_permission_bits($f);
436 return true;
437 }
438
439 /**
440 * Get the cache size and count
441 *
442 * @param string $folder
443 * @return string
444 */
445 public static function get_cachestats($folder) {
446 $stats = WP_Optimize_Utils::get_folder_stats($folder);
447
448 // Translators: %s: number of files.
449 $translated_file_count = sprintf(_n('%s file', '%s files', $stats['file_count'], 'wp-optimize'), number_format_i18n($stats['file_count']));
450 return WP_Optimize()->format_size($stats['size']) . ' (' . $translated_file_count . ')';
451 }
452
453 /**
454 * Purge GoDaddy Managed WordPress Hosting (Varnish)
455 *
456 * Source: https://github.com/wp-media/wp-rocket/blob/master/inc/3rd-party/hosting/godaddy.php
457 *
458 * @param string $method
459 * @param string|null $url
460 */
461 public static function godaddy_request($method, $url = null) {
462 $url = empty($url) ? home_url() : $url;
463 $host = wp_parse_url($url, PHP_URL_HOST);
464 $url = set_url_scheme(str_replace($host, WPaas\Plugin::vip(), $url), 'http');
465 wp_cache_flush();
466 update_option('gd_system_last_cache_flush', time()); // purge apc
467 wp_remote_request(esc_url_raw($url), array('method' => $method, 'blocking' => false, 'headers' => array('Host' => $host)));
468 }
469
470 /**
471 * List all cache files
472 *
473 * @param integer $stamp A timestamp
474 * @param boolean $use_cache If true, do not use transient value
475 * @return array
476 */
477 public static function get_cached_files($stamp = 0, $use_cache = true) {
478 if ($use_cache && $files = get_transient('wpo_minify_get_cached_files')) {
479 return $files;
480 }
481 $cache_path = self::cache_path();
482 $cache_dir = $cache_path['cachedir'];
483 $size = self::get_cachestats($cache_dir);
484 $total_size = self::get_cachestats(WPO_CACHE_MIN_FILES_DIR);
485 $o = wp_optimize_minify_config()->get();
486 $cache_time = (0 === $o['last-cache-update']) ? __('Never.', 'wp-optimize') : self::format_date_time($o['last-cache-update']);
487 $return = array(
488 'js' => array(),
489 'css' => array(),
490 'stamp' => $stamp,
491 'cachesize' => esc_html($size),
492 'total_cache_size' => esc_html($total_size),
493 'cacheTime' => $cache_time,
494 'cachePath' => $cache_path['cachedir']
495 );
496
497 // Inspect directory with opendir, since glob might not be available in some systems
498 clearstatcache();
499 if (is_dir($cache_dir.'/') && $handle = opendir($cache_dir.'/')) {
500 while (false !== ($file = readdir($handle))) {
501 $file = $cache_dir.'/'.$file;
502 $ext = pathinfo($file, PATHINFO_EXTENSION);
503 if (in_array($ext, array('js', 'css'))) {
504 $log = self::generate_log($file.'.json' );
505 $min_css = substr($file, 0, -4).'.min.css';
506 $minjs = substr($file, 0, -3).'.min.js';
507 $file_name = basename($file);
508 $file_url = trailingslashit($cache_path['cachedirurl']).$file_name;
509 if ('css' === $ext && file_exists($min_css)) {
510 $file_name = basename($min_css);
511 }
512 if ('js' === $ext && file_exists($minjs)) {
513 $file_name = basename($minjs);
514 }
515 $file_size = WP_Optimize()->format_size(filesize($file));
516 $uid = hash('adler32', $file_name);
517 array_push($return[$ext], array('uid' => $uid, 'filename' => $file_name, 'file_url' => $file_url, 'log' => $log, 'fsize' => $file_size));
518 }
519 }
520 closedir($handle);
521 }
522 set_transient('wpo_minify_get_cached_files', $return, DAY_IN_SECONDS);
523 return $return;
524 }
525
526 /**
527 * Generate log information from a json file.
528 *
529 * @param string $file Full path of log file.
530 *
531 * @return mixed Could be either a 'json_decode' object upon successful parsing of the JSON file, or a stdClass object
532 * upon failure. In the case of stdClass object, $obj->error will contain the error message.
533 */
534 public static function generate_log($file) {
535 $error_log = new stdClass();
536
537 $cache_path = self::cache_path();
538 $file_name = basename($file);
539 $file_url = trailingslashit($cache_path['cachedirurl']) . $file_name;
540 $file_link_html = '<a href="' . esc_url($file_url) . '" target="_blank">' . $file_name . '</a>';
541
542 if (!file_exists($file)) {
543 // translators: %s is a log file link
544 $error_log->error = sprintf(__('Log file %s is missing', 'wp-optimize'), $file_link_html);
545 return $error_log;
546 }
547
548 $log = json_decode(file_get_contents($file));
549
550 $is_valid_json = json_last_error() === JSON_ERROR_NONE;
551
552 if (!$is_valid_json) {
553 // translators: %1$s is a log file link, %2$s is the error message
554 $error_log->error = sprintf(__('JSON error in file %1$s | Error details: %2$s', 'wp-optimize'), $file_link_html, json_last_error_msg());
555 return $error_log;
556 }
557
558 if (!isset($log->header) || !isset($log->files)) {
559 // translators: %s is a log file link
560 $error_log->error = sprintf(__('Some data is missing in the log file %s', 'wp-optimize'), $file_link_html);
561 return $error_log;
562 }
563
564 return $log;
565 }
566
567 /**
568 * Format a timestamp using WordPress's date_format and time_format
569 *
570 * @param integer $timestamp - The timestamp
571 * @return string
572 */
573 public static function format_date_time($timestamp) {
574 return WP_Optimize()->format_date_time($timestamp);
575 }
576
577 /**
578 * Format the log created when merging assets. Called via array_map
579 *
580 * @param array $files The files array, containing the 'log' object or array.
581 * @return array
582 */
583 public static function format_file_logs($files) {
584 $files['log'] = WP_Optimize()->include_template(
585 'minify/cached-file-log.php',
586 true,
587 array(
588 'log' => $files['log'],
589 'minify_config' => wp_optimize_minify_config()->get(),
590 )
591 );
592 return $files;
593 }
594 }
595