SG Optimizer - Version 5.7.13

Version Description

Download this release

Release Info

Developer sstoqnov
Plugin Icon 128x128 SG Optimizer
Version 5.7.13
Comparing to
See all releases

Code changes from version 5.7.12 to 5.7.13

core/Analysis/Analysis.php CHANGED
@@ -76,9 +76,56 @@ class Analysis {
76
 
77
  $items['score'] = $result['lighthouseResult']['categories']['performance']['score'];
78
 
 
 
 
 
 
79
  // Return the response.
80
  return $items;
81
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
 
83
  /**
84
  * Get optimization messages.
@@ -178,10 +225,11 @@ class Analysis {
178
  'uses-rel-preload' => array(
179
  'enabled' => array(
180
  'siteground_optimizer_optimize_javascript_async',
 
181
  ),
182
  'messages' => array(
183
  'enabled' => __( 'Not all resources can be deferred, so you may continue to get this message, even after your site is well optimized.', 'sg-cachepress' ),
184
- 'default' => __( 'Enable the <strong>Defer Render-blocking JS</strong> option in the <a class="sg-link sg-with-color sg-typography sg-typography--break-all" href="#frontend">Frontend Optimization tab</a> and exclude critical scripts from it to pass this audit.', 'sg-cachepress' ),
185
  ),
186
  ),
187
  'efficient-animated-content' => array(
@@ -316,6 +364,16 @@ class Analysis {
316
  return $response_messages;
317
  }
318
 
 
 
 
 
 
 
 
 
 
 
319
  public function run_analysis_rest( $url, $device = 'desktop' ) {
320
  $analysis = $this->run_analysis( $url, $device );
321
 
@@ -429,4 +487,21 @@ class Analysis {
429
  // Return the analysis.
430
  return $this->process_analysis( $response );
431
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
432
  }
76
 
77
  $items['score'] = $result['lighthouseResult']['categories']['performance']['score'];
78
 
79
+ // Check if we need to add additional information to the results table.
80
+ if ( array_key_exists( 'data', $items['load-opportunities'] ) ) {
81
+ $items = $this->add_additional_info( $items );
82
+ }
83
+
84
  // Return the response.
85
  return $items;
86
  }
87
+ /**
88
+ * Add additional information to optimization opportunities
89
+ *
90
+ * @since 5.7.13
91
+ *
92
+ * @param array $items The array containing the results from the optimization test.
93
+ *
94
+ * @return array $items The items and added additional informative messages.
95
+ */
96
+ public function add_additional_info( $items ) {
97
+ // Array containing the audits to which we want to add additional information.
98
+ $additional_optimization_info = array(
99
+ 'uses-rel-preload',
100
+ );
101
+
102
+ // Array containing the messages, asset type and savigns we can achieve.
103
+ $additional_info_keys = array(
104
+ 'uses-rel-preload' => array(
105
+ 'asset' => 'url',
106
+ 'savings' => 'overallSavingsMs',
107
+ 'additional_message' => __( 'Use <strong>Fonts Preloading</strong> and add the following fonts to the preload list:', 'sg-cachepress' ),
108
+ ),
109
+ );
110
+
111
+ // Loop trough the opportunities and check if we have a match with the additional info list.
112
+ foreach ( $items['load-opportunities']['data'] as $item => $prop ) {
113
+
114
+ if ( in_array( $prop['id'], $additional_optimization_info ) && ! empty( $prop['details']['items'] ) ) {
115
+
116
+ // Add the additional text message to the existing message notification.
117
+ $items['load-opportunities']['data'][ $item ]['action'] .= $additional_info_keys[ $prop['id'] ]['additional_message'];
118
+
119
+ // Loop trough all items and add them based on the additional info keys.
120
+ foreach ( $prop['details']['items'] as $asset ) {
121
+
122
+ $items['load-opportunities']['data'][ $item ]['action'] .= '<br/>' . $asset[ $additional_info_keys[ $prop['id'] ]['asset'] ];
123
+ }
124
+ }
125
+ }
126
+
127
+ return $items;
128
+ }
129
 
130
  /**
131
  * Get optimization messages.
225
  'uses-rel-preload' => array(
226
  'enabled' => array(
227
  'siteground_optimizer_optimize_javascript_async',
228
+ 'siteground_optimizer_optimize_web_fonts',
229
  ),
230
  'messages' => array(
231
  'enabled' => __( 'Not all resources can be deferred, so you may continue to get this message, even after your site is well optimized.', 'sg-cachepress' ),
232
+ 'default' => __( 'Enable the <strong>Defer Render-blocking JS</strong> option in the <a class="sg-link sg-with-color sg-typography sg-typography--break-all" href="#frontend">Frontend Optimization tab</a> and exclude critical scripts from it to pass this audit. You could also enable the <strong>Web Fonts Optimization</strong> and preload the necessary fonts by adding them in the preload list.', 'sg-cachepress' ),
233
  ),
234
  ),
235
  'efficient-animated-content' => array(
364
  return $response_messages;
365
  }
366
 
367
+ /**
368
+ * Run the speed test.
369
+ *
370
+ * @since 5.4.0
371
+ *
372
+ * @param string $url The url to test.
373
+ * @param string $device The device type(mobile/desktop).
374
+ *
375
+ * @return array The speed test result.
376
+ */
377
  public function run_analysis_rest( $url, $device = 'desktop' ) {
378
  $analysis = $this->run_analysis( $url, $device );
379
 
487
  // Return the analysis.
488
  return $this->process_analysis( $response );
489
  }
490
+
491
+ /**
492
+ * Get the pre-migration speed results.
493
+ *
494
+ * @since 5.7.13
495
+ *
496
+ * @return bool/string False if the file is non existing/The array containing the speed-test results.
497
+ */
498
+ public function get_pre_migration_test() {
499
+ // Bail if the file does not exist.
500
+ if ( ! file_exists( Helper::get_uploads_dir() . '/pagespeed_results.json' ) ) {
501
+ return false;
502
+ }
503
+ // Return the string containing the pre-migration speed test.
504
+ return json_decode( file_get_contents( Helper::get_uploads_dir() . '/pagespeed_results.json' ), true );
505
+ }
506
+
507
  }
