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