Completed
Push — master ( c92350...ab0c5b )
by frank
01:42
created

autoptimizeCache::do_fallback()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
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
        $this->cachedir = AUTOPTIMIZE_CACHE_DIR;
44
        $this->nogzip   = AUTOPTIMIZE_CACHE_NOGZIP;
45
        if ( ! $this->nogzip ) {
46
            $this->filename = AUTOPTIMIZE_CACHEFILE_PREFIX . $md5 . '.php';
47
        } else {
48
            if ( in_array( $ext, array( 'js', 'css' ) ) ) {
49
                $this->filename = $ext . '/' . AUTOPTIMIZE_CACHEFILE_PREFIX . $md5 . '.' . $ext;
50
            } else {
51
                $this->filename = AUTOPTIMIZE_CACHEFILE_PREFIX . $md5 . '.' . $ext;
52
            }
53
        }
54
    }
55
56
    /**
57
     * Returns true if the cached file exists on disk.
58
     *
59
     * @return bool
60
     */
61
    public function check()
62
    {
63
        return file_exists( $this->cachedir . $this->filename );
64
    }
65
66
    /**
67
     * Returns cache contents if they exist, false otherwise.
68
     *
69
     * @return string|false
70
     */
71
    public function retrieve()
72
    {
73
        if ( $this->check() ) {
74
            if ( false == $this->nogzip ) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
75
                return file_get_contents( $this->cachedir . $this->filename . '.none' );
76
            } else {
77
                return file_get_contents( $this->cachedir . $this->filename );
78
            }
79
        }
80
        return false;
81
    }
82
83
    /**
84
     * Stores given $data in cache.
85
     *
86
     * @param string $data Data to cache.
87
     * @param string $mime Mimetype.
88
     *
89
     * @return void
90
     */
91
    public function cache( $data, $mime )
92
    {
93
        // off by default; check if cachedirs exist every time before caching
94
        //
95
        // to be activated for users that experience these ugly errors;
96
        // PHP Warning: file_put_contents failed to open stream: No such file or directory.
97
        if ( apply_filters( 'autoptimize_filter_cache_checkdirs_on_write', false ) ) {
98
            $this->check_and_create_dirs();
99
        }
100
101
        if ( false === $this->nogzip ) {
102
            // We handle gzipping ourselves.
103
            $file    = 'default.php';
104
            $phpcode = file_get_contents( AUTOPTIMIZE_PLUGIN_DIR . 'config/' . $file );
105
            $phpcode = str_replace( array( '%%CONTENT%%', 'exit;' ), array( $mime, '' ), $phpcode );
106
107
            file_put_contents( $this->cachedir . $this->filename, $phpcode );
108
            file_put_contents( $this->cachedir . $this->filename . '.none', $data );
109
        } else {
110
            // Write code to cache without doing anything else.
111
            file_put_contents( $this->cachedir . $this->filename, $data );
112
113
            // save fallback .js or .css file if filter true (to be false by default) but not if snippet or single.
114
            if ( self::do_fallback() && strpos( $this->filename, '_snippet_' ) === false && strpos( $this->filename, '_single_' ) === false ) {
115
                $_extension     = pathinfo( $this->filename, PATHINFO_EXTENSION );
116
                $_fallback_file = AUTOPTIMIZE_CACHEFILE_PREFIX . 'fallback.' . $_extension;
117
                if ( ! file_exists( $this->cachedir . $_extension . '/' . $_fallback_file ) ) {
118
                    file_put_contents( $this->cachedir . $_extension . '/' . $_fallback_file, $data );
119
                }
120
            }
121
122
            if ( apply_filters( 'autoptimize_filter_cache_create_static_gzip', false ) ) {
123
                // Create an additional cached gzip file.
124
                file_put_contents( $this->cachedir . $this->filename . '.gz', gzencode( $data, 9, FORCE_GZIP ) );
125
                // If PHP Brotli extension is installed, create an additional cached Brotli file.
126
                if ( function_exists( 'brotli_compress' ) ) {
127
                    file_put_contents( $this->cachedir . $this->filename . '.br', brotli_compress( $data, 11, BROTLI_GENERIC ) );
128
                }
129
            }
130
        }
131
    }