core/Cli/Cli.php CHANGED
@@ -44,8 +44,11 @@ class Cli {
44
  // Heartbeat.
45
  \WP_CLI::add_command( 'sg heartbeat', 'SiteGround_Optimizer\Cli\Cli_Heartbeat' );
46
 
47
- // DNS Prefetch
48
  \WP_CLI::add_command( 'sg dns-prefetch', 'SiteGround_Optimizer\Cli\Cli_DNS_Prefetch' );
 
 
 
49
  }
50
 
51
  }
44
  // Heartbeat.
45
  \WP_CLI::add_command( 'sg heartbeat', 'SiteGround_Optimizer\Cli\Cli_Heartbeat' );
46
 
47
+ // DNS Prefetch.
48
  \WP_CLI::add_command( 'sg dns-prefetch', 'SiteGround_Optimizer\Cli\Cli_DNS_Prefetch' );
49
+
50
+ // Import/Export settings.
51
+ \WP_CLI::add_command( 'sg settings', 'SiteGround_Optimizer\Cli\Cli_Settings' );
52
  }
53
 
54
  }
core/Cli/Cli_Heartbeat.php CHANGED
@@ -24,7 +24,7 @@ class Cli_Heartbeat {
24
  * ## OPTIONS
25
  *
26
  * <location>
27
- * : Settging name.
28
  * ---
29
  * options:
30
  * - frontend
24
  * ## OPTIONS
25
  *
26
  * <location>
27
+ * : Setting name.
28
  * ---
29
  * options:
30
  * - frontend
core/Cli/Cli_Settings.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace SiteGround_Optimizer\Cli;
3
+
4
+ use SiteGround_Optimizer\Settings\Settings;
5
+ use SiteGround_Optimizer\Options\Options;
6
+
7
+ /**
8
+ * WP-CLI: wp sg settings {setting} value.
9
+ *
10
+ * Run the `wp sg settings {type}` command to change the settgins of specific plugin functionality.
11
+ *
12
+ * @since 5.7.13
13
+ * @package Cli
14
+ * @subpackage Cli/Settings
15
+ */
16
+
17
+ /**
18
+ * Define the {@link Cli_Settings} class.
19
+ *
20
+ * @since 5.7.13
21
+ */
22
+ class Cli_Settings {
23
+ /**
24
+ * Enable specific setting for SG Optimizer plugin.
25
+ *
26
+ * ## OPTIONS
27
+ *
28
+ * options:
29
+ * - import
30
+ * - export
31
+ * ---
32
+ * <action>
33
+ * : Setting name.
34
+ * [--hash=<string>]
35
+ * : Settings hash.
36
+ */
37
+ public function __invoke( $args, $assoc_args ) {
38
+ // Call the Settings class.
39
+ $this->settings_service = new Settings();
40
+ $this->options_service = new Options();
41
+
42
+ // Check the type of operation.
43
+ if ( 'export' === $args[0] ) {
44
+ // Start the export.
45
+ return $this->export();
46
+ }
47
+
48
+ // Check if we have the import string.
49
+ if ( empty( $assoc_args['hash'] ) ) {
50
+ \WP_CLI::error( 'Please, use the import command with a `hash` parameter - wp sg settings ' . $args[0] . ' --hash=<The hash string.>' );
51
+ }
52
+
53
+ // Start the import.
54
+ return $this->import( $assoc_args['hash'] );
55
+ }
56
+
57
+ /**
58
+ * Call the Import Settings method
59
+ *
60
+ * @since 5.7.13
61
+ *
62
+ * @param string $hash_import The hash string.
63
+ *
64
+ * @return array Options that were updated.
65
+ */
66
+ public function import( $hash_import ) {
67
+ // Start the import.
68
+ $result = $this->settings_service->import( $hash_import );
69
+
70
+ // Check if we have a valid response from the import.
71
+ if ( false === $result ) {
72
+ return \WP_CLI::error( 'The import was unsuccessful, please make sure your hash is correct.' );
73
+ }
74
+
75
+
76
+ if ( ! empty( $result ) ) {
77
+ \WP_CLI::error( 'We\'ve imported everything except the following settings: ' . implode( $result, ', ' ) );
78
+ }
79
+
80
+ return \WP_CLI::success( 'Import Completed.' );
81
+ }
82
+
83
+ /**
84
+ * Call the Export Settings method.
85
+ *
86
+ * @since 5.7.13
87
+ *
88
+ * @return string The filepath or the export string.
89
+ */
90
+ public function export() {
91
+ // Start the export.
92
+ $result = $this->settings_service->export();
93
+
94
+ // Check if we have a valid response from the export.
95
+ if ( ! $result ) {
96
+ return \WP_CLI::error( 'The export was unsuccessful, please try again.' );
97
+ }
98
+
99
+ // Return the export string.
100
+ return \WP_CLI::success( "The export is completed, please use the following string:\n" . $result );
101
+ }
102
+ }
core/Combinator/Js_Combinator.php CHANGED
@@ -411,6 +411,7 @@ class Js_Combinator extends Abstract_Combinator {
411
  'wpp_params',
412
  '_taboola',
413
  '.ratingbox',
 
414
  );
415
 
416
  /**
411
  'wpp_params',
412
  '_taboola',
413
  '.ratingbox',
414
+ 'wp.apiFetch.nonceMiddleware',
415
  );
416
 
417
  /**
core/DNS/Cloudflare.php CHANGED
@@ -144,7 +144,7 @@ class Cloudflare {
144
 
145
  // Prepare path and data.
146
  $data = array(
147
- 'name' => preg_replace( '(^https?://(www\.)?)', '', untrailingslashit( Helper::get_site_url() ) ),
148
  'match' => 'all',
149
  );
150
 
@@ -252,7 +252,7 @@ class Cloudflare {
252
  $this->get_path( 'worker' ),
253
  'POST',
254
  array(
255
- 'pattern' => untrailingslashit( Helper::get_site_url() ) . '/*',
256
  'script' => $this->worker,
257
  'request_limit_fail_open' => true,
258
  )
144
 
145
  // Prepare path and data.
146
  $data = array(
147
+ 'name' => preg_replace( '(^https?://(www\.)?)', '', untrailingslashit( Helper::get_home_url() ) ),
148
  'match' => 'all',
149
  );
150
 
252
  $this->get_path( 'worker' ),
253
  'POST',
254
  array(
255
+ 'pattern' => untrailingslashit( Helper::get_home_url() ) . '/*',
256
  'script' => $this->worker,
257
  'request_limit_fail_open' => true,
258
  )
core/Helper/Helper.php CHANGED
@@ -15,6 +15,7 @@ use SiteGround_Optimizer\I18n\I18n;
15
  use SiteGround_Optimizer\Heartbeat_Control\Heartbeat_Control;
16
  use SiteGround_Optimizer\Database_Optimizer\Database_Optimizer;
17
  use SiteGround_Optimizer\DNS\Cloudflare;
 
18
 
19
  /**
20
  * Helper functions and main initialization class.
@@ -78,6 +79,8 @@ class Helper {
78
 
79
  // Init Cloudflare API.
80
  new Cloudflare();
 
 
81
  }
82
 
83
  /**
15
  use SiteGround_Optimizer\Heartbeat_Control\Heartbeat_Control;
16
  use SiteGround_Optimizer\Database_Optimizer\Database_Optimizer;
17
  use SiteGround_Optimizer\DNS\Cloudflare;
18
+ use SiteGround_Optimizer\Settings\Settings;
19
 
20
  /**
21
  * Helper functions and main initialization class.
79
 
80
  // Init Cloudflare API.
81
  new Cloudflare();
82
+
83
+ new Settings();
84
  }
85
 
86
  /**
core/Modules/Modules.php CHANGED
@@ -893,8 +893,13 @@ class Modules {
893
  // Parse the url.
894
  $url_parts = parse_url( site_url( '/' ) );
895
 
896
- // Get the A record info.
897
- $dns_resolver_response = $resolver->query( $url_parts['host'], 'A' );
 
 
 
 
 
898
 
899
  if ( empty( $dns_resolver_response->answer ) ) {
900
  return;
893
  // Parse the url.
894
  $url_parts = parse_url( site_url( '/' ) );
895
 
896
+ try {
897
+ // Get the A record info.
898
+ $dns_resolver_response = $resolver->query( $url_parts['host'], 'A' );
899
+ } catch ( \Exception $e ) {
900
+ return;
901
+ }
902
+
903
 
904
  if ( empty( $dns_resolver_response->answer ) ) {
905
  return;
core/Parser/Parser.php CHANGED
@@ -39,6 +39,7 @@ class Parser {
39
  // Do not run optimizations for amp.
40
  if (
41
  $this->is_amp_enabled( $html ) ||
 
42
  is_feed()
43
  ) {
44
  return $html;
@@ -85,6 +86,24 @@ class Parser {
85
  return $run_amp_check;
86
  }
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  /**
89
  * Start buffer.
90
  *
39
  // Do not run optimizations for amp.
40
  if (
41
  $this->is_amp_enabled( $html ) ||
42
+ $this->is_xml( $html ) ||
43
  is_feed()
44
  ) {
45
  return $html;
86
  return $run_amp_check;
87
  }
88
 
89
+ /**
90
+ * Check if the provided html is a xml.
91
+ *
92
+ * @since 5.7.13
93
+ *
94
+ * @param string $html The page html.
95
+ *
96
+ * @return bool $run_xml_check Wheter the page xml sitemap.
97
+ */
98
+ public function is_xml( $html ) {
99
+ // Get the first 200 chars of the file to make the preg_match check faster.
100
+ $is_xml = substr( $html, 0, 200 );
101
+
102
+ $run_xml_check = preg_match( '/<\?xml version="/', $is_xml );
103
+
104
+ return $run_xml_check;
105
+ }
106
+
107
  /**
108
  * Start buffer.
109
  *
core/Settings/Settings.php ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace SiteGround_Optimizer\Settings;
3
+
4
+ use SiteGround_Optimizer\Options\Options;
5
+ use SiteGround_Optimizer\Helper\Helper;
6
+ use SiteGround_Optimizer\Supercacher\Supercacher;
7
+ use SiteGround_Optimizer\Ssl\Ssl;
8
+ use SiteGround_Optimizer\Memcache\Memcache;
9
+ use SiteGround_Optimizer\Htaccess\Htaccess;
10
+
11
+ class Settings {
12
+ /**
13
+ * The array containing all settings that can be imported/exported.
14
+ *
15
+ * @var array
16
+ */
17
+ public $options = array(
18
+ 'enable_cache',
19
+ 'autoflush_cache',
20
+ 'user_agent_header',
21
+ 'enable_memcached',
22
+ 'ssl_enabled',
23
+ 'fix_insecure_content',
24
+ 'enable_gzip_compression',
25
+ 'enable_browser_caching',
26
+ 'optimize_html',
27
+ 'optimize_javascript',
28
+ 'optimize_javascript_async',
29
+ 'optimize_css',
30
+ 'combine_css',
31
+ 'combine_javascript',
32
+ 'optimize_web_fonts',
33
+ 'remove_query_strings',
34
+ 'disable_emojis',
35
+ 'optimize_images',
36
+ 'lazyload_images',
37
+ 'webp_support',
38
+ 'lazyload_gravatars',
39
+ 'lazyload_thumbnails',
40
+ 'lazyload_responsive',
41
+ 'lazyload_textwidgets',
42
+ 'lazyload_mobile',
43
+ 'lazyload_woocommerce',
44
+ 'lazyload_shortcodes',
45
+ 'lazyload_videos',
46
+ 'lazyload_iframes',
47
+ 'supercacher_permissions',
48
+ 'frontend_permissions',
49
+ 'images_permissions',
50
+ 'environment_permissions',
51
+ 'heartbeat_control',
52
+ 'database_optimization',
53
+ 'dns_prefetch',
54
+ 'cloudflare_optimization',
55
+ 'async_javascript_exclude',
56
+ 'excluded_lazy_load_classes',
57
+ 'post_types_exclude',
58
+ 'dns_prefetch_urls',
59
+ 'combine_javascript_exclude',
60
+ 'minify_javascript_exclude',
61
+ );
62
+
63
+ /**
64
+ * Additional actions that need to be done when importing a file or hash code.
65
+ *
66
+ * @var array
67
+ */
68
+ public $complex_options = array(
69
+ 'ssl_enabled' => array(
70
+ 'class' => 'SiteGround_Optimizer\Ssl\Ssl',
71
+ 'enable' => 'enable',
72
+ 'disable' => 'disable',
73
+ ),
74
+ 'enable_memcached' => array(
75
+ 'class' => 'SiteGround_Optimizer\Memcache\Memcache',
76
+ 'enable' => 'create_memcached_dropin',
77
+ 'disable' => 'remove_memcached_dropin',
78
+ ),
79
+ 'enable_gzip_compression' => array(
80
+ 'class' => 'SiteGround_Optimizer\Htaccess\Htaccess',
81
+ 'enable' => 'enable',
82
+ 'disable' => 'disable',
83
+ 'disabled_on_avalon' => 1,
84
+ 'argument' => 'gzip',
85
+ ),
86
+ 'enable_browser_caching' => array(
87
+ 'class' => 'SiteGround_Optimizer\Htaccess\Htaccess',
88
+ 'enable' => 'enable',
89
+ 'disable' => 'disable',
90
+ 'disabled_on_avalon' => 1,
91
+ 'argument' => 'browser-caching',
92
+ ),
93
+ );
94
+
95
+ /**
96
+ * The constructor.
97
+ *
98
+ * @since 5.7.13
99
+ */
100
+ public function __construct() {
101
+ $this->options_service = new Options();
102
+ }
103
+
104
+ /**
105
+ * Create export file or hash.
106
+ *
107
+ * @since 5.7.13
108
+ *
109
+ * @return string/filesource String containing the hashed json or a json file.
110
+ */
111
+ public function export() {
112
+ // Prepare the settings array.
113
+ $settings = array();
114
+ // Get the options from database.
115
+ $options = $this->options_service->fetch_options();
116
+
117
+ foreach ( $options as $option => $value ) {
118
+ if ( ! in_array( $option, $this->options ) ) {
119
+ continue;
120
+ }
121
+
122
+ $settings[ $option ] = $value;
123
+ }
124
+
125
+ // Return the string if we are not writing it to a file.
126
+ return base64_encode( json_encode( $settings ) );
127
+ }
128
+
129
+ /**
130
+ * Prepare the json for import.
131
+ *
132
+ * @since 5.7.13
133
+ *
134
+ * @param string $source The hashed export.
135
+ *
136
+ * @return array The options updated, their values and errors
137
+ */
138
+ public function import( $source ) {
139
+ // Get the content of the hash, decode the import and map the available options for import.
140
+ $options = json_decode( base64_decode( $source ), true );
141
+
142
+ // Check if the provided settings are ok.
143
+ if ( json_last_error() !== JSON_ERROR_NONE ) {
144
+ return false;
145
+ }
146
+
147
+ $failed_options = array();
148
+
149
+ foreach ( $options as $option => $value ) {
150
+ // Bail if the option doesn't exists in the predefined options.
151
+ if ( ! in_array( $option, $this->options ) ) {
152
+ continue;
153
+ }
154
+
155
+ // Update the option if we don't need to do additional optimizations.
156
+ if ( ! array_key_exists( $option, $this->complex_options ) ) {
157
+ // Update the option.
158
+ update_option( 'siteground_optimizer_' . $option, $value );
159
+ continue;
160
+ }
161
+
162
+ // Bail if the option is active by default on avalon server.
163
+ if (
164
+ ! empty( $this->complex_options[ $option ]['disabled_on_avalon'] ) &&
165
+ Helper::is_avalon()
166
+ ) {
167
+ continue;
168
+ }
169
+
170
+ // Impor the complex option.
171
+ $result = $this->import_complex_option( $option, $value );
172
+
173
+ // Add the option to the failed option if the optimization is not successful.
174
+ if ( false === $result ) {
175
+ $failed_options[] = $option;
176
+ }
177
+ }
178
+
179
+ // Flush the cache after the import.
180
+ Supercacher::purge_cache();
181
+
182
+ // Return the failed options.
183
+ return $failed_options;
184
+ }
185
+
186
+ /**
187
+ * Import complex option
188
+ *
189
+ * @since 5.7.13
190
+ *
191
+ * @param string $option The option name.
192
+ * @param mixed $value The option value.
193
+ *
194
+ * @return boolean True on success, false otherwise.
195
+ */
196
+ public function import_complex_option( $option, $value ) {
197
+ // Get the complex option details.
198
+ $optimization_details = $this->complex_options[ $option ];
199
+ // Init the class responsble for option functionality.
200
+ $class = new $optimization_details['class']();
201
+
202
+ // Get the method to call.
203
+ $method = ( 0 === $value ) ? $optimization_details['disable'] : $optimization_details['enable'];
204
+
205
+ // Do the optimization and the the result.
206
+ $result = ! empty( $optimization_details['argument'] ) ? $class->$method( $optimization_details['argument'] ) : $class->$method();
207
+
208
+ // Update the option if the optimization is successful.
209
+ if ( true === $result ) {
210
+ update_option( 'siteground_optimizer_' . $option, $value );
211
+ }
212
+
213
+ // return the result.
214
+ return $result;
215
+ }
216
+ }
readme.txt CHANGED
@@ -179,7 +179,12 @@ In version 5.0 we've added full WP-CLI support for all plugin options and functi
179
  * wp sg optimize dynamic-cache enable|disable - enables or disables Dynamic caching rules
