Skip to content

Commit 53a447e

Browse files
authored
Merge pull request #223 from newfold-labs/enhance/cloudflare-images-features
Add Mid Tier Hosting Plan Features
2 parents 1b4b4a7 + 97251d6 commit 53a447e

File tree

12 files changed

+844
-49
lines changed

12 files changed

+844
-49
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
3+
namespace NewfoldLabs\WP\Module\Performance\Cloudflare;
4+
5+
use NewfoldLabs\WP\Module\Performance\Fonts\FontSettings;
6+
use NewfoldLabs\WP\Module\Performance\Images\ImageSettings;
7+
use WP_Forge\WP_Htaccess_Manager\htaccess;
8+
9+
/**
10+
* Handles detection and tracking of Cloudflare Polish, Mirage, and Font Optimization.
11+
*/
12+
class 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# don’t flag any admin or API endpoints",
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# if we passed all those, set our CF_OPT flag",
94+
"\tRewriteRule .* - [E=CF_OPT:1]",
95+
'</IfModule>',
96+
'<IfModule mod_headers.c>',
97+
"\tHeader set Set-Cookie \"nfd-enable-cf-opt={$header_value}; path=/; Max-Age=86400; HttpOnly\" env=CF_OPT",
98+
'</IfModule>',
99+
);
100+
}
101+
102+
$htaccess = new htaccess( self::MARKER );
103+
if ( empty( $rules ) ) {
104+
$htaccess->removeContent();
105+
} else {
106+
$htaccess->addContent( $rules );
107+
}
108+
}
109+
}

includes/Fonts/FontManager.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace NewfoldLabs\WP\Module\Performance\Fonts;
4+
5+
use NewfoldLabs\WP\ModuleLoader\Container;
6+
use NewfoldLabs\WP\Module\Performance\Fonts\FontSettings;
7+
8+
/**
9+
* Manages the initialization of font optimization settings and listeners.
10+
*/
11+
class FontManager {
12+
13+
/**
14+
* Constructor to initialize the FontManager.
15+
*
16+
* Registers settings and conditionally initializes related services.
17+
*
18+
* @param Container $container Dependency injection container.
19+
*/
20+
public function __construct( Container $container ) {
21+
$this->initialize_settings( $container );
22+
}
23+
24+
/**
25+
* Initializes the FontSettings class to register settings.
26+
*
27+
* @param Container $container Dependency injection container.
28+
*/
29+
private function initialize_settings( Container $container ) {
30+
new FontSettings( $container );
31+
}
32+
}

