Completed
Push — master ( 5da188...964b0c )
by frank
01:47
created

autoptimizeCache::flushPageCache()   D

Complexity

Conditions 21
Paths 22

Size

Total Lines 71

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 21
nc 22
nop 0
dl 0
loc 71
rs 4.1666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 ) {
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...
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 ( ( 'css' === $_extension || 'js' === $_extension ) && ! 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
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...
194
                }
195
            }
196
        }
197
198
        @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...
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 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...
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
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...
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() {
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...
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
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...
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
            // prepare for Shakeeb's Unused CSS files to be 404-handled as well.
673
            if ( strpos( $original_request, 'uucss/uucss-' ) !== false ) {
674
                $original_request = preg_replace( '/uucss\/uucss-[a-z0-9]{32}-/', 'css/', $original_request  );
675
            }
676
677
            // set fallback URL.
678
            $fallback_target = preg_replace( '/(.*)_(?:[a-z0-9]{32})\.(js|css)$/', '${1}_fallback.${2}', $original_request );
679
680
            // redirect to fallback if possible.
681
            if ( $original_request !== $fallback_target && file_exists( $fallback_path ) ) {
682
                // redirect to fallback.
683
                wp_redirect( $fallback_target, 302 );
684
            } else {
685
                // return HTTP 410 (gone) reponse.
686
                status_header( 410 );
687
            }
688
        }
689
    }
690
691
    /**
692
     * Checks if cache dirs exist and create if not.
693
     * Returns false if not succesful.
694
     *
695
     * @return bool
696
     */
697
    public static function check_and_create_dirs() {
698
        if ( ! defined( 'AUTOPTIMIZE_CACHE_DIR' ) ) {
699
            // We didn't set a cache.
700
            return false;
701
        }
702
703
        foreach ( array( '', 'js', 'css' ) as $dir ) {
704
            if ( ! self::check_cache_dir( AUTOPTIMIZE_CACHE_DIR . $dir ) ) {
705
                return false;
706
            }
707
        }
708
        return true;
709
    }
710
711
    /**
712
     * Ensures the specified `$dir` exists and is writeable.
713
     * Returns false if that's not the case.
714
     *
715
     * @param string $dir Directory to check/create.
716
     *
717
     * @return bool
718
     */
719
    protected static function check_cache_dir( $dir )
720
    {
721
        // Try creating the dir if it doesn't exist.
722
        if ( ! file_exists( $dir ) ) {
723
            @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...
724
            if ( ! file_exists( $dir ) ) {
725
                return false;
726
            }
727
        }
728
729
        // If we still cannot write, bail.
730
        if ( ! is_writable( $dir ) ) {
731
            return false;
732
        }
733
734
        // Create an index.html in there to avoid prying eyes!
735
        $idx_file = rtrim( $dir, '/\\' ) . '/index.html';
736
        if ( ! is_file( $idx_file ) ) {
737
            @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...
738
        }
739
740
        return true;
741
    }
742
743
    /**
744
     * Flushes as many page cache plugin's caches as possible.
745
     *
746
     * @return void
747
     */
748
    // @codingStandardsIgnoreStart
749
    public static function flushPageCache()
750
    {
751
        if ( function_exists( 'wp_cache_clear_cache' ) ) {
752
            if ( is_multisite() ) {
753
                $blog_id = get_current_blog_id();
754
                wp_cache_clear_cache( $blog_id );
755
            } else {
756
                wp_cache_clear_cache();
757
            }
758
        } elseif ( has_action( 'cachify_flush_cache' ) ) {
759
            do_action( 'cachify_flush_cache' );
760
        } elseif ( function_exists( 'w3tc_pgcache_flush' ) ) {
761
            w3tc_pgcache_flush();
762
        } elseif ( function_exists( 'wp_fast_cache_bulk_delete_all' ) ) {
763
            wp_fast_cache_bulk_delete_all();
764
        } elseif ( class_exists( 'Swift_Performance_Cache' ) ) {
765
            Swift_Performance_Cache::clear_all_cache();
766
        } elseif ( class_exists( 'WpFastestCache' ) ) {
767
            $wpfc = new WpFastestCache();
768
            $wpfc->deleteCache();
769
        } elseif ( class_exists( 'c_ws_plugin__qcache_purging_routines' ) ) {
770
            c_ws_plugin__qcache_purging_routines::purge_cache_dir(); // quick cache
771
        } elseif ( class_exists( 'zencache' ) ) {
772
            zencache::clear();
773
        } elseif ( class_exists( 'comet_cache' ) ) {
774
            comet_cache::clear();
775
        } elseif ( class_exists( 'WpeCommon' ) ) {
776
            // WPEngine cache purge/flush methods to call by default
777
            $wpe_methods = array(
778
                'purge_varnish_cache',
779
            );
780
781
            // More agressive clear/flush/purge behind a filter
782
            if ( apply_filters( 'autoptimize_flush_wpengine_aggressive', false ) ) {
783
                $wpe_methods = array_merge( $wpe_methods, array( 'purge_memcached', 'clear_maxcdn_cache' ) );
784
            }
785
786
            // Filtering the entire list of WpeCommon methods to be called (for advanced usage + easier testing)
787
            $wpe_methods = apply_filters( 'autoptimize_flush_wpengine_methods', $wpe_methods );
788
789
            foreach ( $wpe_methods as $wpe_method ) {
790
                if ( method_exists( 'WpeCommon', $wpe_method ) ) {
791
                    WpeCommon::$wpe_method();
792
                }
793
            }
794
        } elseif ( function_exists( 'sg_cachepress_purge_cache' ) ) {
795
            sg_cachepress_purge_cache();
796
        } elseif ( array_key_exists( 'KINSTA_CACHE_ZONE', $_SERVER ) ) {
797
            $_kinsta_clear_cache_url = 'https://localhost/kinsta-clear-cache-all';
798
            $_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...
799
                $_kinsta_clear_cache_url,
800
                array( 
801
                    'sslverify' => false,
802
                    'timeout' => 5,
803
                    )
804
            );
805
        } elseif ( defined('NGINX_HELPER_BASENAME') ) {
806
            do_action( 'rt_nginx_helper_purge_all' );
807
        } elseif ( file_exists( WP_CONTENT_DIR . '/wp-cache-config.php' ) && function_exists( 'prune_super_cache' ) ) {
808
            // fallback for WP-Super-Cache
809
            global $cache_path;
810
            if ( is_multisite() ) {
811
                $blog_id = get_current_blog_id();
812
                prune_super_cache( get_supercache_dir( $blog_id ), true );
813
                prune_super_cache( $cache_path . 'blogs/', true );
814
            } else {
815
                prune_super_cache( $cache_path . 'supercache/', true );
816
                prune_super_cache( $cache_path, true );
817
            }
818
        }
819
    }
820
    // @codingStandardsIgnoreEnd
821
}
822