132
133
    /**
134
     * Get cache filename.
135
     *
136
     * @return string
137
     */
138
    public function getname()
139
    {
140
        // NOTE: This could've maybe been a do_action() instead, however,
141
        // that ship has sailed.
142
        // The original idea here was to provide 3rd party code a hook so that
143
        // it can "listen" to all the complete autoptimized-urls that the page
144
        // will emit... Or something to that effect I think?
145
        apply_filters( 'autoptimize_filter_cache_getname', AUTOPTIMIZE_CACHE_URL . $this->filename );
146
147
        return $this->filename;
148
    }
149
150
    /**
151
     * Returns true if given `$file` is considered a valid Autoptimize cache file,
152
     * false otherwise.
153
     *
154
     * @param string $dir Directory name (with a trailing slash).
155
     * @param string $file Filename.
156
     * @return bool
157
     */
158
    protected static function is_valid_cache_file( $dir, $file )
159
    {
160
        if ( '.' !== $file && '..' !== $file &&
161
            false !== strpos( $file, AUTOPTIMIZE_CACHEFILE_PREFIX ) &&
162
            is_file( $dir . $file ) ) {
163
164
            // It's a valid file!
165
            return true;
166
        }
167
168
        // Everything else is considered invalid!
169
        return false;
170
    }
171
172
    /**
173
     * Clears contents of AUTOPTIMIZE_CACHE_DIR.
174
     *
175
     * @return void
176
     */
177
    protected static function clear_cache_classic()
178
    {
179
        $contents = self::get_cache_contents();
180
        foreach ( $contents as $name => $files ) {
181
            $dir = rtrim( AUTOPTIMIZE_CACHE_DIR . $name, '/' ) . '/';
182
            foreach ( $files as $file ) {
183
                if ( self::is_valid_cache_file( $dir, $file ) ) {
184
                    @unlink( $dir . $file ); // @codingStandardsIgnoreLine
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
185
                }
186
            }
187
        }
188
189
        @unlink( AUTOPTIMIZE_CACHE_DIR . '/.htaccess' ); // @codingStandardsIgnoreLine
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
190
    }
191
192
    /**
193
     * Recursively deletes the specified pathname (file/directory) if possible.
194
     * Returns true on success, false otherwise.
195
     *
196
     * @param string $pathname Pathname to remove.
197
     *
198
     * @return bool
199
     */
200
    protected static function rmdir( $pathname )
201
    {
202
        $files = self::get_dir_contents( $pathname );
203
        foreach ( $files as $file ) {
204
            $path = $pathname . '/' . $file;
205
            if ( is_dir( $path ) ) {
206
                self::rmdir( $path );
207
            } else {
208
                unlink( $path );
209
            }
210
        }
211
212
        return rmdir( $pathname );
213
    }
214
215
    /**
216
     * Clears contents of AUTOPTIMIZE_CACHE_DIR by renaming the current
217
     * cache directory into a new one with a unique name and then
218
     * re-creating the default (empty) cache directory.
219
     *
220
     * Important/ Fixme: this does not take multisite into account, so
221
     * if advanced_cache_clear_enabled is true (it is not by default)
222
     * then the content for all subsites is zapped!
223
     *
224
     * @return bool Returns true when everything is done successfully, false otherwise.
225
     */
226
    protected static function clear_cache_via_rename()
227
    {
228
        $ok       = false;
229
        $dir      = self::get_pathname_base();
230
        $new_name = self::get_unique_name();
231
232
        // Makes sure the new pathname is on the same level...
233
        $new_pathname = dirname( $dir ) . '/' . $new_name;
234
        $renamed      = @rename( $dir, $new_pathname ); // @codingStandardsIgnoreLine
235
236
        // When renamed, re-create the default cache directory back so it's
237
        // available again...
238
        if ( $renamed ) {
239
            $ok = self::cacheavail();
240
        }
241
242
        return $ok;
243
    }
