PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.5.2
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.5.2
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / 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.5.2, at minify/class-wp-optimize-minify-cache-functions.php

582 lines 17.7 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 if ($d !== $cache_time && (is_numeric($d) && $d <= $expires)) {
358 $dir = WPO_CACHE_MIN_FILES_DIR.'/'.$d;
359 if (is_dir($dir)) {
360 $log[] = "deleting cache in $dir";
361 if (wpo_delete_files($dir)) {
362 $log[] = "files and folders are deleted recursively - $dir";
363 } else {
364 $log[] = "recursive files and folders deletion unsuccessful - $dir";
365 }
366 if (file_exists($dir)) {
367 if (rmdir($dir)) { // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_rmdir -- N/A
368 $log[] = "folder deleted successfully - $dir";
369 } else {
370 $log[] = "folder deletion unsuccessful - $dir";
371 }
372 }
373 }
374 }
375 }
376 closedir($handle);
377 }
378 }
379 if (wp_optimize_minify_config()->get('debug')) {
380 foreach ($log as $message) {
381 error_log($message); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Used for debugging
382 }
383 }
384
385 return $log;
386 }
387
388 /**
389 * Get transients from the disk
390 *
391 * @param string $key
392 * @return string|boolean
393 */
394 public static function get_transient($key) {
395 $cache_path = self::cache_path();
396 $tmp_dir = $cache_path['tmpdir'];
397 $f = $tmp_dir.'/'.$key.'.transient';
398 clearstatcache();
399 if (file_exists($f)) {
400 return file_get_contents($f);
401 } else {
402 return false;
403 }
404 }
405
406 /**
407 * Set cache on disk
408 *
409 * @param string $key
410 * @param mixed $code
411 *
412 * @return boolean
413 */
414 public static function set_transient($key, $code) {
415 if (is_null($code) || empty($code)) {
416 return false;
417 }
418 $cache_path = self::cache_path();
419 $tmp_dir = $cache_path['tmpdir'];
420 $f = $tmp_dir.'/'.$key.'.transient';
421 file_put_contents($f, $code);
422 self::fix_permission_bits($f);
423 return true;
424 }
425
426 /**
427 * Get the cache size and count
428 *
429 * @param string $folder
430 * @return string
431 */
432 public static function get_cachestats($folder) {
433 $stats = WP_Optimize_Utils::get_folder_stats($folder);
434
435 // Translators: %s: number of files.
436 $translated_file_count = sprintf(_n('%s file', '%s files', $stats['file_count'], 'wp-optimize'), number_format_i18n($stats['file_count']));
437 return WP_Optimize()->format_size($stats['size']) . ' (' . $translated_file_count . ')';
438 }
439
440 /**
441 * Purge GoDaddy Managed WordPress Hosting (Varnish)
442 *
443 * Source: https://github.com/wp-media/wp-rocket/blob/master/inc/3rd-party/hosting/godaddy.php
444 *
445 * @param string $method
446 * @param string|null $url
447 */
448 public static function godaddy_request($method, $url = null) {
449 $url = empty($url) ? home_url() : $url;
450 $host = wp_parse_url($url, PHP_URL_HOST);
451 $url = set_url_scheme(str_replace($host, WPaas\Plugin::vip(), $url), 'http');
452 wp_cache_flush();
453 update_option('gd_system_last_cache_flush', time()); // purge apc
454 wp_remote_request(esc_url_raw($url), array('method' => $method, 'blocking' => false, 'headers' => array('Host' => $host)));
455 }
456
457 /**
458 * List all cache files
459 *
460 * @param integer $stamp A timestamp
461 * @param boolean $use_cache If true, do not use transient value
462 * @return array
463 */
464 public static function get_cached_files($stamp = 0, $use_cache = true) {
465 if ($use_cache && $files = get_transient('wpo_minify_get_cached_files')) {
466 return $files;
467 }
468 $cache_path = self::cache_path();
469 $cache_dir = $cache_path['cachedir'];
470 $size = self::get_cachestats($cache_dir);
471 $total_size = self::get_cachestats(WPO_CACHE_MIN_FILES_DIR);
472 $o = wp_optimize_minify_config()->get();
473 $cache_time = (0 === $o['last-cache-update']) ? __('Never.', 'wp-optimize') : self::format_date_time($o['last-cache-update']);
474 $return = array(
475 'js' => array(),
476 'css' => array(),
477 'stamp' => $stamp,
478 'cachesize' => esc_html($size),
479 'total_cache_size' => esc_html($total_size),
480 'cacheTime' => $cache_time,
481 'cachePath' => $cache_path['cachedir']
482 );
483
484 // Inspect directory with opendir, since glob might not be available in some systems
485 clearstatcache();
486 if (is_dir($cache_dir.'/') && $handle = opendir($cache_dir.'/')) {
487 while (false !== ($file = readdir($handle))) {
488 $file = $cache_dir.'/'.$file;
489 $ext = pathinfo($file, PATHINFO_EXTENSION);
490 if (in_array($ext, array('js', 'css'))) {
491 $log = self::generate_log($file.'.json' );
492 $min_css = substr($file, 0, -4).'.min.css';
493 $minjs = substr($file, 0, -3).'.min.js';
494 $file_name = basename($file);
495 $file_url = trailingslashit($cache_path['cachedirurl']).$file_name;
496 if ('css' === $ext && file_exists($min_css)) {
497 $file_name = basename($min_css);
498 }
499 if ('js' === $ext && file_exists($minjs)) {
500 $file_name = basename($minjs);
501 }
502 $file_size = WP_Optimize()->format_size(filesize($file));
503 $uid = hash('adler32', $file_name);
504 array_push($return[$ext], array('uid' => $uid, 'filename' => $file_name, 'file_url' => $file_url, 'log' => $log, 'fsize' => $file_size));
505 }
506 }
507 closedir($handle);
508 }
509 set_transient('wpo_minify_get_cached_files', $return, DAY_IN_SECONDS);
510 return $return;
511 }
512
513 /**
514 * Generate log information from a json file.
515 *
516 * @param string $file Full path of log file.
517 *
518 * @return mixed Could be either a 'json_decode' object upon successful parsing of the JSON file, or a stdClass object
519 * upon failure. In the case of stdClass object, $obj->error will contain the error message.
520 */
521 public static function generate_log($file) {
522 $error_log = new stdClass();
523
524 $cache_path = self::cache_path();
525 $file_name = basename($file);
526 $file_url = trailingslashit($cache_path['cachedirurl']) . $file_name;
527 $file_link_html = '<a href="' . esc_url($file_url) . '" target="_blank">' . $file_name . '</a>';
528
529 if (!file_exists($file)) {
530 // translators: %s is a log file link
531 $error_log->error = sprintf(__('Log file %s is missing', 'wp-optimize'), $file_link_html);
532 return $error_log;
533 }
534
535 $log = json_decode(file_get_contents($file));
536
537 $is_valid_json = json_last_error() === JSON_ERROR_NONE;
538
539 if (!$is_valid_json) {
540 // translators: %1$s is a log file link, %2$s is the error message
541 $error_log->error = sprintf(__('JSON error in file %1$s | Error details: %2$s', 'wp-optimize'), $file_link_html, json_last_error_msg());
542 return $error_log;
543 }
544
545 if (!isset($log->header) || !isset($log->files)) {
546 // translators: %s is a log file link
547 $error_log->error = sprintf(__('Some data is missing in the log file %s', 'wp-optimize'), $file_link_html);
548 return $error_log;
549 }
550
551 return $log;
552 }
553
554 /**
555 * Format a timestamp using WordPress's date_format and time_format
556 *
557 * @param integer $timestamp - The timestamp
558 * @return string
559 */
560 public static function format_date_time($timestamp) {
561 return WP_Optimize()->format_date_time($timestamp);
562 }
563
564 /**
565 * Format the log created when merging assets. Called via array_map
566 *
567 * @param array $files The files array, containing the 'log' object or array.
568 * @return array
569 */
570 public static function format_file_logs($files) {
571 $files['log'] = WP_Optimize()->include_template(
572 'minify/cached-file-log.php',
573 true,
574 array(
575 'log' => $files['log'],
576 'minify_config' => wp_optimize_minify_config()->get(),
577 )
578 );
579 return $files;
580 }
581 }
582