Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 74
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
Browser
0.00% covered (danger)
0.00%
0 / 74
0.00% covered (danger)
0.00%
0 / 10
420
0.00% covered (danger)
0.00%
0 / 1
 should_enable
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 on_rewrite
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 exclusionChange
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 maybeAddRules
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 removeRules
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 addRules
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
30
 getFileTypeExpirations
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
30
 on_activation
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 on_deactivation
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace NewfoldLabs\WP\Module\Performance\Cache\Types;
4
5use NewfoldLabs\WP\Module\Performance\OptionListener;
6use NewfoldLabs\WP\ModuleLoader\Container;
7use WP_Forge\WP_Htaccess_Manager\htaccess;
8use NewfoldLabs\WP\Module\Performance\Cache\CacheExclusion;
9use NewfoldLabs\WP\Module\Performance\Cache\CacheManager;
10
11use function NewfoldLabs\WP\Module\Performance\get_cache_level;
12use function WP_Forge\WP_Htaccess_Manager\removeMarkers;
13
14/**
15 * Browser cache type.
16 */
17class Browser extends CacheBase {
18    /**
19     * The file marker name.
20     *
21     * @var string
22     */
23    const MARKER = 'Newfold Browser Cache';
24
25    /**
26     * Whether or not the code for this cache type should be loaded.
27     *
28     * @param Container $container Dependency injection container.
29     *
30     * @return bool
31     */
32    public static function should_enable( Container $container ) {
33        return (bool) $container->has( 'isApache' ) && $container->get( 'isApache' );
34    }
35
36    /**
37     * Constructor.
38     */
39    public function __construct() {
40
41        new OptionListener( CacheManager::OPTION_CACHE_LEVEL, array( __CLASS__, 'maybeAddRules' ) );
42
43        new OptionListener( CacheExclusion::OPTION_CACHE_EXCLUSION, array( __CLASS__, 'exclusionChange' ) );
44
45        add_filter( 'newfold_update_htaccess', array( $this, 'on_rewrite' ) );
46    }
47
48    /**
49     * When updating .htaccess, also update our rules as appropriate.
50     */
51    public function on_rewrite() {
52        self::maybeAddRules( get_cache_level() );
53    }
54
55    /**
56     * Manage on exlcusion option change.
57     */
58    public static function exclusionChange() {
59        self::maybeAddRules( get_cache_level() );
60    }
61
62    /**
63     * Determine whether to add or remove rules based on caching level.
64     *
65     * @param int|null $cacheLevel The caching level.
66     */
67    public static function maybeAddRules( $cacheLevel ) {
68        absint( $cacheLevel ) > 0 ? self::addRules( $cacheLevel ) : self::removeRules();
69    }
70
71    /**
72     * Remove our rules from the .htaccess file.
73     */
74    public static function removeRules() {
75        removeMarkers( self::MARKER );
76    }
77
78    /**
79     * Add our rules to the .htaccess file.
80     *
81     * @param int $cacheLevel The caching level.
82     *
83     * @return bool
84     */
85    public static function addRules( $cacheLevel ) {
86
87        $fileTypeExpirations = self::getFileTypeExpirations( $cacheLevel );
88
89        $tab = "\t";
90
91        $rules[] = '<IfModule mod_expires.c>';
92        $rules[] = "{$tab}ExpiresActive On";
93
94        foreach ( $fileTypeExpirations as $file_type => $expiration ) {
95            if ( 'default' === $file_type ) {
96                $rules[] = "{$tab}ExpiresDefault \"access plus {$expiration}\"";
97            } else {
98                $rules[] = "{$tab}ExpiresByType {$file_type} \"access plus {$expiration}\"";
99            }
100        }
101        $rules[] = '</IfModule>';
102
103        $cache_exclusion = get_option( CacheExclusion::OPTION_CACHE_EXCLUSION, '' );
104        if ( is_string( $cache_exclusion ) && '' !== $cache_exclusion ) {
105            $cache_exclusion_parameters = array_map( 'trim', explode( ',', sanitize_text_field( get_option( CacheExclusion::OPTION_CACHE_EXCLUSION, '' ) ) ) );
106            $cache_exclusion_parameters = implode( '|', $cache_exclusion_parameters );
107
108            // Add the cache exclusion rules.
109            $rules[] = '<IfModule mod_rewrite.c>';
110            $rules[] = 'RewriteEngine On';
111            $rules[] = "RewriteCond %{REQUEST_URI} ^/({$cache_exclusion_parameters}) [NC]";
112            $rules[] = '<IfModule mod_headers.c>';
113            $rules[] = 'Header set Cache-Control "no-cache, no-store, must-revalidate"';
114            $rules[] = 'Header set Pragma "no-cache"';
115            $rules[] = 'Header set Expires 0';
116            $rules[] = '</IfModule>';
117            $rules[] = '</IfModule>';
118            // Add the end of the rules about cache exclusion.
119        }
120
121        $htaccess = new htaccess( self::MARKER );
122
123        return $htaccess->addContent( $rules );
124    }
125
126    /**
127     * Get the filetype expirations based on the current caching level.
128     *
129     * @param int $cacheLevel The caching level.
130     *
131     * @return string[]
132     */
133    protected static function getFileTypeExpirations( int $cacheLevel ) {
134
135        switch ( $cacheLevel ) {
136            case 3:
137                return array(
138                    'default'         => '1 week',
139                    'text/html'       => '8 hours',
140                    'image/jpg'       => '1 week',
141                    'image/jpeg'      => '1 week',
142                    'image/gif'       => '1 week',
143                    'image/png'       => '1 week',
144                    'text/css'        => '1 week',
145                    'text/javascript' => '1 week',
146                    'application/pdf' => '1 month',
147                    'image/x-icon'    => '1 year',
148                );
149
150            case 2:
151                return array(
152                    'default'         => '24 hours',
153                    'text/html'       => '2 hours',
154                    'image/jpg'       => '24 hours',
155                    'image/jpeg'      => '24 hours',
156                    'image/gif'       => '24 hours',
157                    'image/png'       => '24 hours',
158                    'text/css'        => '24 hours',
159                    'text/javascript' => '24 hours',
160                    'application/pdf' => '1 week',
161                    'image/x-icon'    => '1 year',
162                );
163
164            case 1:
165                return array(
166                    'default'         => '5 minutes',
167                    'text/html'       => '0 seconds',
168                    'image/jpg'       => '1 hour',
169                    'image/jpeg'      => '1 hour',
170                    'image/gif'       => '1 hour',
171                    'image/png'       => '1 hour',
172                    'text/css'        => '1 hour',
173                    'text/javascript' => '1 hour',
174                    'application/pdf' => '6 hours',
175                    'image/x-icon'    => '1 year',
176                );
177
178            default:
179                return array();
180        }
181    }
182
183    /**
184     * Handle activation logic.
185     */
186    public static function on_activation() {
187        self::maybeAddRules( get_cache_level() );
188    }
189
190    /**
191     * Handle deactivation logic.
192     */
193    public static function on_deactivation() {
194        self::removeRules();
195    }
196}