244
245
    /**
246
     * Returns true when advanced cache clearing is enabled.
247
     *
248
     * @return bool
249
     */
250
    public static function advanced_cache_clear_enabled()
251
    {
252
        return apply_filters( 'autoptimize_filter_cache_clear_advanced', false );
253
    }
254
255
    /**
256
     * Returns a (hopefully) unique new cache folder name for renaming purposes.
257
     *
258
     * @return string
259
     */
260
    protected static function get_unique_name()
261
    {
262
        $prefix   = self::get_advanced_cache_clear_prefix();
263
        $new_name = uniqid( $prefix, true );
264
265
        return $new_name;
266
    }
267
268
    /**
269
     * Get cache prefix name used in advanced cache clearing mode.
270
     *
271
     * @return string
272
     */
273
    protected static function get_advanced_cache_clear_prefix()
274
    {
275
        $pathname = self::get_pathname_base();
276
        $basename = basename( $pathname );
277
        $prefix   = $basename . '-artifact-';
278
279
        return $prefix;
280
    }
281
282
    /**
283
     * Returns an array of file and directory names found within
284
     * the given $pathname without '.' and '..' elements.
285
     *
286
     * @param string $pathname Pathname.
287
     *
288
     * @return array
289
     */
290
    protected static function get_dir_contents( $pathname )
291
    {
292
        return array_slice( scandir( $pathname ), 2 );
293
    }
294
295
    /**
296
     * Wipes directories which were created as part of the fast cache clearing
297
     * routine (which renames the current cache directory into a new one with
298
     * a custom-prefixed unique name).
299
     *
300
     * @return bool
301
     */
302
    public static function delete_advanced_cache_clear_artifacts()
303
    {
304
        // Don't go through these motions (called from the cachechecker) if advanced cache clear isn't even active.
305
        if ( ! self::advanced_cache_clear_enabled() ) {
306
            return false;
307
        }
308
309
        $dir    = self::get_pathname_base();
310
        $prefix = self::get_advanced_cache_clear_prefix();
311
        $parent = dirname( $dir );
312
        $ok     = false;
313
314
        // Returns the list of files without '.' and '..' elements.
315
        $files = self::get_dir_contents( $parent );
316
        if ( is_array( $files ) && ! empty( $files ) ) {
317
            foreach ( $files as $file ) {
318
                $path     = $parent . '/' . $file;
319
                $prefixed = ( false !== strpos( $path, $prefix ) );
320
                // Removing only our own (prefixed) directories...
321
                if ( is_dir( $path ) && $prefixed ) {
322
                    $ok = self::rmdir( $path );
323
                }
324
            }
325
        }
326
327
        return $ok;
328
    }
329
330
    /**
331
     * Returns the cache directory pathname used.
332
     * Done as a function so we canSlightly different
333
     * if multisite is used and `autoptimize_separate_blog_caches` filter
334
     * is used.
335
     *
336
     * @return string
337
     */
338
    public static function get_pathname()