180
  * wp sg optimize web-fonts enable|disable - enables or disables Web Fonts Optimization
181
  * wp sg optimize fix_insecure_content enable|disable - enables or disables Insecure Content Fix
 
 
 
182
  * wp sg status dynamic-cache|autoflush-cache|mobile-cache|html|js|js-async|css|combine-css|querystring|emojis|images|lazyload_images|lazyload_gravatars|lazyload_thumbnails|lazyload_responsive|lazyload_textwidgets|gzip|browser-caching|memcache|ssl|ssl-fix|web-fonts|combine-js|webp - returns optimization current status (enabled|disabled)
 
 
183
 
184
  = Requirements =
185
 
@@ -212,6 +217,13 @@ Our plugin uses a cookie in order to function properly. It does not store person
212
 
213
  == Changelog ==
214
 
 
 
 
 
 
 
 
215
  = Version 5.7.12 =
216
  * Improved Feed Cache Flush
217
 
179
  * wp sg optimize dynamic-cache enable|disable - enables or disables Dynamic caching rules
180
  * wp sg optimize web-fonts enable|disable - enables or disables Web Fonts Optimization
181
  * wp sg optimize fix_insecure_content enable|disable - enables or disables Insecure Content Fix
182
+ * wp sg optimize database-optimization enable|disable - enables or disables the DB Optimization
183
+ * wp sg optimize dns-prefetch enable|disable add|remove|urls <value> - enables or disables the DNS Prefetching, add, remove or list urls.
184
+ * wp sg optimize heartbeat-control enable|disable frontend|dashboard|post --frequency=<frequency> enables or disables the Heartbeat control for a specific location and sets the frequency
185
  * wp sg status dynamic-cache|autoflush-cache|mobile-cache|html|js|js-async|css|combine-css|querystring|emojis|images|lazyload_images|lazyload_gravatars|lazyload_thumbnails|lazyload_responsive|lazyload_textwidgets|gzip|browser-caching|memcache|ssl|ssl-fix|web-fonts|combine-js|webp - returns optimization current status (enabled|disabled)
