Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 67
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 / 67
0.00% covered (danger)
0.00%
0 / 10
342
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 / 14
0.00% covered (danger)
0.00%
0 / 1
12
 getFileTypeExpirations
0.00% covered (danger)
0.00%
0 / 43
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 NewfoldLabs\WP\Module\Performance\Cache\CacheExclusion;
8use NewfoldLabs\WP\Module\Performance\Cache\CacheManager;
9use NewfoldLabs\WP\Module\Htaccess\Api as HtaccessApi;
10use NewfoldLabs\WP\Module\Performance\Cache\Types\Fragments\BrowserCacheFragment;
11
12use function NewfoldLabs\WP\Module\Performance\get_cache_exclusion;
13use function NewfoldLabs\WP\Module\Performance\get_cache_level;
14
15/**
16 * Browser cache type.
17 *
18 * Migrated to new Htaccess Fragment approach:
19 *   - Writes are performed by registering/unregistering a fragment.
20 *   - Content is rendered by the BrowserCacheFragment class.
21 *
22 * @package NewfoldLabs\WP\Module\Performance\Cache\Types
23 * @since 1.0.0
24 */
25class Browser extends CacheBase {
26
27    /**
28     * Human-friendly marker label used in BEGIN/END comments rendered
29     * by the fragment. Preserved for readability and parity.
30     *
31     * @var string
32     */
33    const MARKER = 'Newfold Browser Cache';
34
35    /**
36     * Registry identifier for this fragment.
37     * Must be globally unique across fragments.
38     *
39     * @var string
40     */
41    const FRAGMENT_ID = 'nfd.cache.browser';
42
43    /**
44     * Whether or not the code for this cache type should be loaded.
45     *
46     * @param Container $container Dependency injection container.
47     * @return bool
48     */
49    public static function should_enable( Container $container ) {
50        return (bool) $container->has( 'isApache' ) && $container->get( 'isApache' );
51    }
52
53    /**
54     * Constructor.
55     *
56     * Registers option listeners and filters that keep the fragment in sync
57     * with cache level and exclusion changes.
58     */
59    public function __construct() {
60        new OptionListener( CacheManager::OPTION_CACHE_LEVEL, array( __CLASS__, 'maybeAddRules' ) );
61        new OptionListener( CacheExclusion::OPTION_CACHE_EXCLUSION, array( __CLASS__, 'exclusionChange' ) );
62
63        add_filter( 'newfold_update_htaccess', array( $this, 'on_rewrite' ) );
64    }
65
66    /**
67     * When updating .htaccess, also update our rules as appropriate.
68     *
69     * @return void
70     */
71    public function on_rewrite() {
72        self::maybeAddRules( get_cache_level() );
73    }
74
75    /**
76     * Handle exclusion option change: refresh the fragment.
77     *
78     * @return void
79     */
80    public static function exclusionChange() {
81        self::maybeAddRules( get_cache_level() );
82    }
83
84    /**
85     * Determine whether to add or remove rules based on caching level.
86     *
87     * @param int|null $cache_level The caching level.
88     * @return void
89     */
90    public static function maybeAddRules( $cache_level ) {
91        absint( $cache_level ) > 0 ? self::addRules( $cache_level ) : self::removeRules();
92    }
93
94    /**
95     * Remove our rules by unregistering the fragment.
96     *
97     * @return void
98     */
99    public static function removeRules() {
100        HtaccessApi::unregister( self::FRAGMENT_ID );
101    }
102
103    /**
104     * Add (or replace) our rules by registering a fragment.
105     *
106     * @param int $cache_level The caching level (1–3).
107     * @return void
108     */
109    public static function addRules( $cache_level ) {
110
111        // Build exclusion pattern (same logic as before).
112        $exclusion_pattern = '';
113        $cache_exclusion   = get_cache_exclusion();
114
115        if ( is_string( $cache_exclusion ) && '' !== $cache_exclusion ) {
116            $parts             = array_map( 'trim', explode( ',', sanitize_text_field( $cache_exclusion ) ) );
117            $exclusion_pattern = implode( '|', array_filter( $parts ) );
118        }
119
120        // Register (or replace) a fragment with the current settings.
121        HtaccessApi::register(
122            new BrowserCacheFragment(
123                self::FRAGMENT_ID,
124                self::MARKER,
125                absint( $cache_level ),
126                $exclusion_pattern
127            ),
128            true // queue apply
129        );
130    }
131
132    /**
133     * Get the filetype expirations based on the current caching level.
134     *
135     * @param int $cache_level The caching level.
136     * @return array<string,string> Map of mime-type => TTL (human string).
137     */
138    public static function getFileTypeExpirations( int $cache_level ) {
139
140        switch ( $cache_level ) {
141            case 3:
142                return array(
143                    'default'         => '1 week',
144                    'text/html'       => '8 hours',
145                    'image/jpg'       => '1 week',
146                    'image/jpeg'      => '1 week',
147                    'image/gif'       => '1 week',
148                    'image/png'       => '1 week',
149                    'image/webp'      => '1 week',
150                    'text/css'        => '1 week',
151                    'text/javascript' => '1 week',
152                    'application/pdf' => '1 month',
153                    'image/x-icon'    => '1 year',
154                );
155
156            case 2:
157                return array(
158                    'default'         => '24 hours',
159                    'text/html'       => '2 hours',
160                    'image/jpg'       => '24 hours',
161                    'image/jpeg'      => '24 hours',
162                    'image/gif'       => '24 hours',
163                    'image/png'       => '24 hours',
164                    'image/webp'      => '24 hours',
165                    'text/css'        => '24 hours',
166                    'text/javascript' => '24 hours',
167                    'application/pdf' => '1 week',
168                    'image/x-icon'    => '1 year',
169                );
170
171            case 1:
172                return array(
173                    'default'         => '5 minutes',
174                    'text/html'       => '0 seconds',
175                    'image/jpg'       => '1 hour',
176                    'image/jpeg'      => '1 hour',
177                    'image/gif'       => '1 hour',
178                    'image/png'       => '1 hour',
179                    'image/webp'      => '1 hour',
180                    'text/css'        => '1 hour',
181                    'text/javascript' => '1 hour',
182                    'application/pdf' => '6 hours',
183                    'image/x-icon'    => '1 year',
184                );
185
186            default:
187                return array();
188        }
189    }
190
191    /**
192     * Handle activation logic.
193     *
194     * @return void
195     */
196    public static function on_activation() {
197        self::maybeAddRules( get_cache_level() );
198    }
199
200    /**
201     * Handle deactivation logic.
202     *
203     * @return void
204     */
205    public static function on_deactivation() {
206        self::removeRules();
207    }
208}