339
    {
340
        $pathname = self::get_pathname_base();
341
342 View Code Duplication
        if ( is_multisite() && apply_filters( 'autoptimize_separate_blog_caches', true ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
343
            $blog_id   = get_current_blog_id();
344
            $pathname .= $blog_id . '/';
345
        }
346
347
        return $pathname;
348
    }
349
350
    /**
351
     * Returns the base path of our cache directory.
352
     *
353
     * @return string
354
     */
355
    protected static function get_pathname_base()
356
    {
357
        $pathname = WP_CONTENT_DIR . AUTOPTIMIZE_CACHE_CHILD_DIR;
358
359
        return $pathname;
360
    }
361
362
    /**
363
     * Deletes everything from the cache directories.
364
     *
365
     * @param bool $propagate Whether to trigger additional actions when cache is purged.
366
     *
367
     * @return bool
368
     */
369
    public static function clearall( $propagate = true )
370
    {
371
        if ( ! self::cacheavail() ) {
372
            return false;
373
        }
374
375
        // TODO/FIXME: If cache is big, switch to advanced/new cache clearing automatically?
376
        if ( self::advanced_cache_clear_enabled() ) {
377
            self::clear_cache_via_rename();
378
        } else {
379
            self::clear_cache_classic();
380
        }
381
382
        // Remove 404 handler if required.
383
        if ( self::do_fallback() ) {
384
            $_fallback_php = trailingslashit( WP_CONTENT_DIR ) . 'autoptimize_404_handler.php';
385
            @unlink( $_fallback_php ); // @codingStandardsIgnoreLine
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
386
        }
387
388
        // Remove the transient so it gets regenerated...
389
        delete_transient( 'autoptimize_stats' );
390
391
        // Cache was just purged, clear page cache and allow others to hook into our purging...
392
        if ( true === $propagate ) {
393
            if ( ! function_exists( 'autoptimize_do_cachepurged_action' ) ) {
394
                function autoptimize_do_cachepurged_action() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
395
                    do_action( 'autoptimize_action_cachepurged' );
396
                }
397
            }
398
            add_action( 'shutdown', 'autoptimize_do_cachepurged_action', 11 );
399
            add_action( 'autoptimize_action_cachepurged', array( 'autoptimizeCache', 'flushPageCache' ), 10, 0 );
400
        }
401
402
        // Warm cache (part of speedupper)!
403
        if ( apply_filters( 'autoptimize_filter_speedupper', true ) && false == get_transient( 'autoptimize_cache_warmer_protector' ) ) {
404
            set_transient( 'autoptimize_cache_warmer_protector', 'I shall not warm cache for another 10 minutes.', 60 * 10 );
405
            $url   = site_url() . '/?ao_speedup_cachebuster=' . rand( 1, 100000 );
406
            $url   = apply_filters( 'autoptimize_filter_cache_warmer_url', $url );
407
            $cache = @wp_remote_get( $url ); // @codingStandardsIgnoreLine
408
            unset( $cache );
409
        }
410
411
        return true;
412
    }
413
414
    /**
415
     * Wrapper for clearall but with false param
416
     * to ensure the event is not propagated to others
417
     * through our own hooks (to avoid infinite loops).
418
     *
419
     * @return bool
420
     */
421
    public static function clearall_actionless()
422
    {
423
        return self::clearall( false );
424
    }
425
426
    /**
427
     * Returns the contents of our cache dirs.
428
     *
429
     * @return array
430
     */
431
    protected static function get_cache_contents()
432
    {
433
        $contents = array();
434
435
        foreach ( array( '', 'js', 'css' ) as $dir ) {
436
            $contents[ $dir ] = scandir( AUTOPTIMIZE_CACHE_DIR . $dir );
437
        }
438
439
        return $contents;
440
    }
441
442
    /**
443
     * Returns stats about cached contents.
444
     *
445
     * @return array
446
     */
447
    public static function stats()
448
    {
449
        $stats = get_transient( 'autoptimize_stats' );
450
451
        // If no transient, do the actual scan!
452
        if ( ! is_array( $stats ) ) {
453
            if ( ! self::cacheavail() ) {
454
                return 0;
455
            }
456
            $stats = self::stats_scan();
457
            $count = $stats[0];
458
            if ( $count > 100 ) {
459
                // Store results in transient.
460
                set_transient(
461
                    'autoptimize_stats',
462
                    $stats,
463
                    apply_filters( 'autoptimize_filter_cache_statsexpiry', HOUR_IN_SECONDS )
464
                );
465
            }
466
        }
467
468
        return $stats;
469
    }
470
471
    /**
472
     * Performs a scan of cache directory contents and returns an array
473
     * with 3 values: count, size, timestamp.
474
     * count = total number of found files
475
     * size = total filesize (in bytes) of found files
476
     * timestamp = unix timestamp when the scan was last performed/finished.
477
     *
478
     * @return array
479
     */