includes/Fonts/FontSettings.php

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<?php
2+
3+
namespace NewfoldLabs\WP\Module\Performance\Fonts;
4+
5+
/**
6+
* Manages the registration and sanitization of font optimization settings.
7+
*/
8+
class FontSettings {
9+
10+
/**
11+
* The setting key for font optimization.
12+
*/
13+
private const SETTING_KEY = 'nfd_fonts_optimization';
14+
15+
/**
16+
* The default setting.
17+
*
18+
* @var array
19+
*/
20+
private $default_settings = array(
21+
'cloudflare' => array(
22+
'fonts' => array(
23+
'value' => false,
24+
'user_set' => false,
25+
),
26+
'last_updated' => 0,
27+
),
28+
);
29+
30+
/**
31+
* Constructor to initialize defaults and register setting.
32+
*
33+
* @param \NewfoldLabs\WP\Container\Container|null $container Optional DI container.
34+
*/
35+
public function __construct( $container = null ) {
36+
if ( $container && $container->has( 'capabilities' ) ) {
37+
$capabilities = $container->get( 'capabilities' );
38+
$this->default_settings['cloudflare']['fonts']['value'] = (bool) $capabilities->get( 'hasCloudflareFonts' );
39+
}
40+
41+
$this->default_settings['cloudflare']['last_updated'] = time();
42+
43+
$this->register_settings();
44+
$this->initialize_settings();
45+
}
46+
47+
/**
48+
* Registers the `nfd_fonts_optimization` setting.
49+
*/
50+
private function register_settings() {
51+
register_setting(
52+
'general',
53+
self::SETTING_KEY,
54+
array(
55+
'type' => 'object',
56+
'description' => __( 'Settings for NFD Font Optimization.', 'wp-module-performance' ),
57+
'sanitize_callback' => array( $this, 'sanitize_settings' ),
58+
'default' => $this->default_settings,
59+
'show_in_rest' => array(
60+
'schema' => array(
61+
'type' => 'object',
62+
'properties' => array(
63+
'cloudflare' => array(
64+
'type' => 'object',
65+
'description' => __( 'Cloudflare-related font optimization settings.', 'wp-module-performance' ),
66+
'properties' => array(
67+
'fonts' => array(
68+
'type' => 'object',
69+
'description' => __( 'Enable Cloudflare Font Optimization.', 'wp-module-performance' ),
70+
'properties' => array(
71+
'value' => array(
72+
'type' => 'boolean',
73+
'default' => $this->default_settings['cloudflare']['fonts']['value'],
74+
),
75+
'user_set' => array(
76+
'type' => 'boolean',
77+
'default' => $this->default_settings['cloudflare']['fonts']['user_set'],
78+
),
79+
),
80+
),
81+
'last_updated' => array(
82+
'type' => 'integer',
83+
'description' => __( 'Timestamp of last update.', 'wp-module-performance' ),
84+
'default' => $this->default_settings['cloudflare']['last_updated'],
85+
),
86+
),
87+
),
88+
),
89+
'additionalProperties' => false,
90+
),
91+
),
92+
)
93+
);
94+
}
95+
96+
/**
97+
* Initializes the setting if it doesn't exist.
98+
*/
99+
private function initialize_settings() {
100+
if ( get_option( self::SETTING_KEY, false ) === false ) {
101+
add_option( self::SETTING_KEY, $this->default_settings );
102+
}
103+
}
104+
105+
/**
106+
* Sanitizes the font optimization settings before saving.
107+
*
108+
* @param array $settings The input settings from the request.
109+
* @return array Sanitized settings array with a timestamp.
110+
*/
111+
public function sanitize_settings( $settings ) {
112+
$existing = get_option( self::SETTING_KEY, array() );
113+
114+
$fonts_value = isset( $settings['cloudflare']['fonts']['value'] )
115+
? ! empty( $settings['cloudflare']['fonts']['value'] )
116+
: ( ! empty( $existing['cloudflare']['fonts']['value'] ) );
117+
$fonts_user_set = isset( $settings['cloudflare']['fonts']['user_set'] )
118+
? ! empty( $settings['cloudflare']['fonts']['user_set'] )
119+
: ( ! empty( $existing['cloudflare']['fonts']['user_set'] ) );
120+
121+
return array(
122+
'cloudflare' => array(
123+
'fonts' => array(
124+
'value' => $fonts_value,
125+
'user_set' => $fonts_user_set,
126+
),
127+
'last_updated' => time(),
128+
),
129+
);
130+
}
131+
132+
/**
133+
* Refreshes legacy font settings based on Cloudflare capabilities.
134+
*
135+
* @param object|null $capabilities Optional capabilities object.
136+
*/
137+
public static function maybe_refresh_with_capabilities( $capabilities ) {
138+
$settings = get_option( self::SETTING_KEY, array() );
139+
140+
if ( ! isset( $settings['cloudflare']['fonts'] ) ) {
141+
$settings['cloudflare']['fonts'] = array(
142+
'value' => false,
143+
'user_set' => false,
144+
);
145+
}
146+
147+
if ( is_array( $capabilities ) ) {
148+
$has_fonts = (bool) $capabilities['hasCloudflareFonts'] ?? false;
149+
150+
if ( $settings['cloudflare']['fonts']['user_set'] ) {
151+
if ( $settings['cloudflare']['fonts']['value'] && ! $has_fonts ) {
152+
$settings['cloudflare']['fonts']['value'] = false;
153+
}
154+
} elseif ( ! $settings['cloudflare']['fonts']['value'] && $has_fonts ) {
155+
$settings['cloudflare']['fonts']['value'] = true;
156+
}
157+
}
158+
159+
$settings['cloudflare']['last_updated'] = time();
160+
update_option( self::SETTING_KEY, $settings );
161+
}
162+
163+
/**
164+
* Checks if Cloudflare font optimization is enabled.
165+
*
166+
* @return bool
167+
*/
168+
public static function is_cloudflare_fonts_enabled() {
169+
$settings = get_option( self::SETTING_KEY, array( 'cloudflare' => array( 'fonts' => array( 'value' => false ) ) ) );
170+
return ! empty( $settings['cloudflare']['fonts']['value'] );
171+
}
172+
}

includes/Images/ImageManager.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,17 @@ class ImageManager {
2020
* @param Container $container Dependency injection container.
2121
*/
2222
public function __construct( Container $container ) {
23-
$this->initialize_settings();
23+
$this->initialize_settings( $container );
2424
$this->initialize_services( $container );
2525
}
2626

2727
/**
2828
* Initializes the ImageSettings class to register settings.
29+
*
30+
* @param Container $container Dependency injection container.
2931
*/
30-
private function initialize_settings() {
31-
new ImageSettings();
32+
private function initialize_settings( Container $container ) {
33+
new ImageSettings( $container );
3234
}
3335

3436
/**

0 commit comments

Comments
 (0)