Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
CloudflareFeaturesManager
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 5
240
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 on_image_optimization_change
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 on_fonts_optimization_change
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 on_site_capabilities_change
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 update_htaccess_header
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 1
110
1<?php
2
3namespace NewfoldLabs\WP\Module\Performance\Cloudflare;
4
5use NewfoldLabs\WP\Module\Performance\Fonts\FontSettings;
6use NewfoldLabs\WP\Module\Performance\Images\ImageSettings;
7use WP_Forge\WP_Htaccess_Manager\htaccess;
8
9/**
10 * Handles detection and tracking of Cloudflare Polish, Mirage, and Font Optimization.
11 */
12class CloudflareFeaturesManager {
13
14    private const MARKER = 'Newfold CF Optimization Header';
15
16    /**
17     * Constructor to register hooks for settings changes.
18     */
19    public function __construct() {
20        add_action( 'update_option_nfd_image_optimization', array( $this, 'on_image_optimization_change' ), 10, 2 );
21        add_action( 'add_option_nfd_image_optimization', array( $this, 'on_image_optimization_change' ), 10, 2 );
22        add_action( 'update_option_nfd_fonts_optimization', array( $this, 'on_fonts_optimization_change' ), 10, 2 );
23        add_action( 'add_option_nfd_fonts_optimization', array( $this, 'on_fonts_optimization_change' ), 10, 2 );
24        add_action( 'set_transient_nfd_site_capabilities', array( $this, 'on_site_capabilities_change' ), 10, 2 );
25    }
26
27    /**
28     * Handles image optimization setting changes.
29     *
30     * @param array $old_value Previous value.
31     * @param array $new_value New value.
32     */
33    public function on_image_optimization_change( $old_value, $new_value ) {
34        $this->update_htaccess_header( $new_value, get_option( 'nfd_fonts_optimization', false ) );
35    }
36
37    /**
38     * Handles font optimization setting changes.
39     *
40     * @param mixed $old_value Previous value.
41     * @param mixed $new_value New value.
42     */
43    public function on_fonts_optimization_change( $old_value, $new_value ) {
44        $this->update_htaccess_header( get_option( 'nfd_image_optimization', array() ), $new_value );
45    }
46
47    /**
48     * Callback for when the `nfd_site_capabilities` transient is set.
49     *
50     * Triggers a refresh of image and font optimization settings based on updated site capabilities.
51     *
52     * @param mixed $value      The value being set in the transient.
53     * @param int   $expiration The expiration time in seconds.
54     */
55    public function on_site_capabilities_change( $value, $expiration ) {
56        if ( is_array( $value ) ) {
57            ImageSettings::maybe_refresh_with_capabilities( $value );
58            FontSettings::maybe_refresh_with_capabilities( $value );
59        }
60    }
61
62    /**
63     * Updates the .htaccess header based on current optimization settings.
64     *
65     * @param array $image_settings Array of image optimization settings.
66     * @param mixed $fonts_enabled  Whether font optimization is enabled.
67     */
68    private function update_htaccess_header( $image_settings, $fonts_enabled ) {
69        $images_cloudflare = isset( $image_settings['cloudflare'] ) ? $image_settings['cloudflare'] : array();
70        $fonts_cloudflare  = isset( $fonts_enabled['cloudflare'] ) ? $fonts_enabled['cloudflare'] : array();
71
72        $mirage_enabled     = ! empty( $images_cloudflare['mirage']['value'] );
73        $polish_enabled     = ! empty( $images_cloudflare['polish']['value'] );
74        $fonts_enabled_flag = ! empty( $fonts_cloudflare['fonts']['value'] );
75
76        $mirage_hash = $mirage_enabled ? substr( sha1( 'mirage' ), 0, 8 ) : '';
77        $polish_hash = $polish_enabled ? substr( sha1( 'polish' ), 0, 8 ) : '';
78        $fonts_hash  = $fonts_enabled_flag ? substr( sha1( 'fonts' ), 0, 8 ) : '';
79
80        $header_value = "{$mirage_hash}{$polish_hash}{$fonts_hash}";
81        $rules        = array();
82
83        if ( $mirage_enabled || $polish_enabled || $fonts_enabled_flag ) {
84            $rules = array(
85                '<IfModule mod_rewrite.c>',
86                "\tRewriteEngine On",
87                "\t# Skip setting for admin/API routes",
88                "\tRewriteCond %{REQUEST_URI} !/wp-admin/       [NC]",
89                "\tRewriteCond %{REQUEST_URI} !/wp-login\\.php   [NC]",
90                "\tRewriteCond %{REQUEST_URI} !/wp-json/        [NC]",
91                "\tRewriteCond %{REQUEST_URI} !/xmlrpc\\.php     [NC]",
92                "\tRewriteCond %{REQUEST_URI} !/admin-ajax\\.php [NC]",
93                "\t# Skip if the exact cookie and value are already present",
94                "\tRewriteCond %{HTTP_COOKIE} !(^|;\\s*)nfd-enable-cf-opt={$header_value} [NC]",
95                "\t# Set env var if we passed all conditions",
96                "\tRewriteRule .* - [E=CF_OPT:1]",
97                '</IfModule>',
98                '<IfModule mod_headers.c>',
99                "\t# Set cookie only if env var is present (i.e., exact cookie not found)",
100                "\tHeader set Set-Cookie \"nfd-enable-cf-opt={$header_value}; path=/; Max-Age=86400; HttpOnly\" env=CF_OPT",
101                '</IfModule>',
102            );
103        }
104
105        $htaccess = new htaccess( self::MARKER );
106        if ( empty( $rules ) ) {
107            $htaccess->removeContent();
108        } else {
109            $htaccess->addContent( $rules );
110        }
111    }
112}