480
    protected static function stats_scan()
481
    {
482
        $count = 0;
483
        $size  = 0;
484
485
        // Scan everything in our cache directories.
486
        foreach ( self::get_cache_contents() as $name => $files ) {
487
            $dir = rtrim( AUTOPTIMIZE_CACHE_DIR . $name, '/' ) . '/';
488
            foreach ( $files as $file ) {
489
                if ( self::is_valid_cache_file( $dir, $file ) ) {
490
                    if ( AUTOPTIMIZE_CACHE_NOGZIP &&
491
                        (
492
                            false !== strpos( $file, '.js' ) ||
493
                            false !== strpos( $file, '.css' ) ||
494
                            false !== strpos( $file, '.img' ) ||
495
                            false !== strpos( $file, '.txt' )
496
                        )
497
                    ) {
498
                        // Web server is gzipping, we count .js|.css|.img|.txt files.
499
                        $count++;
500
                    } elseif ( ! AUTOPTIMIZE_CACHE_NOGZIP && false !== strpos( $file, '.none' ) ) {
501
                        // We are gzipping ourselves via php, counting only .none files.
502
                        $count++;
503
                    }
504
                    $size += filesize( $dir . $file );
505
                }
506
            }
507
        }
508
509
        $stats = array( $count, $size, time() );
510
511
        return $stats;
512
    }
513
514
    /**
515
     * Ensures the cache directory exists, is writeable and contains the
516
     * required .htaccess files.
517
     * Returns false in case it fails to ensure any of those things.
518
     *
519
     * @return bool
520
     */
521
    public static function cacheavail()
522
    {
523
        if ( false === autoptimizeCache::check_and_create_dirs() ) {
524
            return false;
525
        }
526
527
        // Using .htaccess inside our cache folder to overrule wp-super-cache.
528
        $htaccess = AUTOPTIMIZE_CACHE_DIR . '/.htaccess';
529
        if ( ! is_file( $htaccess ) ) {
530
            /**
531
             * Create `wp-content/AO_htaccess_tmpl` file with
532
             * whatever htaccess rules you might need
533
             * if you want to override default AO htaccess
534
             */
535
            $htaccess_tmpl = WP_CONTENT_DIR . '/AO_htaccess_tmpl';
536
            if ( is_file( $htaccess_tmpl ) ) {
537
                $content = file_get_contents( $htaccess_tmpl );
538
            } elseif ( is_multisite() || ! AUTOPTIMIZE_CACHE_NOGZIP ) {
539
                $content = '<IfModule mod_expires.c>
540
        ExpiresActive On
541
        ExpiresByType text/css A30672000
542
        ExpiresByType text/javascript A30672000
543
        ExpiresByType application/javascript A30672000
544
</IfModule>
545
<IfModule mod_headers.c>
546
    Header append Cache-Control "public, immutable"
547
</IfModule>
548
<IfModule mod_deflate.c>
549
        <FilesMatch "\.(js|css)$">
550
        SetOutputFilter DEFLATE
551
    </FilesMatch>
552
</IfModule>
553
<IfModule mod_authz_core.c>
554
    <Files *.php>
555
        Require all granted
556
    </Files>
557
</IfModule>
558
<IfModule !mod_authz_core.c>
559
    <Files *.php>
560
        Order allow,deny
561
        Allow from all
562
    </Files>
563
</IfModule>';
564
            } else {
565
                $content = '<IfModule mod_expires.c>
566
        ExpiresActive On
567
        ExpiresByType text/css A30672000
568
        ExpiresByType text/javascript A30672000
569
        ExpiresByType application/javascript A30672000
570
</IfModule>
571
<IfModule mod_headers.c>
572
    Header append Cache-Control "public, immutable"
573
</IfModule>
574
<IfModule mod_deflate.c>
575
    <FilesMatch "\.(js|css)$">
576
        SetOutputFilter DEFLATE
577
    </FilesMatch>
578
</IfModule>
579
<IfModule mod_authz_core.c>
580
    <Files *.php>
581
        Require all denied
582
    </Files>
583
</IfModule>
584
<IfModule !mod_authz_core.c>
585
    <Files *.php>
586
        Order deny,allow
587
        Deny from all
588
    </Files>
589
</IfModule>';
590
            }
591
592
            if ( self::do_fallback() ) {
593
                $content .= "\nErrorDocument 404 " . trailingslashit( parse_url( content_url(), PHP_URL_PATH ) ) . 'autoptimize_404_handler.php';
594
            }
595
            @file_put_contents( $htaccess, $content ); // @codingStandardsIgnoreLine
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
596
        }
597
598
        if ( self::do_fallback() ) {
599
            self::check_fallback_php();
600
        }
601
602
        // All OK!
603
        return true;
604
    }
