PluginProbe
Autoptimize / 2.7.0
Autoptimize v2.7.0
2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 All 107 releases
autoptimize / classes / autoptimizeCache.php

autoptimizeCache.php in Autoptimize 2.7.0, at classes/autoptimizeCache.php

815 lines 27.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles disk-cache-related operations.
4 */
5
6 if ( ! defined( 'ABSPATH' ) ) {
7 exit;
8 }
9
10 class autoptimizeCache
11 {
12 /**
13 * Cache filename.
14 *
15 * @var string
16 */
17 private $filename;
18
19 /**
20 * Cache directory path (with a trailing slash).
21 *
22 * @var string
23 */
24 private $cachedir;
25
26 /**
27 * Whether gzipping is done by the web server or us.
28 * True => we don't gzip, the web server does it.
29 * False => we do it ourselves.
30 *
31 * @var bool
32 */
33 private $nogzip;
34
35 /**
36 * Ctor.
37 *
38 * @param string $md5 Hash.
39 * @param string $ext Extension.
40 */
41 public function __construct( $md5, $ext = 'php' )
42 {
43 $_min_ext = '';
44 if ( apply_filters( 'autoptimize_filter_cache_url_add_min_ext', false ) ) {
45 $_min_ext = '.min';
46 }
47
48 $this->cachedir = AUTOPTIMIZE_CACHE_DIR;
49 $this->nogzip = AUTOPTIMIZE_CACHE_NOGZIP;
50 if ( ! $this->nogzip ) {
51 $this->filename = AUTOPTIMIZE_CACHEFILE_PREFIX . $md5 . $_min_ext . '.php';
52 } else {
53 if ( in_array( $ext, array( 'js', 'css' ) ) ) {
54 $this->filename = $ext . '/' . AUTOPTIMIZE_CACHEFILE_PREFIX . $md5 . $_min_ext . '.' . $ext;
55 } else {
56 $this->filename = AUTOPTIMIZE_CACHEFILE_PREFIX . $md5 . $_min_ext . '.' . $ext;
57 }
58 }
59 }
60
61 /**
62 * Returns true if the cached file exists on disk.
63 *
64 * @return bool
65 */
66 public function check()
67 {
68 return file_exists( $this->cachedir . $this->filename );
69 }
70
71 /**
72 * Returns cache contents if they exist, false otherwise.
73 *
74 * @return string|false
75 */
76 public function retrieve()
77 {
78 if ( $this->check() ) {
79 if ( false == $this->nogzip ) {
80 return file_get_contents( $this->cachedir . $this->filename . '.none' );
81 } else {
82 return file_get_contents( $this->cachedir . $this->filename );
83 }
84 }
85 return false;
86 }
87
88 /**
89 * Stores given $data in cache.
90 *
91 * @param string $data Data to cache.
92 * @param string $mime Mimetype.
93 *
94 * @return void
95 */
96 public function cache( $data, $mime )
97 {
98 // off by default; check if cachedirs exist every time before caching
99 //
100 // to be activated for users that experience these ugly errors;
101 // PHP Warning: file_put_contents failed to open stream: No such file or directory.
102 if ( apply_filters( 'autoptimize_filter_cache_checkdirs_on_write', false ) ) {
103 $this->check_and_create_dirs();
104 }
105
106 if ( false === $this->nogzip ) {
107 // We handle gzipping ourselves.
108 $file = 'default.php';
109 $phpcode = file_get_contents( AUTOPTIMIZE_PLUGIN_DIR . 'config/' . $file );
110 $phpcode = str_replace( array( '%%CONTENT%%', 'exit;' ), array( $mime, '' ), $phpcode );
111
112 file_put_contents( $this->cachedir . $this->filename, $phpcode );
113 file_put_contents( $this->cachedir . $this->filename . '.none', $data );
114 } else {
115 // Write code to cache without doing anything else.
116 file_put_contents( $this->cachedir . $this->filename, $data );
117
118 // save fallback .js or .css file if filter true (to be false by default) but not if snippet or single.
119 if ( self::do_fallback() && strpos( $this->filename, '_snippet_' ) === false && strpos( $this->filename, '_single_' ) === false ) {
120 $_extension = pathinfo( $this->filename, PATHINFO_EXTENSION );
121 $_fallback_file = AUTOPTIMIZE_CACHEFILE_PREFIX . 'fallback.' . $_extension;
122 if ( ! file_exists( $this->cachedir . $_extension . '/' . $_fallback_file ) ) {
123 file_put_contents( $this->cachedir . $_extension . '/' . $_fallback_file, $data );
124 }
125 }
126
127 if ( apply_filters( 'autoptimize_filter_cache_create_static_gzip', false ) ) {
128 // Create an additional cached gzip file.
129 file_put_contents( $this->cachedir . $this->filename . '.gz', gzencode( $data, 9, FORCE_GZIP ) );
130 // If PHP Brotli extension is installed, create an additional cached Brotli file.
131 if ( function_exists( 'brotli_compress' ) ) {
132 file_put_contents( $this->cachedir . $this->filename . '.br', brotli_compress( $data, 11, BROTLI_GENERIC ) );
133 }
134 }
135 }
136
137 // Provide 3rd party action hook for every cache file that is created.
138 // This hook can for example be used to inject a copy of the created cache file to a other domain.
139 do_action( 'autoptimize_action_cache_file_created', $this->cachedir . $this->filename );
140 }
141
142 /**
143 * Get cache filename.
144 *
145 * @return string
146 */
147 public function getname()
148 {
149 // NOTE: This could've maybe been a do_action() instead, however,
150 // that ship has sailed.
151 // The original idea here was to provide 3rd party code a hook so that
152 // it can "listen" to all the complete autoptimized-urls that the page
153 // will emit... Or something to that effect I think?
154 apply_filters( 'autoptimize_filter_cache_getname', AUTOPTIMIZE_CACHE_URL . $this->filename );
155
156 return $this->filename;
157 }
158
159 /**
160 * Returns true if given `$file` is considered a valid Autoptimize cache file,
161 * false otherwise.
162 *
163 * @param string $dir Directory name (with a trailing slash).
164 * @param string $file Filename.
165 * @return bool
166 */
167 protected static function is_valid_cache_file( $dir, $file )
168 {
169 if ( '.' !== $file && '..' !== $file &&
170 false !== strpos( $file, AUTOPTIMIZE_CACHEFILE_PREFIX ) &&
171 is_file( $dir . $file ) ) {
172
173 // It's a valid file!
174 return true;
175 }
176
177 // Everything else is considered invalid!
178 return false;
179 }
180
181 /**
182 * Clears contents of AUTOPTIMIZE_CACHE_DIR.
183 *
184 * @return void
185 */
186 protected static function clear_cache_classic()
187 {
188 $contents = self::get_cache_contents();
189 foreach ( $contents as $name => $files ) {
190 $dir = rtrim( AUTOPTIMIZE_CACHE_DIR . $name, '/' ) . '/';
191 foreach ( $files as $file ) {
192 if ( self::is_valid_cache_file( $dir, $file ) ) {
193 @unlink( $dir . $file ); // @codingStandardsIgnoreLine
194 }
195 }
196 }
197
198 @unlink( AUTOPTIMIZE_CACHE_DIR . '/.htaccess' ); // @codingStandardsIgnoreLine
199 }
200
201 /**
202 * Recursively deletes the specified pathname (file/directory) if possible.
203 * Returns true on success, false otherwise.
204 *
205 * @param string $pathname Pathname to remove.
206 *
207 * @return bool
208 */
209 protected static function rmdir( $pathname )
210 {
211 $files = self::get_dir_contents( $pathname );
212 foreach ( $files as $file ) {
213 $path = $pathname . '/' . $file;
214 if ( is_dir( $path ) ) {
215 self::rmdir( $path );
216 } else {
217 unlink( $path );
218 }
219 }
220
221 return rmdir( $pathname );
222 }
223
224 /**
225 * Clears contents of AUTOPTIMIZE_CACHE_DIR by renaming the current
226 * cache directory into a new one with a unique name and then
227 * re-creating the default (empty) cache directory.
228 *
229 * Important/ Fixme: this does not take multisite into account, so
230 * if advanced_cache_clear_enabled is true (it is not by default)
231 * then the content for all subsites is zapped!
232 *
233 * @return bool Returns true when everything is done successfully, false otherwise.
234 */
235 protected static function clear_cache_via_rename()
236 {
237 $ok = false;
238 $dir = self::get_pathname_base();
239 $new_name = self::get_unique_name();
240
241 // Makes sure the new pathname is on the same level...
242 $new_pathname = dirname( $dir ) . '/' . $new_name;
243 $renamed = @rename( $dir, $new_pathname ); // @codingStandardsIgnoreLine
244
245 // When renamed, re-create the default cache directory back so it's
246 // available again...
247 if ( $renamed ) {
248 $ok = self::cacheavail();
249 }
250
251 return $ok;
252 }
253
254 /**
255 * Returns true when advanced cache clearing is enabled.
256 *
257 * @return bool
258 */
259 public static function advanced_cache_clear_enabled()
260 {
261 return apply_filters( 'autoptimize_filter_cache_clear_advanced', false );
262 }
263
264 /**
265 * Returns a (hopefully) unique new cache folder name for renaming purposes.
266 *
267 * @return string
268 */
269 protected static function get_unique_name()
270 {
271 $prefix = self::get_advanced_cache_clear_prefix();
272 $new_name = uniqid( $prefix, true );
273
274 return $new_name;
275 }
276
277 /**
278 * Get cache prefix name used in advanced cache clearing mode.
279 *
280 * @return string
281 */
282 protected static function get_advanced_cache_clear_prefix()
283 {
284 $pathname = self::get_pathname_base();
285 $basename = basename( $pathname );
286 $prefix = $basename . '-artifact-';
287
288 return $prefix;
289 }
290
291 /**
292 * Returns an array of file and directory names found within
293 * the given $pathname without '.' and '..' elements.
294 *
295 * @param string $pathname Pathname.
296 *
297 * @return array
298 */
299 protected static function get_dir_contents( $pathname )
300 {
301 return array_slice( scandir( $pathname ), 2 );
302 }
303
304 /**
305 * Wipes directories which were created as part of the fast cache clearing
306 * routine (which renames the current cache directory into a new one with
307 * a custom-prefixed unique name).
308 *
309 * @return bool
310 */
311 public static function delete_advanced_cache_clear_artifacts()
312 {
313 // Don't go through these motions (called from the cachechecker) if advanced cache clear isn't even active.
314 if ( ! self::advanced_cache_clear_enabled() ) {
315 return false;
316 }
317
318 $dir = self::get_pathname_base();
319 $prefix = self::get_advanced_cache_clear_prefix();
320 $parent = dirname( $dir );
321 $ok = false;
322
323 // Returns the list of files without '.' and '..' elements.
324 $files = self::get_dir_contents( $parent );
325 if ( is_array( $files ) && ! empty( $files ) ) {
326 foreach ( $files as $file ) {
327 $path = $parent . '/' . $file;
328 $prefixed = ( false !== strpos( $path, $prefix ) );
329 // Removing only our own (prefixed) directories...
330 if ( is_dir( $path ) && $prefixed ) {
331 $ok = self::rmdir( $path );
332 }
333 }
334 }
335
336 return $ok;
337 }
338
339 /**
340 * Returns the cache directory pathname used.
341 * Done as a function so we canSlightly different
342 * if multisite is used and `autoptimize_separate_blog_caches` filter
343 * is used.
344 *
345 * @return string
346 */
347 public static function get_pathname()
348 {
349 $pathname = self::get_pathname_base();
350
351 if ( is_multisite() && apply_filters( 'autoptimize_separate_blog_caches', true ) ) {
352 $blog_id = get_current_blog_id();
353 $pathname .= $blog_id . '/';
354 }
355
356 return $pathname;
357 }
358
359 /**
360 * Returns the base path of our cache directory.
361 *
362 * @return string
363 */
364 protected static function get_pathname_base()
365 {
366 $pathname = WP_CONTENT_DIR . AUTOPTIMIZE_CACHE_CHILD_DIR;
367
368 return $pathname;
369 }
370
371 /**
372 * Deletes everything from the cache directories.
373 *
374 * @param bool $propagate Whether to trigger additional actions when cache is purged.
375 *
376 * @return bool
377 */
378 public static function clearall( $propagate = true )
379 {
380 if ( ! self::cacheavail() ) {
381 return false;
382 }
383
384 // TODO/FIXME: If cache is big, switch to advanced/new cache clearing automatically?
385 if ( self::advanced_cache_clear_enabled() ) {
386 self::clear_cache_via_rename();
387 } else {
388 self::clear_cache_classic();
389 }
390
391 // Remove 404 handler if required.
392 if ( self::do_fallback() ) {
393 $_fallback_php = trailingslashit( WP_CONTENT_DIR ) . 'autoptimize_404_handler.php';
394 @unlink( $_fallback_php ); // @codingStandardsIgnoreLine
395 }
396
397 // Remove the transient so it gets regenerated...
398 delete_transient( 'autoptimize_stats' );
399
400 // Cache was just purged, clear page cache and allow others to hook into our purging...
401 if ( true === $propagate ) {
402 if ( ! function_exists( 'autoptimize_do_cachepurged_action' ) ) {
403 function autoptimize_do_cachepurged_action() {
404 do_action( 'autoptimize_action_cachepurged' );
405 }
406 }
407 add_action( 'shutdown', 'autoptimize_do_cachepurged_action', 11 );
408 add_action( 'autoptimize_action_cachepurged', array( 'autoptimizeCache', 'flushPageCache' ), 10, 0 );
409 }
410
411 // Warm cache (part of speedupper)!
412 if ( apply_filters( 'autoptimize_filter_speedupper', true ) && false == get_transient( 'autoptimize_cache_warmer_protector' ) ) {
413 set_transient( 'autoptimize_cache_warmer_protector', 'I shall not warm cache for another 10 minutes.', 60 * 10 );
414 $url = site_url() . '/?ao_speedup_cachebuster=' . rand( 1, 100000 );
415 $url = apply_filters( 'autoptimize_filter_cache_warmer_url', $url );
416 $cache = @wp_remote_get( $url ); // @codingStandardsIgnoreLine
417 unset( $cache );
418 }
419
420 return true;
421 }
422
423 /**
424 * Wrapper for clearall but with false param
425 * to ensure the event is not propagated to others
426 * through our own hooks (to avoid infinite loops).
427 *
428 * @return bool
429 */
430 public static function clearall_actionless()
431 {
432 return self::clearall( false );
433 }
434
435 /**
436 * Returns the contents of our cache dirs.
437 *
438 * @return array
439 */
440 protected static function get_cache_contents()
441 {
442 $contents = array();
443
444 foreach ( array( '', 'js', 'css' ) as $dir ) {
445 $contents[ $dir ] = scandir( AUTOPTIMIZE_CACHE_DIR . $dir );
446 }
447
448 return $contents;
449 }
450
451 /**
452 * Returns stats about cached contents.
453 *
454 * @return array
455 */
456 public static function stats()
457 {
458 $stats = get_transient( 'autoptimize_stats' );
459
460 // If no transient, do the actual scan!
461 if ( ! is_array( $stats ) ) {
462 if ( ! self::cacheavail() ) {
463 return 0;
464 }
465 $stats = self::stats_scan();
466 $count = $stats[0];
467 if ( $count > 100 ) {
468 // Store results in transient.
469 set_transient(
470 'autoptimize_stats',
471 $stats,
472 apply_filters( 'autoptimize_filter_cache_statsexpiry', HOUR_IN_SECONDS )
473 );
474 }
475 }
476
477 return $stats;
478 }
479
480 /**
481 * Performs a scan of cache directory contents and returns an array
482 * with 3 values: count, size, timestamp.
483 * count = total number of found files
484 * size = total filesize (in bytes) of found files
485 * timestamp = unix timestamp when the scan was last performed/finished.
486 *
487 * @return array
488 */
489 protected static function stats_scan()
490 {
491 $count = 0;
492 $size = 0;
493
494 // Scan everything in our cache directories.
495 foreach ( self::get_cache_contents() as $name => $files ) {
496 $dir = rtrim( AUTOPTIMIZE_CACHE_DIR . $name, '/' ) . '/';
497 foreach ( $files as $file ) {
498 if ( self::is_valid_cache_file( $dir, $file ) ) {
499 if ( AUTOPTIMIZE_CACHE_NOGZIP &&
500 (
501 false !== strpos( $file, '.js' ) ||
502 false !== strpos( $file, '.css' ) ||
503 false !== strpos( $file, '.img' ) ||
504 false !== strpos( $file, '.txt' )
505 )
506 ) {
507 // Web server is gzipping, we count .js|.css|.img|.txt files.
508 $count++;
509 } elseif ( ! AUTOPTIMIZE_CACHE_NOGZIP && false !== strpos( $file, '.none' ) ) {
510 // We are gzipping ourselves via php, counting only .none files.
511 $count++;
512 }
513 $size += filesize( $dir . $file );
514 }
515 }
516 }
517
518 $stats = array( $count, $size, time() );
519
520 return $stats;
521 }
522
523 /**
524 * Ensures the cache directory exists, is writeable and contains the
525 * required .htaccess files.
526 * Returns false in case it fails to ensure any of those things.
527 *
528 * @return bool
529 */
530 public static function cacheavail()
531 {
532 if ( false === autoptimizeCache::check_and_create_dirs() ) {
533 return false;
534 }
535
536 // Using .htaccess inside our cache folder to overrule wp-super-cache.
537 $htaccess = AUTOPTIMIZE_CACHE_DIR . '/.htaccess';
538 if ( ! is_file( $htaccess ) ) {
539 /**
540 * Create `wp-content/AO_htaccess_tmpl` file with
541 * whatever htaccess rules you might need
542 * if you want to override default AO htaccess
543 */
544 $htaccess_tmpl = WP_CONTENT_DIR . '/AO_htaccess_tmpl';
545 if ( is_file( $htaccess_tmpl ) ) {
546 $content = file_get_contents( $htaccess_tmpl );
547 } elseif ( is_multisite() || ! AUTOPTIMIZE_CACHE_NOGZIP ) {
548 $content = '<IfModule mod_expires.c>
549 ExpiresActive On
550 ExpiresByType text/css A30672000
551 ExpiresByType text/javascript A30672000
552 ExpiresByType application/javascript A30672000
553 </IfModule>
554 <IfModule mod_headers.c>
555 Header append Cache-Control "public, immutable"
556 </IfModule>
557 <IfModule mod_deflate.c>
558 <FilesMatch "\.(js|css)$">
559 SetOutputFilter DEFLATE
560 </FilesMatch>
561 </IfModule>
562 <IfModule mod_authz_core.c>
563 <Files *.php>
564 Require all granted
565 </Files>
566 </IfModule>
567 <IfModule !mod_authz_core.c>
568 <Files *.php>
569 Order allow,deny
570 Allow from all
571 </Files>
572 </IfModule>';
573 } else {
574 $content = '<IfModule mod_expires.c>
575 ExpiresActive On
576 ExpiresByType text/css A30672000
577 ExpiresByType text/javascript A30672000
578 ExpiresByType application/javascript A30672000
579 </IfModule>
580 <IfModule mod_headers.c>
581 Header append Cache-Control "public, immutable"
582 </IfModule>
583 <IfModule mod_deflate.c>
584 <FilesMatch "\.(js|css)$">
585 SetOutputFilter DEFLATE
586 </FilesMatch>
587 </IfModule>
588 <IfModule mod_authz_core.c>
589 <Files *.php>
590 Require all denied
591 </Files>
592 </IfModule>
593 <IfModule !mod_authz_core.c>
594 <Files *.php>
595 Order deny,allow
596 Deny from all
597 </Files>
598 </IfModule>';
599 }
600
601 if ( self::do_fallback() === true ) {
602 $content .= "\nErrorDocument 404 " . trailingslashit( parse_url( content_url(), PHP_URL_PATH ) ) . 'autoptimize_404_handler.php';
603 }
604 @file_put_contents( $htaccess, $content ); // @codingStandardsIgnoreLine
605 }
606
607 if ( self::do_fallback() ) {
608 self::check_fallback_php();
609 }
610
611 // All OK!
612 return true;
613 }
614
615 /**
616 * Checks if fallback-php file exists and create it if not.
617 *
618 * Return bool
619 */
620 public static function check_fallback_php() {
621 $_fallback_filename = 'autoptimize_404_handler.php';
622 $_fallback_php = trailingslashit( WP_CONTENT_DIR ) . $_fallback_filename;
623 $_fallback_status = true;
624
625 if ( ! file_exists( $_fallback_php ) ) {
626 $_fallback_php_contents = file_get_contents( AUTOPTIMIZE_PLUGIN_DIR . 'config/' . $_fallback_filename );
627 $_fallback_php_contents = str_replace( '<?php exit;', '<?php', $_fallback_php_contents );
628 $_fallback_php_contents = str_replace( '<!--ao-cache-dir-->', AUTOPTIMIZE_CACHE_DIR, $_fallback_php_contents );
629 if ( apply_filters( 'autoptimize_filter_cache_fallback_log_errors', false ) ) {
630 $_fallback_php_contents = str_replace( '// error_log', 'error_log', $_fallback_php_contents );
631 }
632 $_fallback_status = file_put_contents( $_fallback_php, $_fallback_php_contents );
633 }
634
635 return $_fallback_status;
636 }
637
638 /**
639 * Tells if AO should try to avoid 404's by creating fallback filesize
640 * and create a php 404 handler and tell .htaccess to redirect to said handler
641 * and hook into WordPress to redirect 404 to said handler as well. NGINX users
642 * are smart enough to get this working, no? ;-)
643 *
644 * Return bool
645 */
646 public static function do_fallback() {
647 static $_do_fallback = null;
648
649 if ( null === $_do_fallback ) {
650 $_do_fallback = (bool) apply_filters( 'autoptimize_filter_cache_do_fallback', autoptimizeOptionWrapper::get_option( 'autoptimize_cache_fallback', '' ) );
651 }
652
653 return $_do_fallback;
654 }
655
656 /**
657 * Hooks into template_redirect, will act on 404-ing requests for
658 * Autoptimized files and redirects to the fallback CSS/ JS if available
659 * and 410'ing ("Gone") if fallback not available.
660 */
661 public static function wordpress_notfound_fallback() {
662 $original_request = strtok( $_SERVER['REQUEST_URI'], '?' );
663 if ( strpos( $original_request, wp_basename( WP_CONTENT_DIR ) . AUTOPTIMIZE_CACHE_CHILD_DIR ) !== false && is_404() ) {
664 // make sure this is not considered a 404.
665 global $wp_query;
666 $wp_query->is_404 = false;
667
668 // set fallback path.
669 $js_or_css = pathinfo( $original_request, PATHINFO_EXTENSION );
670 $fallback_path = AUTOPTIMIZE_CACHE_DIR . $js_or_css . '/autoptimize_fallback.' . $js_or_css;
671
672 // set fallback URL.
673 $fallback_target = preg_replace( '/(.*)_(?:[a-z0-9]{32})\.(js|css)$/', '${1}_fallback.${2}', $original_request );
674
675 // redirect to fallback if possible.
676 if ( $original_request !== $fallback_target && file_exists( $fallback_path ) ) {
677 // redirect to fallback.
678 wp_redirect( $fallback_target, 302 );
679 } else {
680 // return HTTP 410 (gone) reponse.
681 status_header( 410 );
682 }
683 }
684 }
685
686 /**
687 * Checks if cache dirs exist and create if not.
688 * Returns false if not succesful.
689 *
690 * @return bool
691 */
692 public static function check_and_create_dirs() {
693 if ( ! defined( 'AUTOPTIMIZE_CACHE_DIR' ) ) {
694 // We didn't set a cache.
695 return false;
696 }
697
698 foreach ( array( '', 'js', 'css' ) as $dir ) {
699 if ( ! self::check_cache_dir( AUTOPTIMIZE_CACHE_DIR . $dir ) ) {
700 return false;
701 }
702 }
703 return true;
704 }
705
706 /**
707 * Ensures the specified `$dir` exists and is writeable.
708 * Returns false if that's not the case.
709 *
710 * @param string $dir Directory to check/create.
711 *
712 * @return bool
713 */
714 protected static function check_cache_dir( $dir )
715 {
716 // Try creating the dir if it doesn't exist.
717 if ( ! file_exists( $dir ) ) {
718 @mkdir( $dir, 0775, true ); // @codingStandardsIgnoreLine
719 if ( ! file_exists( $dir ) ) {
720 return false;
721 }
722 }
723
724 // If we still cannot write, bail.
725 if ( ! is_writable( $dir ) ) {
726 return false;
727 }
728
729 // Create an index.html in there to avoid prying eyes!
730 $idx_file = rtrim( $dir, '/\\' ) . '/index.html';
731 if ( ! is_file( $idx_file ) ) {
732 @file_put_contents( $idx_file, '<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>' ); // @codingStandardsIgnoreLine
733 }
734
735 return true;
736 }
737
738 /**
739 * Flushes as many page cache plugin's caches as possible.
740 *
741 * @return void
742 */
743 // @codingStandardsIgnoreStart
744 public static function flushPageCache()
745 {
746 if ( function_exists( 'wp_cache_clear_cache' ) ) {
747 if ( is_multisite() ) {
748 $blog_id = get_current_blog_id();
749 wp_cache_clear_cache( $blog_id );
750 } else {
751 wp_cache_clear_cache();
752 }
753 } elseif ( has_action( 'cachify_flush_cache' ) ) {
754 do_action( 'cachify_flush_cache' );
755 } elseif ( function_exists( 'w3tc_pgcache_flush' ) ) {
756 w3tc_pgcache_flush();
757 } elseif ( function_exists( 'wp_fast_cache_bulk_delete_all' ) ) {
758 wp_fast_cache_bulk_delete_all();
759 } elseif ( class_exists( 'WpFastestCache' ) ) {
760 $wpfc = new WpFastestCache();
761 $wpfc->deleteCache();
762 } elseif ( class_exists( 'c_ws_plugin__qcache_purging_routines' ) ) {
763 c_ws_plugin__qcache_purging_routines::purge_cache_dir(); // quick cache
764 } elseif ( class_exists( 'zencache' ) ) {
765 zencache::clear();
766 } elseif ( class_exists( 'comet_cache' ) ) {
767 comet_cache::clear();
768 } elseif ( class_exists( 'WpeCommon' ) ) {
769 // WPEngine cache purge/flush methods to call by default
770 $wpe_methods = array(
771 'purge_varnish_cache',
772 );
773
774 // More agressive clear/flush/purge behind a filter
775 if ( apply_filters( 'autoptimize_flush_wpengine_aggressive', false ) ) {
776 $wpe_methods = array_merge( $wpe_methods, array( 'purge_memcached', 'clear_maxcdn_cache' ) );
777 }
778
779 // Filtering the entire list of WpeCommon methods to be called (for advanced usage + easier testing)
780 $wpe_methods = apply_filters( 'autoptimize_flush_wpengine_methods', $wpe_methods );
781
782 foreach ( $wpe_methods as $wpe_method ) {
783 if ( method_exists( 'WpeCommon', $wpe_method ) ) {
784 WpeCommon::$wpe_method();
785 }
786 }
787 } elseif ( function_exists( 'sg_cachepress_purge_cache' ) ) {
788 sg_cachepress_purge_cache();
789 } elseif ( array_key_exists( 'KINSTA_CACHE_ZONE', $_SERVER ) ) {
790 $_kinsta_clear_cache_url = 'https://localhost/kinsta-clear-cache-all';
791 $_kinsta_response = wp_remote_get(
792 $_kinsta_clear_cache_url,
793 array(
794 'sslverify' => false,
795 'timeout' => 5,
796 )
797 );
798 } elseif ( defined('NGINX_HELPER_BASENAME') ) {
799 do_action( 'rt_nginx_helper_purge_all' );
800 } elseif ( file_exists( WP_CONTENT_DIR . '/wp-cache-config.php' ) && function_exists( 'prune_super_cache' ) ) {
801 // fallback for WP-Super-Cache
802 global $cache_path;
803 if ( is_multisite() ) {
804 $blog_id = get_current_blog_id();
805 prune_super_cache( get_supercache_dir( $blog_id ), true );
806 prune_super_cache( $cache_path . 'blogs/', true );
807 } else {
808 prune_super_cache( $cache_path . 'supercache/', true );
809 prune_super_cache( $cache_path, true );
810 }
811 }
812 }
813 // @codingStandardsIgnoreEnd
814 }
815