186
+ * wp sg settings export - exports the current plugin settings
187
+ * wp sg settings import --hash=<string> - imports plugin settings and applies them
188
 
189
  = Requirements =
190
 
217
 
218
  == Changelog ==
219
 
220
+ = Version 5.7.13 =
221
+ * Add settings import/export cli command
222
+ * Do not optimize xml sitemaps
223
+ * Fix DNS Resolver fatal error for non existing hosts
224
+ * Fix Cloudflare optimization for sites with custom wp-content dir
225
+ * Improved Speed Test description for Webfonts optimization
226
+
227
  = Version 5.7.12 =
228
  * Improved Feed Cache Flush
229
 
sg-cachepress.php CHANGED
@@ -10,7 +10,7 @@
10
  * Plugin Name: SG Optimizer
11
  * Plugin URI: https://siteground.com
12
  * Description: This plugin will link your WordPress application with all the performance optimizations provided by SiteGround
13
- * Version: 5.7.12
14
  * Author: SiteGround
15
  * Author URI: https://www.siteground.com
16
  * Text Domain: sg-cachepress
@@ -31,7 +31,7 @@ if ( ! defined( 'WPINC' ) ) {
31
 
32
  // Define version constant.
33
  if ( ! defined( __NAMESPACE__ . '\VERSION' ) ) {
34
- define( __NAMESPACE__ . '\VERSION', '5.7.12' );
35
  }
36
 
37
  // Define slug constant.
10
  * Plugin Name: SG Optimizer
11
  * Plugin URI: https://siteground.com
12
  * Description: This plugin will link your WordPress application with all the performance optimizations provided by SiteGround
13
+ * Version: 5.7.13
14
  * Author: SiteGround
15
  * Author URI: https://www.siteground.com
16
  * Text Domain: sg-cachepress
31
 
32
  // Define version constant.
33
  if ( ! defined( __NAMESPACE__ . '\VERSION' ) ) {
34
+ define( __NAMESPACE__ . '\VERSION', '5.7.13' );
35
  }
36
 
37
  // Define slug constant.
templates/cloudflare-worker.tpl CHANGED
@@ -1,129 +1,219 @@
1
  // Default cookie prefixes for cache bypassing
2
  const DEFAULT_BYPASS_COOKIES = [
3
- "wp-",
4
- "wordpress_logged_in_",
5
- "comment_",
6
- "woocommerce_",
7
- "wordpress_sec_",
8
- "yith_wcwl_products",
9
- "edd_items_in_cart",
10
- "it_exchange_session_",
11
- "wordpresspass_",
12
- "comment_author",
13
- "dshack_level",
14
- "wordpressuser_",
15
- "auth",
16
- "noaffiliate_",
17
- "mp_session",
18
- "mp_globalcart_",
19
  ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  async function handleRequest(event) {
22
 
23
- let request = event.request;
24
- let bypassCache = false;
25
- let response = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- // Bypass cache if are present specific cookies
28
- const cookieHeader = request.headers.get('cookie');
29
- let bypassCookies = DEFAULT_BYPASS_COOKIES;
30
 
31
- if( cookieHeader && cookieHeader.length && bypassCookies.length ) {
32
 
33
- const cookies = cookieHeader.split(';');
 
 
 
34
 
35
- for( let cookie of cookies ) {
 
 
36
 
37
- for( let prefix of bypassCookies ) {
 
 
38
 
39
- if( cookie.trim().startsWith(prefix) ) {
40
- bypassCache = true;
41
- break;
42
- }
43
 
44
- }
 
 
45
 
46
- if (bypassCache) {
47
- break;
48
- }
49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  }
51
 
52
- }
53
 
 
 
 
54
 
55
- // Bypass cache for non-html requests
56
- let accept = request.headers.get('Accept');
57
- // Lets handle the request URL nicely
58
- const requestURL = new URL(request.url)
59
-
60
- // Check if accept has value and the type is not text/html || */* || the user is accessing WP-ADMIN
61
- // Then only we set bypassCache as true else we will bypass that request and won't modify anything
62
- if(
63
- (
64
- !accept &&
65
- !( accept.includes('text/html') || accept.includes('*/*') )
66
- ) ||
67
- requestURL.pathname.match(/^(?:\/wp-admin\/)/g)
68
- ) {
69
- bypassCache = true;
70
  }
71
 
72
- // When bypassCache is false that means we should intercept that request and add our headers
73
- if( !bypassCache ) {
74
 
75
- // Get the cacheKey from by passing our requestURL & request
76
- let cacheKey = new Request(requestURL, request);
77
- let cache = caches.default;
78
 
79
- // Get this request from this zone's cache
80
- response = await cache.match(cacheKey);
 
81
 
82
- if (response) {
 
83
 
84
- response = new Response(response.body, response);
85
- response.headers.set('SG-Optimizer-Worker-Status', 'hit');
86
 
87
- } else {
 
 
88
 
89
- response = await fetch(request);
90
- response = new Response(response.body, response);
91
 
92
- if (response.headers.has('X-Cache-Enabled')) {
 
 
93
 
94
- response.headers.set('Cache-Control', response.headers.get('SG-Optimizer-Cache-Control'));
95
- response.headers.set('SG-Optimizer-Worker-Status', 'miss');
 
 
 
 
96
 
97
- // add page in cache
98
- event.waitUntil(cache.put(cacheKey, response.clone()));
 
 
99
 
100
- } else {
101
- response.headers.set('SG-Optimizer-Worker-Status', 'bypass');
 
 
102
  }
 
 
103
 
104
- }
 
105
 
106
- }
107
- // Get response from the origin
108
- else {
109
 
110
- response = await fetch(request);
111
- response = new Response(response.body, response);
112
  response.headers.set('SG-Optimizer-Worker-Status', 'bypass');
113
  response.headers.set('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
114
 
 
 
115
  }
116
 
117
- return response;
118
 
 
 
 
 
 
 
 
 
 
 
 
119
  }
120
 
121
  addEventListener('fetch', event => {
122
 
123
- try {
124
- return event.respondWith(handleRequest(event));
125
- } catch (e) {
126
- return event.respondWith(new Response('Error thrown ' + e.message));
127
- }
128
 
129
- })
1
  // Default cookie prefixes for cache bypassing
2
  const DEFAULT_BYPASS_COOKIES = [
3
+ "wp-",
4
+ "wordpress_logged_in_",
5
+ "comment_",
6
+ "woocommerce_",
7
+ "wordpress_sec_",
8
+ "yith_wcwl_products",
9
+ "edd_items_in_cart",
10
+ "it_exchange_session_",
11
+ "wordpresspass_",
12
+ "comment_author",
13
+ "dshack_level",
14
+ "wordpressuser_",
15
+ "auth",
16
+ "noaffiliate_",
17
+ "mp_session",
18
+ "mp_globalcart_",
19
  ];
20
+ const THIRDPARTY_PARAMETERS = [
21
+ 'fbclid',
22
+ 'fb_action_ids',
23
+ 'fb_action_types',
24
+ 'fb_source',
25
+ '_ga',
26
+ 'age-verified',
27
+ 'ao_noptimize',
28
+ 'usqp',
29
+ 'cn-reloaded',
30
+ 'klaviyo',
31
+ 'amp',
32
+ 'gclid',
33
+ 'utm_source',
34
+ 'utm_medium',
35
+ 'utm_campaign',
36
+ 'utm_content',
37
+ 'utm_term',
38
+ ];
39
+
40
+ function remove_third_party_parameters(event) {
41
+
42
+ // Fetch the Request URL from the event and parse it for better handling
43
+ const requestedURL = new URL(event.request.url);
44
+
45
+ // List of query parameters we need to remove
46
+ const third_party_parameters = THIRDPARTY_PARAMETERS;
47
+
48
+ // Loop through the queries and check if we have any present in the URL, and remove them
49
+ third_party_parameters.forEach((queryParam) => {
50
+ // Create regex to test the url for the parameters we are looking
51
+ const check_parameters = new RegExp('(&?)(' + queryParam + '=\\w+)', 'g');
52
+
53
+ // Check if the url has these parameters
54
+ if (check_parameters.test(requestedURL.search)) {
55
+ // If the url has any remove them
56
+ const urlSearchParams = requestedURL.searchParams;
57
+ urlSearchParams.delete(queryParam);
58
+ }
59
+
60
+ });
61
+
62
+ return requestedURL;
63
+ }
64
 
65
  async function handleRequest(event) {
66
 
67
+ const request = event.request;
68
+ const requestURL = remove_third_party_parameters(event);
69
+ const cookieHeader = request.headers.get('cookie');
70
+ const reqDetails = {
71
+ 'contentTypeHTML': false
72
+ };
73
+ //Bypass cache based on request method different from GET & HEAD
74
+ const allowedRequestMethods = ['GET', 'HEAD'];
75
+ let response = false;
76
+ let bypassCache = false;
77
+ let bypassCookies = DEFAULT_BYPASS_COOKIES;
78
+
79
+
80
+ if (!bypassCache && request) {
81
+ if (!allowedRequestMethods.includes(request.method)) {
82
+ bypassCache = true;
83
+ }
84
+ }
85
 
86
+ // BYPASS the cache for WP Admin HTML Requests & files from that directoryt & API endpoints.
87
+ const accept = request.headers.get('Accept');
 
88
 
89
+ if (!bypassCache && accept) {
90
 
91
+ // Paths that should be bypassed.
92
+ const bypass_cache_paths = new RegExp(/(\/((wp-admin)|(wp-json)|(wc-api)|(edd-api))\/)/g);
93
+ // Files that should be bypassed.
94
+ const bypass_file_ext = new RegExp(/\.(xsl|xml)$/);
95
 
96
+ // Make sure that the request has text/html.
97
+ if (accept.includes('text/html')) {
98
+ reqDetails.contentTypeHTML = true;
99
 
100
+ // Check if the request URL is an admin URL
101
+ if (bypass_cache_paths.test(requestURL.pathname)) {
102
+ bypassCache = true;
103
 
104
+ }
105
+ }
 
 
106
 
107
+ if (bypass_file_ext.test(requestURL.pathname)) {
108
+ // Bypass the request since these paths must be accessed from the origin and not cached. We also make sure that we do not cache any sitemaps.
109
+ bypassCache = true;
110
 
111
+ }
112
+ }
 
113
 
114
+ // Check if we have to bypass because of cookies (only for html request)
115
+ if (!bypassCache && reqDetails.contentTypeHTML && cookieHeader && cookieHeader.length > 0 && bypassCookies.length > 0 ) {
116
+
117
+ // Separate the request cookies by semicolon and create an Array
118
+ const cookies = cookieHeader.split(';');
119
+
120
+ // Check if we have a match.
121
+ for( let cookie of cookies ) {
122
+
123
+ for( let prefix of bypassCookies ) {
124
+
125
+ if( cookie.trim().startsWith(prefix) ) {
126
+ bypassCache = true;
127
+ break;
128
  }
129
 
130
+ }
131
 
132
+ if (bypassCache) {
133
+ break;
134
+ }
135
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  }
137
 
138
+ }
 
139
 
140
+ // If the request has not been bypassed check for it in the CF Edge.
141
+ if (!bypassCache) {
 
142
 
143
+ // Check if the Request present in the CF Edge Cache
144
+ const cacheKey = new Request(requestURL, request);
145
+ const cache = caches.default;
146
 
147
+ // Try to fetch the request from the zone's cache
148
+ response = await cache.match(cacheKey);
149
 
150
+ if (response) {
 
151
 
152
+ // If the response exist no actions are needed. Build the response and set the headers.
153
+ response = new Response(response.body, response);
154
+ response.headers.set('SG-Optimizer-Worker-Status', 'hit');
155
 
156
+ } else {
 
157
 
158
+ // If the response is not present at the edge, fetch it from the origin.
159
+ const fetchedResponse = await fetch(request);
160
+ response = new Response(fetchedResponse.body, fetchedResponse);
161
 
162
+ // Check if the response is cached and if needs to be bypassed. If not cached make sure we put it in the cache.
163
+ if (!bypassCache) {
164
+ // If the response is different add it to the cache. More info: https://developers.cloudflare.com/workers/runtime-apis/cache#put
165
+ if (response.status !== 206) {
166
+ // Set the worker status as miss and put the item in CF cache
167
+ response.headers.set('SG-Optimizer-Worker-Status', 'miss');
168
 
169
+ // Add page in cache using cache.put()
170
+ event.waitUntil(cache.put(cacheKey, response.clone()));
171
+
172
+ } else {
173
 
174
+ // If the response is 206 cache it with cacheEverything set to TRUE. Info: https://developers.cloudflare.com/workers/runtime-apis/request#requestinitcfproperties.
175
+ response = await fetch(request, {
176
+ cf: {
177
+ cacheEverything: true
178
  }
179
+ });
180
+ response = new Response(response.body, response);
181
 
182
+ // Set the worker status as miss and put the item in CF cache
183
+ response.headers.set('SG-Optimizer-Worker-Status', 'miss');
184
 
185
+ }
186
+ } else {
 
187
 
188
+ // BYPASS the request and add our custom headers
 
189
  response.headers.set('SG-Optimizer-Worker-Status', 'bypass');
190
  response.headers.set('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
191
 
192
+ }
193
+
194
  }
195
 
196
+ } else {
197
 
198
+ // Make request to the origin.
199
+ const bypassedResponse = await fetch(request);
200
+ response = new Response(bypassedResponse.body, bypassedResponse);
201
+
202
+ // Add the bypass headers.
203
+ response.headers.set('SG-Optimizer-Worker-Status', 'bypass');
204
+ response.headers.set('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
205
+
206
+ }
207
+
208
+ return response;
209
  }
210
 
211
  addEventListener('fetch', event => {
212
 
213
+ try {
214
+ return event.respondWith(handleRequest(event));
215
+ } catch (e) {
216
+ return event.respondWith(new Response('Error thrown ' + e.message));
217
+ }
218
 
219
+ });