605
606
    /**
607
     * Checks if fallback-php file exists and create it if not.
608
     *
609
     * Return bool
610
     */
611
    public static function check_fallback_php() {
612
        $_fallback_filename = 'autoptimize_404_handler.php';
613
        $_fallback_php      = trailingslashit( WP_CONTENT_DIR ) . $_fallback_filename;
614
        $_fallback_status   = true;
615
616
        if ( ! file_exists( $_fallback_php ) ) {
617
            $_fallback_php_contents = file_get_contents( AUTOPTIMIZE_PLUGIN_DIR . 'config/' . $_fallback_filename );
618
            $_fallback_php_contents = str_replace( '<?php exit;', '<?php', $_fallback_php_contents );
619
            $_fallback_php_contents = str_replace( '<!--ao-cache-dir-->', AUTOPTIMIZE_CACHE_DIR, $_fallback_php_contents );
620
            if ( apply_filters( 'autoptimize_filter_cache_fallback_log_errors', false ) ) {
621
                $_fallback_php_contents = str_replace( '// error_log', 'error_log', $_fallback_php_contents );
622
            }
623
            $_fallback_status = file_put_contents( $_fallback_php, $_fallback_php_contents );
624
        }
625
626
        return $_fallback_status;
627
    }
628
629
    /**
630
     * Tells if AO should try to avoid 404's by creating fallback filesize
631
     * and create a php 404 handler and tell .htaccess to redirect to said handler.
632
     *
633
     * Return bool
634
     */
635
    public static function do_fallback() {
636
        return apply_filters( 'autoptimize_filter_cache_do_fallback', false );
637
    }
638
639
    /**
640
     * Checks if cache dirs exist and create if not.
641
     * Returns false if not succesful.
642
     *
643
     * @return bool
644
     */
645
    public static function check_and_create_dirs() {
646
        if ( ! defined( 'AUTOPTIMIZE_CACHE_DIR' ) ) {
647
            // We didn't set a cache.
648
            return false;
649
        }
650
651
        foreach ( array( '', 'js', 'css' ) as $dir ) {
652
            if ( ! self::check_cache_dir( AUTOPTIMIZE_CACHE_DIR . $dir ) ) {
653
                return false;
654
            }
655
        }
656
        return true;
657
    }
658
659
    /**
660
     * Ensures the specified `$dir` exists and is writeable.
661
     * Returns false if that's not the case.
662
     *
663
     * @param string $dir Directory to check/create.
664
     *
665
     * @return bool
666
     */
667
    protected static function check_cache_dir( $dir )
668
    {
669
        // Try creating the dir if it doesn't exist.
670
        if ( ! file_exists( $dir ) ) {
671
            @mkdir( $dir, 0775, true ); // @codingStandardsIgnoreLine
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
672
            if ( ! file_exists( $dir ) ) {
673
                return false;
674
            }
675
        }
676
677
        // If we still cannot write, bail.
678
        if ( ! is_writable( $dir ) ) {
679
            return false;
680
        }
681
682
        // Create an index.html in there to avoid prying eyes!
683
        $idx_file = rtrim( $dir, '/\\' ) . '/index.html';
684
        if ( ! is_file( $idx_file ) ) {
685
            @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
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
686
        }
687
688
        return true;
689
    }
690
691
    /**
692
     * Flushes as many page cache plugin's caches as possible.
693
     *
694
     * @return void
695
     */
696
    // @codingStandardsIgnoreStart
697
    public static function flushPageCache()
698
    {
699
        if ( function_exists( 'wp_cache_clear_cache' ) ) {
700
            if ( is_multisite() ) {
701
                $blog_id = get_current_blog_id();
702
                wp_cache_clear_cache( $blog_id );
703
            } else {
704
                wp_cache_clear_cache();
705
            }
706
        } elseif ( has_action( 'cachify_flush_cache' ) ) {
707
            do_action( 'cachify_flush_cache' );
708
        } elseif ( function_exists( 'w3tc_pgcache_flush' ) ) {
709
            w3tc_pgcache_flush();
710
        } elseif ( function_exists( 'wp_fast_cache_bulk_delete_all' ) ) {
711
            wp_fast_cache_bulk_delete_all();
712
        } elseif ( class_exists( 'WpFastestCache' ) ) {
713
            $wpfc = new WpFastestCache();
714
            $wpfc->deleteCache();
715
        } elseif ( class_exists( 'c_ws_plugin__qcache_purging_routines' ) ) {
716
            c_ws_plugin__qcache_purging_routines::purge_cache_dir(); // quick cache
717
        } elseif ( class_exists( 'zencache' ) ) {
718
            zencache::clear();
719
        } elseif ( class_exists( 'comet_cache' ) ) {
720
            comet_cache::clear();
721
        } elseif ( class_exists( 'WpeCommon' ) ) {
722
            // WPEngine cache purge/flush methods to call by default
723
            $wpe_methods = array(
724
                'purge_varnish_cache',
725
            );
726
727
            // More agressive clear/flush/purge behind a filter
728
            if ( apply_filters( 'autoptimize_flush_wpengine_aggressive', false ) ) {
729
                $wpe_methods = array_merge( $wpe_methods, array( 'purge_memcached', 'clear_maxcdn_cache' ) );
730
            }
731
732
            // Filtering the entire list of WpeCommon methods to be called (for advanced usage + easier testing)
733
            $wpe_methods = apply_filters( 'autoptimize_flush_wpengine_methods', $wpe_methods );
734
735
            foreach ( $wpe_methods as $wpe_method ) {
736
                if ( method_exists( 'WpeCommon', $wpe_method ) ) {
737
                    WpeCommon::$wpe_method();
738
                }
739
            }
740
        } elseif ( function_exists( 'sg_cachepress_purge_cache' ) ) {
741
            sg_cachepress_purge_cache();
742
        } elseif ( array_key_exists( 'KINSTA_CACHE_ZONE', $_SERVER ) ) {
743
            $_kinsta_clear_cache_url = 'https://localhost/kinsta-clear-cache-all';
744
            $_kinsta_response        = wp_remote_get(
0 ignored issues
show
Unused Code introduced by
$_kinsta_response is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
745
                $_kinsta_clear_cache_url,
746
                array( 
747
                    'sslverify' => false,
748
                    'timeout' => 5,
749
                    )
750
            );
751
        } elseif ( defined('NGINX_HELPER_BASENAME') ) {
752
            do_action( 'rt_nginx_helper_purge_all' );
753
        } elseif ( file_exists( WP_CONTENT_DIR . '/wp-cache-config.php' ) && function_exists( 'prune_super_cache' ) ) {
754
            // fallback for WP-Super-Cache
755
            global $cache_path;
756
            if ( is_multisite() ) {
757
                $blog_id = get_current_blog_id();
758
                prune_super_cache( get_supercache_dir( $blog_id ), true );
759
                prune_super_cache( $cache_path . 'blogs/', true );
760
            } else {
761
                prune_super_cache( $cache_path . 'supercache/', true );
762
                prune_super_cache( $cache_path, true );
763
            }
764
        }
765
    }
766
    // @codingStandardsIgnoreEnd
767
}
768